@appilots/sdk 0.2.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/LICENSE +21 -0
- package/README.md +38 -0
- package/dist/chunk-GEM6TPAK.mjs +5834 -0
- package/dist/chunk-HDLZGXNF.mjs +2348 -0
- package/dist/chunk-IASJ7YHC.js +5847 -0
- package/dist/chunk-KCDNL4HV.mjs +61 -0
- package/dist/chunk-KGN3DNGX.js +67 -0
- package/dist/chunk-SIBKF7AA.js +2395 -0
- package/dist/hooks/index.d.mts +2 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +43 -0
- package/dist/hooks/index.mjs +2 -0
- package/dist/index-BDb1WVNo.d.mts +1300 -0
- package/dist/index-BDb1WVNo.d.ts +1300 -0
- package/dist/index.d.mts +1156 -0
- package/dist/index.d.ts +1156 -0
- package/dist/index.js +2836 -0
- package/dist/index.mjs +2658 -0
- package/dist/navigation/index.d.mts +43 -0
- package/dist/navigation/index.d.ts +43 -0
- package/dist/navigation/index.js +51 -0
- package/dist/navigation/index.mjs +2 -0
- package/dist/registerScreen-D1oGm28T.d.mts +159 -0
- package/dist/registerScreen-D1oGm28T.d.ts +159 -0
- package/metro.js +158 -0
- package/package.json +90 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2658 @@
|
|
|
1
|
+
export { AppilotsNavigationContainer } from './chunk-KCDNL4HV.mjs';
|
|
2
|
+
import { useAppilotsChat, useAppilotsActions, shouldShowSuggestedPrompts, useSuggestedPrompts } from './chunk-GEM6TPAK.mjs';
|
|
3
|
+
export { captureSnapshot, executeAction, useAppilots, useAppilotsActions, useAppilotsChat, useAppilotsField, useAppilotsNavigation, useAppilotsSlider, useAppilotsTarget, useAppilotsToggle, useSuggestedPrompts } from './chunk-GEM6TPAK.mjs';
|
|
4
|
+
import { useAppilotsContext, componentRegistry } from './chunk-HDLZGXNF.mjs';
|
|
5
|
+
export { AppilotsClient, AppilotsProvider, AppilotsRegistryProvider, SDK_VERSION, _patchJsxRuntimes, clearAppilotsDebugTraces, componentRegistry, createComponentRegistry, createElementRegistry, createListRegistry, elementRegistry, enableAppilotsAutoTracking, getAppilotsDebugTraces, getGlobalConfig, initAppilots, isAutoTrackingEnabled, listRegistry, recordAppilotsDebugTrace, registerScreen, subscribeAppilotsDebugTraces, useAppilotsContext, useResolvedRegistry } from './chunk-HDLZGXNF.mjs';
|
|
6
|
+
import * as React7 from 'react';
|
|
7
|
+
import React7__default, { createContext, useRef, useEffect, useMemo, useContext, useState, useCallback } from 'react';
|
|
8
|
+
import { StyleSheet, Dimensions, Platform, View, Animated, Text, TouchableOpacity, ActivityIndicator, useColorScheme, NativeModules, FlatList, Image, TextInput, Modal, KeyboardAvoidingView, Linking } from 'react-native';
|
|
9
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
10
|
+
import Svg, { Path } from 'react-native-svg';
|
|
11
|
+
|
|
12
|
+
// ../shared/dist/chunk-43YQ52Q7.mjs
|
|
13
|
+
var SENSITIVE_FIELD_NAME_RE = /(\b|_|-)(password|senha|contrase[ñn]a|mot[\s_-]*de[\s_-]*passe|passwd|pwd|secret|token|api[\s_-]*key|access[\s_-]*key|private[\s_-]*key|cvv|cvc|cvn|cvc2|cid|pin(?:[\s_-]*code)?|otp|one[\s_-]*time[\s_-]*code|2fa|mfa|security[\s_-]*code|c[óo]digo[\s_-]*seguran[çc]a|c[óo]digo[\s_-]*verifica[çc][ãa]o|verification[\s_-]*code)(\b|_|-)/i;
|
|
14
|
+
function isSensitiveFieldName(id, label, placeholder, type) {
|
|
15
|
+
if (type === "password") return true;
|
|
16
|
+
for (const candidate of [id, label, placeholder]) {
|
|
17
|
+
if (typeof candidate === "string" && SENSITIVE_FIELD_NAME_RE.test(candidate)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/components/humanizeAction.ts
|
|
25
|
+
function describeAction(action) {
|
|
26
|
+
const state = mapStatus(action.status);
|
|
27
|
+
const label = labelFor(action, state);
|
|
28
|
+
return { label, state };
|
|
29
|
+
}
|
|
30
|
+
function mapStatus(status) {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case "pending":
|
|
33
|
+
return "pending";
|
|
34
|
+
case "executing":
|
|
35
|
+
return "running";
|
|
36
|
+
case "completed":
|
|
37
|
+
return "success";
|
|
38
|
+
case "failed":
|
|
39
|
+
return "failed";
|
|
40
|
+
default:
|
|
41
|
+
return "pending";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function labelFor(action, state) {
|
|
45
|
+
if (state === "failed") {
|
|
46
|
+
return failureLabel(action);
|
|
47
|
+
}
|
|
48
|
+
switch (action.type) {
|
|
49
|
+
case "navigate":
|
|
50
|
+
return navigateLabel(action, state);
|
|
51
|
+
case "form_fill":
|
|
52
|
+
return formFillLabel(action, state);
|
|
53
|
+
case "ui_interaction":
|
|
54
|
+
return uiInteractionLabel(action, state);
|
|
55
|
+
case "scroll_list":
|
|
56
|
+
return scrollListLabel(action, state);
|
|
57
|
+
case "confirm":
|
|
58
|
+
return confirmLabel(state);
|
|
59
|
+
default:
|
|
60
|
+
return genericLabel(action.type, state);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function navigateLabel(action, state) {
|
|
64
|
+
const screen = readString(action.payload, "screenName") || "a outra tela";
|
|
65
|
+
switch (state) {
|
|
66
|
+
case "pending":
|
|
67
|
+
return `Vou navegar para ${screen}`;
|
|
68
|
+
case "running":
|
|
69
|
+
return `Navegando para ${screen}...`;
|
|
70
|
+
case "success":
|
|
71
|
+
return `Naveguei para ${screen}`;
|
|
72
|
+
default:
|
|
73
|
+
return `Navega\xE7\xE3o para ${screen}`;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function formFillLabel(action, state) {
|
|
77
|
+
const fields = readArray(action.payload, "fields");
|
|
78
|
+
const summary = summariseFields(fields);
|
|
79
|
+
switch (state) {
|
|
80
|
+
case "pending":
|
|
81
|
+
return summary ? `Vou preencher ${summary}` : "Vou preencher o formul\xE1rio";
|
|
82
|
+
case "running":
|
|
83
|
+
return summary ? `Preenchendo ${summary}...` : "Preenchendo formul\xE1rio...";
|
|
84
|
+
case "success":
|
|
85
|
+
return summary ? `Preenchi ${summary}` : "Preenchi o formul\xE1rio";
|
|
86
|
+
default:
|
|
87
|
+
return "Preenchimento do formul\xE1rio";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function uiInteractionLabel(action, state) {
|
|
91
|
+
const verbBase = uiVerb(readString(action.payload, "action"));
|
|
92
|
+
const target = humanActionTarget(action.payload);
|
|
93
|
+
const where = target ? ` em ${target}` : "";
|
|
94
|
+
switch (state) {
|
|
95
|
+
case "pending":
|
|
96
|
+
return `Vou ${verbBase.infinitive}${where}`;
|
|
97
|
+
case "running":
|
|
98
|
+
return `${verbBase.gerund}${where}...`;
|
|
99
|
+
case "success":
|
|
100
|
+
return `${verbBase.past}${where}`;
|
|
101
|
+
default:
|
|
102
|
+
return `Intera\xE7\xE3o${where}`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function scrollListLabel(action, state) {
|
|
106
|
+
const payload = action.payload;
|
|
107
|
+
const toIndex = typeof payload?.toIndex === "number" ? payload.toIndex : void 0;
|
|
108
|
+
const direction = payload?.direction === "up" ? "para cima" : "para baixo";
|
|
109
|
+
const target = toIndex !== void 0 ? `at\xE9 o item ${toIndex}` : direction;
|
|
110
|
+
switch (state) {
|
|
111
|
+
case "pending":
|
|
112
|
+
return `Vou rolar a lista ${target}`;
|
|
113
|
+
case "running":
|
|
114
|
+
return `Rolando a lista ${target}...`;
|
|
115
|
+
case "success":
|
|
116
|
+
return `Rolei a lista ${target}`;
|
|
117
|
+
default:
|
|
118
|
+
return "Rolagem da lista";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function confirmLabel(state) {
|
|
122
|
+
switch (state) {
|
|
123
|
+
case "pending":
|
|
124
|
+
case "running":
|
|
125
|
+
return "Aguardando confirma\xE7\xE3o";
|
|
126
|
+
case "success":
|
|
127
|
+
return "Voc\xEA confirmou";
|
|
128
|
+
case "failed":
|
|
129
|
+
return "Confirma\xE7\xE3o cancelada";
|
|
130
|
+
default:
|
|
131
|
+
return "Confirma\xE7\xE3o";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function genericLabel(type, state) {
|
|
135
|
+
switch (state) {
|
|
136
|
+
case "pending":
|
|
137
|
+
return `Vou executar ${type}`;
|
|
138
|
+
case "running":
|
|
139
|
+
return `Executando ${type}...`;
|
|
140
|
+
case "success":
|
|
141
|
+
return `Executei ${type}`;
|
|
142
|
+
default:
|
|
143
|
+
return type;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function failureLabel(action) {
|
|
147
|
+
const cause = humanizeError(action.error);
|
|
148
|
+
const head = failureHead(action);
|
|
149
|
+
return cause ? `${head} (${cause})` : head;
|
|
150
|
+
}
|
|
151
|
+
function failureHead(action) {
|
|
152
|
+
switch (action.type) {
|
|
153
|
+
case "navigate": {
|
|
154
|
+
const screen = readString(action.payload, "screenName") || "a tela";
|
|
155
|
+
return `N\xE3o consegui navegar para ${screen}`;
|
|
156
|
+
}
|
|
157
|
+
case "form_fill":
|
|
158
|
+
return "N\xE3o consegui preencher o formul\xE1rio";
|
|
159
|
+
case "ui_interaction": {
|
|
160
|
+
const verb = uiVerb(readString(action.payload, "action"));
|
|
161
|
+
const target = humanComponentId(
|
|
162
|
+
readString(action.payload, "componentId") || readString(action.payload, "targetId")
|
|
163
|
+
);
|
|
164
|
+
return target ? `N\xE3o consegui ${verb.infinitive} em ${target}` : `N\xE3o consegui ${verb.infinitive}`;
|
|
165
|
+
}
|
|
166
|
+
case "scroll_list":
|
|
167
|
+
return "N\xE3o consegui rolar a lista";
|
|
168
|
+
case "confirm":
|
|
169
|
+
return "Voc\xEA cancelou";
|
|
170
|
+
default:
|
|
171
|
+
return `N\xE3o consegui executar ${action.type}`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
var TECHNICAL_PATTERNS = [
|
|
175
|
+
/\bat\s+\S+\s*\(/,
|
|
176
|
+
// stack trace frame
|
|
177
|
+
/TypeError|ReferenceError|SyntaxError|RangeError/i,
|
|
178
|
+
/HTTP\s*[45]\d{2}/i,
|
|
179
|
+
/^\s*\{[\s\S]*\}\s*$/,
|
|
180
|
+
// JSON dump
|
|
181
|
+
/undefined is not a function/i,
|
|
182
|
+
/Cannot read propert/i
|
|
183
|
+
];
|
|
184
|
+
var FRIENDLY_REWRITES = [
|
|
185
|
+
[/rejected by user/i, "cancelado pelo usu\xE1rio"],
|
|
186
|
+
[/component '([^']+)' not found/i, "componente $1 n\xE3o encontrado"],
|
|
187
|
+
[/field '([^']+)' not found/i, "campo $1 n\xE3o encontrado"],
|
|
188
|
+
[/screen '([^']+)' not registered/i, "tela $1 n\xE3o registrada"],
|
|
189
|
+
[/permission denied/i, "sem permiss\xE3o"],
|
|
190
|
+
[/timeout|timed out/i, "tempo esgotado"],
|
|
191
|
+
[/network/i, "falha de rede"]
|
|
192
|
+
];
|
|
193
|
+
var MAX_REASON_LEN = 80;
|
|
194
|
+
function humanizeError(raw, _action) {
|
|
195
|
+
if (!raw) return void 0;
|
|
196
|
+
const trimmed = String(raw).trim();
|
|
197
|
+
if (!trimmed) return void 0;
|
|
198
|
+
for (const [pattern, replacement] of FRIENDLY_REWRITES) {
|
|
199
|
+
if (pattern.test(trimmed)) {
|
|
200
|
+
return finalize(trimmed.replace(pattern, replacement));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (TECHNICAL_PATTERNS.some((p) => p.test(trimmed))) {
|
|
204
|
+
return "algo deu errado";
|
|
205
|
+
}
|
|
206
|
+
if (trimmed.length > MAX_REASON_LEN) {
|
|
207
|
+
return "algo deu errado";
|
|
208
|
+
}
|
|
209
|
+
return finalize(trimmed);
|
|
210
|
+
}
|
|
211
|
+
function finalize(text) {
|
|
212
|
+
let out = text.trim();
|
|
213
|
+
if (out.length > MAX_REASON_LEN) out = out.slice(0, MAX_REASON_LEN - 1).trimEnd() + "\u2026";
|
|
214
|
+
if (out.length > 0) out = out[0].toLowerCase() + out.slice(1);
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
function uiVerb(raw) {
|
|
218
|
+
switch ((raw ?? "press").toLowerCase()) {
|
|
219
|
+
case "press":
|
|
220
|
+
case "tap":
|
|
221
|
+
case "click":
|
|
222
|
+
return { infinitive: "clicar", gerund: "Clicando", past: "Cliquei" };
|
|
223
|
+
case "longpress":
|
|
224
|
+
case "long_press":
|
|
225
|
+
return { infinitive: "pressionar", gerund: "Pressionando", past: "Pressionei" };
|
|
226
|
+
case "scroll":
|
|
227
|
+
case "scroll_to":
|
|
228
|
+
return { infinitive: "rolar", gerund: "Rolando", past: "Rolei" };
|
|
229
|
+
case "focus":
|
|
230
|
+
return { infinitive: "focar", gerund: "Focando", past: "Foquei" };
|
|
231
|
+
default:
|
|
232
|
+
return { infinitive: "interagir", gerund: "Interagindo", past: "Interagi" };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function summariseFields(fields) {
|
|
236
|
+
if (!Array.isArray(fields) || fields.length === 0) return void 0;
|
|
237
|
+
const parts = [];
|
|
238
|
+
const visible = fields.slice(0, 3);
|
|
239
|
+
for (const f of visible) {
|
|
240
|
+
if (!f || typeof f !== "object") continue;
|
|
241
|
+
const obj = f;
|
|
242
|
+
const name = humanFieldName(obj.label, obj.fieldId);
|
|
243
|
+
if (!name) continue;
|
|
244
|
+
const fieldIdStr = typeof obj.fieldId === "string" ? obj.fieldId : void 0;
|
|
245
|
+
const labelStr = typeof obj.label === "string" ? obj.label : void 0;
|
|
246
|
+
const fieldTypeStr = typeof obj.fieldType === "string" ? obj.fieldType : void 0;
|
|
247
|
+
const sensitive = isSensitiveFieldName(fieldIdStr, labelStr, void 0, fieldTypeStr);
|
|
248
|
+
if (sensitive) {
|
|
249
|
+
parts.push(`${name}=\u2022\u2022\u2022\u2022`);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const value = formatFieldValue(obj.value);
|
|
253
|
+
parts.push(value !== void 0 ? `${name}=${value}` : name);
|
|
254
|
+
}
|
|
255
|
+
if (parts.length === 0) return void 0;
|
|
256
|
+
const extra = fields.length - parts.length;
|
|
257
|
+
return extra > 0 ? `${parts.join(", ")} +${extra}` : parts.join(", ");
|
|
258
|
+
}
|
|
259
|
+
function formatFieldValue(value) {
|
|
260
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
261
|
+
if (typeof value === "string") {
|
|
262
|
+
const v = value.length > 18 ? `${value.slice(0, 17)}\u2026` : value;
|
|
263
|
+
return `"${v}"`;
|
|
264
|
+
}
|
|
265
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
266
|
+
return void 0;
|
|
267
|
+
}
|
|
268
|
+
function humanActionTarget(payload) {
|
|
269
|
+
if (!payload || typeof payload !== "object") return "";
|
|
270
|
+
const record = payload;
|
|
271
|
+
const friendly = readFriendlyString(record, "label") || readFriendlyString(record, "actionDescription") || readFriendlyString(record, "title") || readFriendlyString(record, "name");
|
|
272
|
+
if (friendly) return friendly;
|
|
273
|
+
return humanComponentId(
|
|
274
|
+
readString(payload, "componentId") || readString(payload, "targetId")
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
function humanFieldName(label, fieldId) {
|
|
278
|
+
if (typeof label === "string" && label.trim()) return label.trim();
|
|
279
|
+
if (typeof fieldId !== "string" || isTechnicalIdentifier(fieldId)) return void 0;
|
|
280
|
+
return humanComponentId(fieldId) || void 0;
|
|
281
|
+
}
|
|
282
|
+
function readFriendlyString(record, key) {
|
|
283
|
+
const value = record[key];
|
|
284
|
+
if (typeof value !== "string") return "";
|
|
285
|
+
const trimmed = value.trim();
|
|
286
|
+
if (!trimmed || isTechnicalIdentifier(trimmed)) return "";
|
|
287
|
+
return trimmed;
|
|
288
|
+
}
|
|
289
|
+
function humanComponentId(id) {
|
|
290
|
+
if (!id) return "";
|
|
291
|
+
if (isTechnicalIdentifier(id)) return "";
|
|
292
|
+
const cleaned = id.replace(/^(btn-|btn_|button-|component-)/i, "").replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").trim();
|
|
293
|
+
if (!cleaned) return "";
|
|
294
|
+
return cleaned[0].toUpperCase() + cleaned.slice(1);
|
|
295
|
+
}
|
|
296
|
+
function isTechnicalIdentifier(value) {
|
|
297
|
+
const raw = value.trim();
|
|
298
|
+
if (!raw) return false;
|
|
299
|
+
if (/^el:/i.test(raw)) return true;
|
|
300
|
+
if (/^list-\d+-item-\d+$/i.test(raw)) return true;
|
|
301
|
+
if (/(testid|test-id|accessibilityid|accessibility-id|automationid|automation-id)/i.test(raw)) {
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
if (/^[a-z]+:[^ ]+:[^ ]+/i.test(raw)) return true;
|
|
305
|
+
if (/^[A-Za-z][\w-]*:[^\s]+$/i.test(raw)) return true;
|
|
306
|
+
if (/^[a-z]+(?:[-_][a-z]+)*[-_][a-z0-9]{5,}$/i.test(raw)) return true;
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
function readString(payload, key) {
|
|
310
|
+
if (!payload || typeof payload !== "object") return "";
|
|
311
|
+
const v = payload[key];
|
|
312
|
+
return typeof v === "string" ? v : "";
|
|
313
|
+
}
|
|
314
|
+
function readArray(payload, key) {
|
|
315
|
+
if (!payload || typeof payload !== "object") return [];
|
|
316
|
+
const v = payload[key];
|
|
317
|
+
return Array.isArray(v) ? v : [];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/theme/tokens.ts
|
|
321
|
+
var defaultLightTheme = {
|
|
322
|
+
colors: {
|
|
323
|
+
primary: "#6366f1",
|
|
324
|
+
secondary: "#8b5cf6",
|
|
325
|
+
background: "#ffffff",
|
|
326
|
+
surface: "#f3f4f6",
|
|
327
|
+
text: "#1f2937",
|
|
328
|
+
textSecondary: "#6b7280",
|
|
329
|
+
border: "#e5e7eb",
|
|
330
|
+
// NOTE (BACKLOG 4.2): success/warning/error used to have ZERO visual
|
|
331
|
+
// consumers — nothing read them, so ActionBreadcrumb's tones and the
|
|
332
|
+
// confirm card's destructive accents were hardcoded separately with
|
|
333
|
+
// their OWN hex values (#10B981/#EF4444/#9CA3AF-ish grays). Since no
|
|
334
|
+
// other UI in the SDK reads these three tokens, we picked these
|
|
335
|
+
// defaults to exactly match those pre-existing hardcoded values —
|
|
336
|
+
// wiring the tokens up to ActionBreadcrumb (see breadcrumbPresentation.ts)
|
|
337
|
+
// is now a no-op for anyone on the bundled default theme, while a
|
|
338
|
+
// custom theme's success/warning/error now actually do something.
|
|
339
|
+
success: "#10b981",
|
|
340
|
+
warning: "#9ca3af",
|
|
341
|
+
error: "#ef4444",
|
|
342
|
+
// Same value as `text` — split out so an app can diverge them, but
|
|
343
|
+
// any app that doesn't set these renders pixel-identical to before.
|
|
344
|
+
buttonText: "#1f2937",
|
|
345
|
+
bubbleText: "#1f2937",
|
|
346
|
+
userBubbleText: "#ffffff"
|
|
347
|
+
},
|
|
348
|
+
typography: {
|
|
349
|
+
fontFamily: "System",
|
|
350
|
+
fontSizeXs: 11,
|
|
351
|
+
fontSizeSm: 13,
|
|
352
|
+
fontSizeBase: 15,
|
|
353
|
+
fontSizeLg: 18
|
|
354
|
+
},
|
|
355
|
+
radii: {
|
|
356
|
+
sm: 8,
|
|
357
|
+
md: 12,
|
|
358
|
+
lg: 16
|
|
359
|
+
},
|
|
360
|
+
shadows: {
|
|
361
|
+
appearance: "light"
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var defaultDarkTheme = {
|
|
365
|
+
colors: {
|
|
366
|
+
primary: "#818cf8",
|
|
367
|
+
secondary: "#a78bfa",
|
|
368
|
+
background: "#0f172a",
|
|
369
|
+
surface: "#1e293b",
|
|
370
|
+
text: "#f1f5f9",
|
|
371
|
+
textSecondary: "#94a3b8",
|
|
372
|
+
border: "#334155",
|
|
373
|
+
// Kept IDENTICAL to the light theme's success/warning/error (not the
|
|
374
|
+
// usual "lighter for dark bg" adjustment other tokens get) — the
|
|
375
|
+
// breadcrumb's tones were a fixed, theme-agnostic hardcoded palette
|
|
376
|
+
// before this token existed, rendering the SAME hex in light and dark
|
|
377
|
+
// mode. Diverging them here would change dark-mode breadcrumbs even
|
|
378
|
+
// though nothing in the token tree asked for that.
|
|
379
|
+
success: "#10b981",
|
|
380
|
+
warning: "#9ca3af",
|
|
381
|
+
error: "#ef4444",
|
|
382
|
+
buttonText: "#f1f5f9",
|
|
383
|
+
bubbleText: "#f1f5f9",
|
|
384
|
+
userBubbleText: "#ffffff"
|
|
385
|
+
},
|
|
386
|
+
typography: { ...defaultLightTheme.typography },
|
|
387
|
+
radii: { ...defaultLightTheme.radii },
|
|
388
|
+
shadows: { appearance: "dark" }
|
|
389
|
+
};
|
|
390
|
+
function mergeThemeTokens(base, partial) {
|
|
391
|
+
if (!partial) return base;
|
|
392
|
+
return {
|
|
393
|
+
colors: { ...base.colors, ...partial.colors ?? {} },
|
|
394
|
+
typography: { ...base.typography, ...partial.typography ?? {} },
|
|
395
|
+
radii: { ...base.radii, ...partial.radii ?? {} },
|
|
396
|
+
shadows: { ...base.shadows, ...partial.shadows ?? {} }
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function resolveBaseTheme(mode, systemScheme) {
|
|
400
|
+
if (mode === "dark") return defaultDarkTheme;
|
|
401
|
+
if (mode === "light") return defaultLightTheme;
|
|
402
|
+
return systemScheme === "dark" ? defaultDarkTheme : defaultLightTheme;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/components/breadcrumbPresentation.ts
|
|
406
|
+
var DEFAULT_BREADCRUMB_PALETTE = {
|
|
407
|
+
primary: defaultLightTheme.colors.primary,
|
|
408
|
+
text: defaultLightTheme.colors.text,
|
|
409
|
+
textSecondary: defaultLightTheme.colors.textSecondary,
|
|
410
|
+
surface: defaultLightTheme.colors.surface,
|
|
411
|
+
border: defaultLightTheme.colors.border,
|
|
412
|
+
success: defaultLightTheme.colors.success,
|
|
413
|
+
warning: defaultLightTheme.colors.warning,
|
|
414
|
+
error: defaultLightTheme.colors.error
|
|
415
|
+
};
|
|
416
|
+
var KNOWN_TINTS = {
|
|
417
|
+
[DEFAULT_BREADCRUMB_PALETTE.primary.toUpperCase()]: "#EEF2FF",
|
|
418
|
+
[DEFAULT_BREADCRUMB_PALETTE.success.toUpperCase()]: "#ECFDF5",
|
|
419
|
+
[DEFAULT_BREADCRUMB_PALETTE.warning.toUpperCase()]: "#F3F4F6",
|
|
420
|
+
[DEFAULT_BREADCRUMB_PALETTE.error.toUpperCase()]: "#FEF2F2"
|
|
421
|
+
};
|
|
422
|
+
function tint(hex) {
|
|
423
|
+
const known = KNOWN_TINTS[hex.toUpperCase()];
|
|
424
|
+
if (known) return known;
|
|
425
|
+
const base = hex.length === 9 ? hex.slice(0, 7) : hex;
|
|
426
|
+
return `${base}1a`;
|
|
427
|
+
}
|
|
428
|
+
function deriveStateTone(palette = DEFAULT_BREADCRUMB_PALETTE) {
|
|
429
|
+
return {
|
|
430
|
+
pending: { color: palette.warning, iconBg: tint(palette.warning) },
|
|
431
|
+
running: { color: palette.primary, iconBg: tint(palette.primary) },
|
|
432
|
+
success: { color: palette.success, iconBg: tint(palette.success) },
|
|
433
|
+
failed: { color: palette.error, iconBg: tint(palette.error) }
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
deriveStateTone();
|
|
437
|
+
function iconGlyphFor(state) {
|
|
438
|
+
switch (state) {
|
|
439
|
+
case "pending":
|
|
440
|
+
return "\u25CB";
|
|
441
|
+
case "success":
|
|
442
|
+
return "\u2713";
|
|
443
|
+
case "failed":
|
|
444
|
+
return "\u2717";
|
|
445
|
+
case "running":
|
|
446
|
+
default:
|
|
447
|
+
return "\xB7";
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function isDestructiveAction(action) {
|
|
451
|
+
return action.payload?.destructive === true || action.destructive === true;
|
|
452
|
+
}
|
|
453
|
+
DEFAULT_BREADCRUMB_PALETTE.error;
|
|
454
|
+
function deriveConfirmCardContent(action, palette = DEFAULT_BREADCRUMB_PALETTE) {
|
|
455
|
+
const payload = action.payload ?? {};
|
|
456
|
+
const isDestructive = isDestructiveAction(action);
|
|
457
|
+
const message = typeof payload.message === "string" && payload.message || typeof payload.actionDescription === "string" && payload.actionDescription || "Confirme para prosseguir";
|
|
458
|
+
const title = typeof payload.title === "string" && payload.title || (isDestructive ? "A\xE7\xE3o destrutiva" : "Confirma\xE7\xE3o necess\xE1ria");
|
|
459
|
+
const confirmLabel2 = typeof payload.confirmLabel === "string" && payload.confirmLabel || (isDestructive ? "Sim, executar" : "Confirmar");
|
|
460
|
+
const cancelLabel = typeof payload.cancelLabel === "string" && payload.cancelLabel || "Cancelar";
|
|
461
|
+
return {
|
|
462
|
+
isDestructive,
|
|
463
|
+
title,
|
|
464
|
+
message,
|
|
465
|
+
confirmLabel: confirmLabel2,
|
|
466
|
+
cancelLabel,
|
|
467
|
+
glyph: isDestructive ? "\u26A0" : "?",
|
|
468
|
+
actionColor: isDestructive ? palette.error : palette.primary,
|
|
469
|
+
cardBackground: palette.surface,
|
|
470
|
+
cardBorderColor: isDestructive ? palette.error : palette.border,
|
|
471
|
+
messageColor: palette.textSecondary,
|
|
472
|
+
cancelBackground: palette.border,
|
|
473
|
+
cancelTextColor: palette.textSecondary
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/components/ActionBreadcrumb.tsx
|
|
478
|
+
function ActionBreadcrumb({
|
|
479
|
+
actions,
|
|
480
|
+
primaryColor,
|
|
481
|
+
textColor,
|
|
482
|
+
fontFamily,
|
|
483
|
+
palette,
|
|
484
|
+
requireApprovalFor,
|
|
485
|
+
onApprove,
|
|
486
|
+
onReject
|
|
487
|
+
}) {
|
|
488
|
+
if (actions.length === 0) return null;
|
|
489
|
+
const resolvedPalette = {
|
|
490
|
+
...DEFAULT_BREADCRUMB_PALETTE,
|
|
491
|
+
...palette,
|
|
492
|
+
primary: primaryColor,
|
|
493
|
+
text: textColor
|
|
494
|
+
};
|
|
495
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: styles.container }, actions.map((action) => /* @__PURE__ */ React7__default.createElement(
|
|
496
|
+
BreadcrumbRow,
|
|
497
|
+
{
|
|
498
|
+
key: action.id,
|
|
499
|
+
action,
|
|
500
|
+
primaryColor,
|
|
501
|
+
textColor,
|
|
502
|
+
fontFamily,
|
|
503
|
+
palette: resolvedPalette,
|
|
504
|
+
needsApproval: requireApprovalFor?.(action) ?? false,
|
|
505
|
+
onApprove,
|
|
506
|
+
onReject
|
|
507
|
+
}
|
|
508
|
+
)));
|
|
509
|
+
}
|
|
510
|
+
function BreadcrumbRow(props) {
|
|
511
|
+
const { action, needsApproval } = props;
|
|
512
|
+
if (action.type === "confirm" && action.status === "pending" && needsApproval) {
|
|
513
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
514
|
+
ConfirmCard,
|
|
515
|
+
{
|
|
516
|
+
action,
|
|
517
|
+
palette: props.palette,
|
|
518
|
+
fontFamily: props.fontFamily,
|
|
519
|
+
onApprove: props.onApprove,
|
|
520
|
+
onReject: props.onReject
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
return /* @__PURE__ */ React7__default.createElement(RegularBreadcrumbRow, { ...props });
|
|
525
|
+
}
|
|
526
|
+
function RegularBreadcrumbRow({
|
|
527
|
+
action,
|
|
528
|
+
primaryColor,
|
|
529
|
+
textColor,
|
|
530
|
+
fontFamily,
|
|
531
|
+
palette,
|
|
532
|
+
needsApproval,
|
|
533
|
+
onApprove,
|
|
534
|
+
onReject
|
|
535
|
+
}) {
|
|
536
|
+
const { label, state } = describeAction(action);
|
|
537
|
+
const tone = deriveStateTone(palette)[state];
|
|
538
|
+
const fade = useRef(new Animated.Value(0)).current;
|
|
539
|
+
const pulse = useRef(new Animated.Value(1)).current;
|
|
540
|
+
const lastState = useRef(state);
|
|
541
|
+
useEffect(() => {
|
|
542
|
+
Animated.timing(fade, {
|
|
543
|
+
toValue: 1,
|
|
544
|
+
duration: 180,
|
|
545
|
+
useNativeDriver: true
|
|
546
|
+
}).start();
|
|
547
|
+
}, [fade]);
|
|
548
|
+
useEffect(() => {
|
|
549
|
+
if (lastState.current === state) return;
|
|
550
|
+
lastState.current = state;
|
|
551
|
+
pulse.setValue(0.4);
|
|
552
|
+
Animated.timing(pulse, {
|
|
553
|
+
toValue: 1,
|
|
554
|
+
duration: 220,
|
|
555
|
+
useNativeDriver: true
|
|
556
|
+
}).start();
|
|
557
|
+
}, [state, pulse]);
|
|
558
|
+
const isDestructive = isDestructiveAction(action);
|
|
559
|
+
return /* @__PURE__ */ React7__default.createElement(Animated.View, { style: [styles.row, { opacity: fade }] }, /* @__PURE__ */ React7__default.createElement(Animated.View, { style: { opacity: pulse, flexDirection: "row", alignItems: "flex-start", flex: 1 } }, /* @__PURE__ */ React7__default.createElement(StateIcon, { state, tone, primaryColor }), /* @__PURE__ */ React7__default.createElement(
|
|
560
|
+
Text,
|
|
561
|
+
{
|
|
562
|
+
style: [
|
|
563
|
+
styles.label,
|
|
564
|
+
{ color: state === "failed" ? tone.color : textColor, fontFamily }
|
|
565
|
+
],
|
|
566
|
+
accessibilityLabel: `${state}: ${label}`
|
|
567
|
+
},
|
|
568
|
+
label,
|
|
569
|
+
isDestructive && /* @__PURE__ */ React7__default.createElement(Text, { style: [styles.destructiveTag, { color: palette.error }] }, " \u26A0 destrutiva")
|
|
570
|
+
)), needsApproval && action.status === "pending" && onApprove && onReject && /* @__PURE__ */ React7__default.createElement(View, { style: styles.approvalButtons }, /* @__PURE__ */ React7__default.createElement(
|
|
571
|
+
TouchableOpacity,
|
|
572
|
+
{
|
|
573
|
+
onPress: () => onReject(action.id),
|
|
574
|
+
style: [
|
|
575
|
+
styles.btn,
|
|
576
|
+
{ backgroundColor: deriveStateTone(palette).failed.iconBg }
|
|
577
|
+
],
|
|
578
|
+
activeOpacity: 0.7,
|
|
579
|
+
accessibilityLabel: "Rejeitar a\xE7\xE3o"
|
|
580
|
+
},
|
|
581
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: [styles.rejectBtnText, { color: palette.error }] }, "Rejeitar")
|
|
582
|
+
), /* @__PURE__ */ React7__default.createElement(
|
|
583
|
+
TouchableOpacity,
|
|
584
|
+
{
|
|
585
|
+
onPress: () => onApprove(action.id),
|
|
586
|
+
style: [styles.btn, styles.approveBtn, { backgroundColor: primaryColor }],
|
|
587
|
+
activeOpacity: 0.7,
|
|
588
|
+
accessibilityLabel: "Aprovar a\xE7\xE3o"
|
|
589
|
+
},
|
|
590
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: styles.approveBtnText }, "Aprovar")
|
|
591
|
+
)));
|
|
592
|
+
}
|
|
593
|
+
function ConfirmCard({ action, palette, fontFamily, onApprove, onReject }) {
|
|
594
|
+
const {
|
|
595
|
+
isDestructive,
|
|
596
|
+
title,
|
|
597
|
+
message,
|
|
598
|
+
confirmLabel: confirmLabel2,
|
|
599
|
+
cancelLabel,
|
|
600
|
+
glyph,
|
|
601
|
+
actionColor,
|
|
602
|
+
cardBackground,
|
|
603
|
+
cardBorderColor,
|
|
604
|
+
messageColor,
|
|
605
|
+
cancelBackground,
|
|
606
|
+
cancelTextColor
|
|
607
|
+
} = deriveConfirmCardContent(action, palette);
|
|
608
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
609
|
+
View,
|
|
610
|
+
{
|
|
611
|
+
style: [
|
|
612
|
+
confirmCardStyles.card,
|
|
613
|
+
{ backgroundColor: cardBackground, borderColor: cardBorderColor }
|
|
614
|
+
],
|
|
615
|
+
testID: "assistant-confirm-card"
|
|
616
|
+
},
|
|
617
|
+
/* @__PURE__ */ React7__default.createElement(View, { style: confirmCardStyles.headerRow }, /* @__PURE__ */ React7__default.createElement(Text, { style: [confirmCardStyles.glyph, { color: actionColor }] }, glyph), /* @__PURE__ */ React7__default.createElement(
|
|
618
|
+
Text,
|
|
619
|
+
{
|
|
620
|
+
style: [
|
|
621
|
+
confirmCardStyles.title,
|
|
622
|
+
{ color: isDestructive ? palette.error : palette.text, fontFamily }
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
title
|
|
626
|
+
)),
|
|
627
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: [confirmCardStyles.message, { color: messageColor, fontFamily }] }, message),
|
|
628
|
+
/* @__PURE__ */ React7__default.createElement(View, { style: confirmCardStyles.buttons }, /* @__PURE__ */ React7__default.createElement(
|
|
629
|
+
TouchableOpacity,
|
|
630
|
+
{
|
|
631
|
+
onPress: () => onReject?.(action.id),
|
|
632
|
+
style: [confirmCardStyles.btn, { backgroundColor: cancelBackground }],
|
|
633
|
+
activeOpacity: 0.7,
|
|
634
|
+
accessibilityLabel: "Cancelar",
|
|
635
|
+
testID: "assistant-confirm-reject"
|
|
636
|
+
},
|
|
637
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: [confirmCardStyles.cancelText, { color: cancelTextColor, fontFamily }] }, cancelLabel)
|
|
638
|
+
), /* @__PURE__ */ React7__default.createElement(
|
|
639
|
+
TouchableOpacity,
|
|
640
|
+
{
|
|
641
|
+
onPress: () => onApprove?.(action.id),
|
|
642
|
+
style: [
|
|
643
|
+
confirmCardStyles.btn,
|
|
644
|
+
confirmCardStyles.confirmBtn,
|
|
645
|
+
{ backgroundColor: actionColor }
|
|
646
|
+
],
|
|
647
|
+
activeOpacity: 0.7,
|
|
648
|
+
accessibilityLabel: "Confirmar",
|
|
649
|
+
testID: "assistant-confirm-approve"
|
|
650
|
+
},
|
|
651
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: [confirmCardStyles.confirmText, { fontFamily }] }, confirmLabel2)
|
|
652
|
+
))
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
var confirmCardStyles = StyleSheet.create({
|
|
656
|
+
card: {
|
|
657
|
+
alignSelf: "stretch",
|
|
658
|
+
marginVertical: 6,
|
|
659
|
+
padding: 14,
|
|
660
|
+
borderRadius: 14,
|
|
661
|
+
borderWidth: 1
|
|
662
|
+
// backgroundColor / borderColor set inline — theme-driven (surface / border / error).
|
|
663
|
+
},
|
|
664
|
+
headerRow: {
|
|
665
|
+
flexDirection: "row",
|
|
666
|
+
alignItems: "center",
|
|
667
|
+
gap: 8,
|
|
668
|
+
marginBottom: 6
|
|
669
|
+
},
|
|
670
|
+
glyph: {
|
|
671
|
+
fontSize: 16,
|
|
672
|
+
fontWeight: "700"
|
|
673
|
+
},
|
|
674
|
+
title: {
|
|
675
|
+
fontSize: 14,
|
|
676
|
+
fontWeight: "700",
|
|
677
|
+
flexShrink: 1
|
|
678
|
+
},
|
|
679
|
+
message: {
|
|
680
|
+
fontSize: 14,
|
|
681
|
+
lineHeight: 20,
|
|
682
|
+
// color set inline — theme-driven (textSecondary).
|
|
683
|
+
marginBottom: 12
|
|
684
|
+
},
|
|
685
|
+
buttons: {
|
|
686
|
+
flexDirection: "row",
|
|
687
|
+
gap: 8,
|
|
688
|
+
justifyContent: "flex-end"
|
|
689
|
+
},
|
|
690
|
+
btn: {
|
|
691
|
+
paddingHorizontal: 14,
|
|
692
|
+
paddingVertical: 8,
|
|
693
|
+
borderRadius: 10,
|
|
694
|
+
minWidth: 96,
|
|
695
|
+
alignItems: "center"
|
|
696
|
+
},
|
|
697
|
+
cancelText: {
|
|
698
|
+
fontSize: 13,
|
|
699
|
+
fontWeight: "600"
|
|
700
|
+
// color set inline — theme-driven (textSecondary).
|
|
701
|
+
},
|
|
702
|
+
confirmBtn: {
|
|
703
|
+
// backgroundColor set inline
|
|
704
|
+
},
|
|
705
|
+
confirmText: {
|
|
706
|
+
fontSize: 13,
|
|
707
|
+
fontWeight: "600",
|
|
708
|
+
color: "#FFFFFF"
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
function StateIcon({
|
|
712
|
+
state,
|
|
713
|
+
tone,
|
|
714
|
+
primaryColor
|
|
715
|
+
}) {
|
|
716
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: [styles.iconCircle, { backgroundColor: tone.iconBg }] }, state === "running" ? /* @__PURE__ */ React7__default.createElement(ActivityIndicator, { size: "small", color: primaryColor }) : /* @__PURE__ */ React7__default.createElement(Text, { style: [styles.iconGlyph, { color: tone.color }] }, iconGlyphFor(state)));
|
|
717
|
+
}
|
|
718
|
+
var styles = StyleSheet.create({
|
|
719
|
+
container: {
|
|
720
|
+
alignSelf: "stretch",
|
|
721
|
+
marginBottom: 8,
|
|
722
|
+
paddingVertical: 4
|
|
723
|
+
},
|
|
724
|
+
row: {
|
|
725
|
+
flexDirection: "row",
|
|
726
|
+
alignItems: "center",
|
|
727
|
+
paddingVertical: 6,
|
|
728
|
+
paddingHorizontal: 4
|
|
729
|
+
},
|
|
730
|
+
iconCircle: {
|
|
731
|
+
width: 22,
|
|
732
|
+
height: 22,
|
|
733
|
+
borderRadius: 11,
|
|
734
|
+
alignItems: "center",
|
|
735
|
+
justifyContent: "center",
|
|
736
|
+
marginRight: 10,
|
|
737
|
+
marginTop: 1
|
|
738
|
+
},
|
|
739
|
+
iconGlyph: {
|
|
740
|
+
fontSize: 13,
|
|
741
|
+
fontWeight: "700",
|
|
742
|
+
lineHeight: 16
|
|
743
|
+
},
|
|
744
|
+
label: {
|
|
745
|
+
flex: 1,
|
|
746
|
+
fontSize: 14,
|
|
747
|
+
lineHeight: 19
|
|
748
|
+
},
|
|
749
|
+
destructiveTag: {
|
|
750
|
+
// color set inline — theme-driven (error).
|
|
751
|
+
fontSize: 12,
|
|
752
|
+
fontWeight: "700"
|
|
753
|
+
},
|
|
754
|
+
approvalButtons: {
|
|
755
|
+
flexDirection: "row",
|
|
756
|
+
gap: 6,
|
|
757
|
+
marginLeft: 8
|
|
758
|
+
},
|
|
759
|
+
btn: {
|
|
760
|
+
paddingHorizontal: 10,
|
|
761
|
+
paddingVertical: 5,
|
|
762
|
+
borderRadius: 8
|
|
763
|
+
},
|
|
764
|
+
rejectBtnText: {
|
|
765
|
+
fontSize: 12,
|
|
766
|
+
fontWeight: "600"
|
|
767
|
+
// color set inline — theme-driven (error).
|
|
768
|
+
},
|
|
769
|
+
approveBtn: {
|
|
770
|
+
// backgroundColor set inline
|
|
771
|
+
},
|
|
772
|
+
approveBtnText: {
|
|
773
|
+
fontSize: 12,
|
|
774
|
+
fontWeight: "600",
|
|
775
|
+
color: "#FFFFFF"
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
// src/components/personalization.ts
|
|
780
|
+
function str(value) {
|
|
781
|
+
const trimmed = value?.trim();
|
|
782
|
+
return trimmed ? trimmed : void 0;
|
|
783
|
+
}
|
|
784
|
+
function resolveTheme(themeProp, remote) {
|
|
785
|
+
return themeProp ?? remote?.theme ?? void 0;
|
|
786
|
+
}
|
|
787
|
+
function resolveThemeMode(themeModeProp, remote) {
|
|
788
|
+
return themeModeProp ?? remote?.theme?.mode ?? "auto";
|
|
789
|
+
}
|
|
790
|
+
function resolveLocaleProp(localeProp, remote) {
|
|
791
|
+
return localeProp ?? remote?.defaultLocale ?? null;
|
|
792
|
+
}
|
|
793
|
+
function resolvePersonalization(props, remote) {
|
|
794
|
+
return {
|
|
795
|
+
headerTitle: props.headerTitle ?? props.assistantName ?? str(remote?.chatTitle) ?? str(remote?.assistantName),
|
|
796
|
+
assistantAvatar: props.assistantAvatar ?? str(remote?.assistantAvatar),
|
|
797
|
+
emptyStateIcon: props.emptyStateIcon ?? str(remote?.emptyStateIcon),
|
|
798
|
+
welcomeMessage: props.welcomeMessage ?? str(remote?.welcomeMessage),
|
|
799
|
+
// Remote is always a boolean when present (server coerces legacy
|
|
800
|
+
// nulls to true), so `??` chains cleanly to the SDK default.
|
|
801
|
+
poweredByVisible: props.poweredByVisible ?? remote?.poweredByVisible ?? true,
|
|
802
|
+
triggerButtonColor: props.triggerButtonColor ?? str(remote?.triggerButtonColor),
|
|
803
|
+
triggerButtonImage: props.triggerButtonImage ?? str(remote?.triggerButtonImageUrl)
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
function ChatIcon({ size = 24, color = "#ffffff" }) {
|
|
807
|
+
return /* @__PURE__ */ React7.createElement(Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React7.createElement(
|
|
808
|
+
Path,
|
|
809
|
+
{
|
|
810
|
+
d: "M5 3.5h14a3 3 0 0 1 3 3v9a3 3 0 0 1-3 3h-7.4l-4.6 4v-4H5a3 3 0 0 1-3-3v-9a3 3 0 0 1 3-3z",
|
|
811
|
+
fill: color
|
|
812
|
+
}
|
|
813
|
+
));
|
|
814
|
+
}
|
|
815
|
+
function HeadsetIcon({ size = 18, color = "#ffffff" }) {
|
|
816
|
+
return /* @__PURE__ */ React7.createElement(Svg, { width: size, height: size, viewBox: "0 0 24 24", fill: "none" }, /* @__PURE__ */ React7.createElement(
|
|
817
|
+
Path,
|
|
818
|
+
{
|
|
819
|
+
d: "M12 3a8 8 0 0 0-8 8v5.5A2.5 2.5 0 0 0 6.5 19H8a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H6v-1a6 6 0 0 1 12 0v1h-2a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h1.5a2.5 2.5 0 0 0 2.5-2.5V11a8 8 0 0 0-8-8z",
|
|
820
|
+
fill: color
|
|
821
|
+
}
|
|
822
|
+
));
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/components/chatTimeline.ts
|
|
826
|
+
function buildChatTimeline(messages, actions) {
|
|
827
|
+
const visibleMessageIds = new Set(messages.map((message) => message.id));
|
|
828
|
+
const visibleActionPrompts = actions.filter(
|
|
829
|
+
(action) => action.type === "confirm" && action.status === "pending"
|
|
830
|
+
);
|
|
831
|
+
const actionsByMessageId = /* @__PURE__ */ new Map();
|
|
832
|
+
const orphanActions = [];
|
|
833
|
+
for (const action of visibleActionPrompts) {
|
|
834
|
+
if (action.messageId && visibleMessageIds.has(action.messageId)) {
|
|
835
|
+
const list = actionsByMessageId.get(action.messageId) ?? [];
|
|
836
|
+
list.push(action);
|
|
837
|
+
actionsByMessageId.set(action.messageId, list);
|
|
838
|
+
} else {
|
|
839
|
+
orphanActions.push(action);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
const timeline = [];
|
|
843
|
+
for (const msg of messages) {
|
|
844
|
+
timeline.push({ type: "message", data: msg });
|
|
845
|
+
if (msg.role === "assistant") {
|
|
846
|
+
const msgActions = actionsByMessageId.get(msg.id);
|
|
847
|
+
if (msgActions && msgActions.length > 0) {
|
|
848
|
+
timeline.push({ type: "breadcrumb", key: `bc-${msg.id}`, actions: msgActions });
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (orphanActions.length > 0) {
|
|
853
|
+
timeline.push({ type: "breadcrumb", key: "bc-orphan", actions: orphanActions });
|
|
854
|
+
}
|
|
855
|
+
return timeline;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/components/markdownContent.ts
|
|
859
|
+
var ORDERED_LINE = /^\s*(\d+)\.\s+(.+)$/;
|
|
860
|
+
var BULLET_LINE = /^\s*[-*•]\s+(.+)$/;
|
|
861
|
+
var HEADING_LINE = /^\s*(#{1,6})\s+(.+)$/;
|
|
862
|
+
var FENCE_LINE = /^\s*```\s*([A-Za-z0-9+#_-]*)\s*$/;
|
|
863
|
+
var INLINE_PATTERN = /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)|\*\*(.+?)\*\*|__(.+?)__|`([^`]+)`|\*(.+?)\*|_(.+?)_/g;
|
|
864
|
+
var MIN_ENHANCE_LENGTH = 80;
|
|
865
|
+
var SENTENCE_SPLIT = /[^.!?]+[.!?]+(?=\s+[A-ZÁÉÍÓÚÀÈÌÒÙÂÊÎÔÛÃÕÇÑ¿¡"“]|$)|[^.!?]+$/g;
|
|
866
|
+
var CAPABILITY_SENTENCE = /^((?:No aplicativo,?|In (?:this|the) app,?|En la aplicaci[oó]n,?|Dans l['’]application,?)?(?:\s+(?:voc[eê]|voce|you|tu|usted|puedes|puede|peux))?\s*(?:pode(?:m|r)?|can|could|puedes|puede|peux|peut))\s+(.+?)\.?\s*$/i;
|
|
867
|
+
var INCLUDE_LIST_SENTENCE = /^(.+?)\s+(?:incluem|inclui|include|includes|incluye|incluyen|such as|for example|par exemple|por exemplo)\s+(.+?)\.?\s*$/i;
|
|
868
|
+
function looksLikeMarkdown(content) {
|
|
869
|
+
if (!content) return false;
|
|
870
|
+
return /\*\*.+?\*\*/.test(content) || /__.+?__/.test(content) || /`[^`\n]+`/.test(content) || /^\s*[-*•]\s+/m.test(content) || /^\s*\d+\.\s+/m.test(content) || /^\s*#{1,6}\s+/m.test(content) || /^\s*```/m.test(content) || /\[[^\]\n]+\]\(https?:\/\/[^\s)]+\)/.test(content);
|
|
871
|
+
}
|
|
872
|
+
function splitEnumeration(text) {
|
|
873
|
+
const trimmed = text.trim().replace(/\.\s*$/, "");
|
|
874
|
+
if (!trimmed) return [];
|
|
875
|
+
const commaParts = trimmed.split(",").map((part) => part.trim()).filter(Boolean);
|
|
876
|
+
if (commaParts.length <= 1) {
|
|
877
|
+
const conjParts = trimmed.split(/\s+(?:e|and|y|ou|or)\s+/i).map((part) => part.trim());
|
|
878
|
+
return conjParts.filter(Boolean);
|
|
879
|
+
}
|
|
880
|
+
const last = commaParts[commaParts.length - 1];
|
|
881
|
+
const conjMatch = last.match(/^(.+?)\s+(?:e|and|y|ou|or)\s+(.+)$/i);
|
|
882
|
+
if (conjMatch) {
|
|
883
|
+
commaParts[commaParts.length - 1] = conjMatch[1].trim();
|
|
884
|
+
commaParts.push(conjMatch[2].trim());
|
|
885
|
+
}
|
|
886
|
+
return commaParts.filter(Boolean);
|
|
887
|
+
}
|
|
888
|
+
function splitSentences(text) {
|
|
889
|
+
const matches = text.match(SENTENCE_SPLIT);
|
|
890
|
+
if (!matches) return [text.trim()];
|
|
891
|
+
return matches.map((part) => part.trim()).filter(Boolean);
|
|
892
|
+
}
|
|
893
|
+
function formatCapabilitySentence(sentence) {
|
|
894
|
+
const match = CAPABILITY_SENTENCE.exec(sentence.trim());
|
|
895
|
+
if (!match) return null;
|
|
896
|
+
const items = splitEnumeration(match[2] ?? "");
|
|
897
|
+
if (items.length < 3) return null;
|
|
898
|
+
const intro = `${match[1].trim()}:`;
|
|
899
|
+
return `${intro}
|
|
900
|
+
${items.map((item) => `- ${item}`).join("\n")}`;
|
|
901
|
+
}
|
|
902
|
+
function formatIncludeListSentence(sentence) {
|
|
903
|
+
const match = INCLUDE_LIST_SENTENCE.exec(sentence.trim());
|
|
904
|
+
if (!match) return null;
|
|
905
|
+
const intro = match[1].trim().replace(/[,:]\s*$/, "");
|
|
906
|
+
const items = splitEnumeration(match[2] ?? "");
|
|
907
|
+
if (items.length < 2) return null;
|
|
908
|
+
return `${intro}:
|
|
909
|
+
${items.map((item) => `- ${item}`).join("\n")}`;
|
|
910
|
+
}
|
|
911
|
+
function formatReadableSentence(sentence) {
|
|
912
|
+
return formatCapabilitySentence(sentence) ?? formatIncludeListSentence(sentence) ?? sentence.trim();
|
|
913
|
+
}
|
|
914
|
+
function enhancePlainAssistantContent(content) {
|
|
915
|
+
if (!content?.trim() || looksLikeMarkdown(content)) return content;
|
|
916
|
+
const trimmed = content.trim();
|
|
917
|
+
if (trimmed.length < MIN_ENHANCE_LENGTH) return content;
|
|
918
|
+
const sentences = splitSentences(trimmed);
|
|
919
|
+
if (sentences.length === 0) return content;
|
|
920
|
+
const blocks = sentences.map(formatReadableSentence);
|
|
921
|
+
const enhanced = blocks.join("\n\n");
|
|
922
|
+
return enhanced || content;
|
|
923
|
+
}
|
|
924
|
+
function needsWideChatLayout(content) {
|
|
925
|
+
if (!content?.trim()) return false;
|
|
926
|
+
const prepared = enhancePlainAssistantContent(content);
|
|
927
|
+
return looksLikeMarkdown(prepared) || /\n\n+/.test(prepared);
|
|
928
|
+
}
|
|
929
|
+
function parseMarkdownBlocks(content) {
|
|
930
|
+
const lines = content.replace(/\r\n/g, "\n").split("\n");
|
|
931
|
+
const blocks = [];
|
|
932
|
+
let paragraph = [];
|
|
933
|
+
let bulletItems = [];
|
|
934
|
+
let orderedItems = [];
|
|
935
|
+
let codeLines = null;
|
|
936
|
+
let codeLanguage;
|
|
937
|
+
const flushParagraph = () => {
|
|
938
|
+
if (paragraph.length === 0) return;
|
|
939
|
+
blocks.push({ kind: "paragraph", text: paragraph.join("\n") });
|
|
940
|
+
paragraph = [];
|
|
941
|
+
};
|
|
942
|
+
const flushBullets = () => {
|
|
943
|
+
if (bulletItems.length === 0) return;
|
|
944
|
+
blocks.push({ kind: "bullet", items: bulletItems });
|
|
945
|
+
bulletItems = [];
|
|
946
|
+
};
|
|
947
|
+
const flushOrdered = () => {
|
|
948
|
+
if (orderedItems.length === 0) return;
|
|
949
|
+
blocks.push({ kind: "ordered", items: orderedItems });
|
|
950
|
+
orderedItems = [];
|
|
951
|
+
};
|
|
952
|
+
const flushLists = () => {
|
|
953
|
+
flushBullets();
|
|
954
|
+
flushOrdered();
|
|
955
|
+
};
|
|
956
|
+
const flushCode = () => {
|
|
957
|
+
if (codeLines === null) return;
|
|
958
|
+
blocks.push({
|
|
959
|
+
kind: "codeblock",
|
|
960
|
+
code: codeLines.join("\n"),
|
|
961
|
+
...codeLanguage ? { language: codeLanguage } : {}
|
|
962
|
+
});
|
|
963
|
+
codeLines = null;
|
|
964
|
+
codeLanguage = void 0;
|
|
965
|
+
};
|
|
966
|
+
for (const line of lines) {
|
|
967
|
+
if (codeLines !== null) {
|
|
968
|
+
if (/^\s*```\s*$/.test(line)) {
|
|
969
|
+
flushCode();
|
|
970
|
+
} else {
|
|
971
|
+
codeLines.push(line);
|
|
972
|
+
}
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
const fence = FENCE_LINE.exec(line);
|
|
976
|
+
if (fence) {
|
|
977
|
+
flushParagraph();
|
|
978
|
+
flushLists();
|
|
979
|
+
codeLines = [];
|
|
980
|
+
codeLanguage = fence[1] || void 0;
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
const trimmed = line.trim();
|
|
984
|
+
if (!trimmed) {
|
|
985
|
+
flushParagraph();
|
|
986
|
+
flushLists();
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
const heading = HEADING_LINE.exec(line);
|
|
990
|
+
if (heading) {
|
|
991
|
+
flushParagraph();
|
|
992
|
+
flushLists();
|
|
993
|
+
const level = Math.min(heading[1].length, 3);
|
|
994
|
+
blocks.push({ kind: "heading", level, text: heading[2].trim() });
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
const ordered = ORDERED_LINE.exec(line);
|
|
998
|
+
if (ordered) {
|
|
999
|
+
flushParagraph();
|
|
1000
|
+
flushBullets();
|
|
1001
|
+
orderedItems.push(ordered[2].trim());
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
const bullet = BULLET_LINE.exec(line);
|
|
1005
|
+
if (bullet) {
|
|
1006
|
+
flushParagraph();
|
|
1007
|
+
flushOrdered();
|
|
1008
|
+
bulletItems.push(bullet[1].trim());
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
flushLists();
|
|
1012
|
+
paragraph.push(line);
|
|
1013
|
+
}
|
|
1014
|
+
flushParagraph();
|
|
1015
|
+
flushLists();
|
|
1016
|
+
flushCode();
|
|
1017
|
+
return blocks.length > 0 ? blocks : [{ kind: "paragraph", text: content }];
|
|
1018
|
+
}
|
|
1019
|
+
function parseInlineMarkdown(text) {
|
|
1020
|
+
if (!text) return [{ kind: "text", value: "" }];
|
|
1021
|
+
const segments = [];
|
|
1022
|
+
let lastIndex = 0;
|
|
1023
|
+
INLINE_PATTERN.lastIndex = 0;
|
|
1024
|
+
for (let match = INLINE_PATTERN.exec(text); match; match = INLINE_PATTERN.exec(text)) {
|
|
1025
|
+
if (match.index > lastIndex) {
|
|
1026
|
+
segments.push({ kind: "text", value: text.slice(lastIndex, match.index) });
|
|
1027
|
+
}
|
|
1028
|
+
if (match[1] && match[2]) {
|
|
1029
|
+
segments.push({ kind: "link", value: match[1].trim(), href: match[2] });
|
|
1030
|
+
} else if (match[3] || match[4]) {
|
|
1031
|
+
segments.push({ kind: "bold", value: (match[3] ?? match[4]).trim() });
|
|
1032
|
+
} else if (match[5]) {
|
|
1033
|
+
segments.push({ kind: "code", value: match[5] });
|
|
1034
|
+
} else if (match[6] || match[7]) {
|
|
1035
|
+
segments.push({ kind: "italic", value: (match[6] ?? match[7]).trim() });
|
|
1036
|
+
}
|
|
1037
|
+
lastIndex = INLINE_PATTERN.lastIndex;
|
|
1038
|
+
}
|
|
1039
|
+
if (lastIndex < text.length) {
|
|
1040
|
+
segments.push({ kind: "text", value: text.slice(lastIndex) });
|
|
1041
|
+
}
|
|
1042
|
+
return segments.length > 0 ? segments : [{ kind: "text", value: text }];
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// src/components/ChatMarkdown.tsx
|
|
1046
|
+
function segmentKey(segment, index) {
|
|
1047
|
+
return `${segment.kind}-${index}-${segment.value.slice(0, 12)}`;
|
|
1048
|
+
}
|
|
1049
|
+
function renderInlineSegments(text, theme, baseStyle) {
|
|
1050
|
+
const segments = parseInlineMarkdown(text);
|
|
1051
|
+
return segments.map((segment, index) => {
|
|
1052
|
+
const key = segmentKey(segment, index);
|
|
1053
|
+
switch (segment.kind) {
|
|
1054
|
+
case "bold":
|
|
1055
|
+
return /* @__PURE__ */ React7__default.createElement(Text, { key, style: [baseStyle, styles2.bold] }, segment.value);
|
|
1056
|
+
case "italic":
|
|
1057
|
+
return /* @__PURE__ */ React7__default.createElement(Text, { key, style: [baseStyle, styles2.italic] }, segment.value);
|
|
1058
|
+
case "code":
|
|
1059
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
1060
|
+
Text,
|
|
1061
|
+
{
|
|
1062
|
+
key,
|
|
1063
|
+
style: [
|
|
1064
|
+
baseStyle,
|
|
1065
|
+
styles2.code,
|
|
1066
|
+
{
|
|
1067
|
+
backgroundColor: theme.codeBackground ?? (theme.variant === "user" ? "rgba(255,255,255,0.22)" : "rgba(0,0,0,0.06)"),
|
|
1068
|
+
color: theme.codeColor ?? theme.color
|
|
1069
|
+
}
|
|
1070
|
+
]
|
|
1071
|
+
},
|
|
1072
|
+
segment.value
|
|
1073
|
+
);
|
|
1074
|
+
case "link":
|
|
1075
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
1076
|
+
Text,
|
|
1077
|
+
{
|
|
1078
|
+
key,
|
|
1079
|
+
accessibilityRole: "link",
|
|
1080
|
+
style: [
|
|
1081
|
+
baseStyle,
|
|
1082
|
+
styles2.link,
|
|
1083
|
+
{
|
|
1084
|
+
color: theme.linkColor ?? (theme.variant === "user" ? theme.color : "#2563EB")
|
|
1085
|
+
}
|
|
1086
|
+
],
|
|
1087
|
+
onPress: () => {
|
|
1088
|
+
Linking.openURL(segment.href).catch(() => {
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
segment.value
|
|
1093
|
+
);
|
|
1094
|
+
default:
|
|
1095
|
+
return segment.value;
|
|
1096
|
+
}
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
function InlineMarkdown({
|
|
1100
|
+
text,
|
|
1101
|
+
theme
|
|
1102
|
+
}) {
|
|
1103
|
+
const baseStyle = {
|
|
1104
|
+
color: theme.color,
|
|
1105
|
+
fontFamily: theme.fontFamily,
|
|
1106
|
+
fontSize: theme.fontSize,
|
|
1107
|
+
lineHeight: theme.lineHeight
|
|
1108
|
+
};
|
|
1109
|
+
return /* @__PURE__ */ React7__default.createElement(Text, { style: baseStyle }, renderInlineSegments(text, theme, baseStyle));
|
|
1110
|
+
}
|
|
1111
|
+
function ChatMarkdown({ content, theme, contentWidth }) {
|
|
1112
|
+
const prepared = enhancePlainAssistantContent(content);
|
|
1113
|
+
const baseTextStyle = {
|
|
1114
|
+
color: theme.color,
|
|
1115
|
+
fontFamily: theme.fontFamily,
|
|
1116
|
+
fontSize: theme.fontSize,
|
|
1117
|
+
lineHeight: theme.lineHeight
|
|
1118
|
+
};
|
|
1119
|
+
const rootStyle = contentWidth ? [styles2.root, { width: contentWidth }] : styles2.root;
|
|
1120
|
+
if (!looksLikeMarkdown(prepared)) {
|
|
1121
|
+
const paragraphs = prepared.split(/\n\n+/).map((part) => part.trim()).filter(Boolean);
|
|
1122
|
+
if (paragraphs.length > 1) {
|
|
1123
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: rootStyle }, paragraphs.map((paragraph, index) => /* @__PURE__ */ React7__default.createElement(View, { key: `plain-p-${index}`, style: index > 0 ? styles2.blockGap : void 0 }, /* @__PURE__ */ React7__default.createElement(Text, { style: baseTextStyle }, paragraph))));
|
|
1124
|
+
}
|
|
1125
|
+
return /* @__PURE__ */ React7__default.createElement(Text, { style: baseTextStyle }, prepared);
|
|
1126
|
+
}
|
|
1127
|
+
const blocks = parseMarkdownBlocks(prepared);
|
|
1128
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: rootStyle }, blocks.map((block, blockIndex) => {
|
|
1129
|
+
if (block.kind === "paragraph") {
|
|
1130
|
+
return /* @__PURE__ */ React7__default.createElement(View, { key: `p-${blockIndex}`, style: blockIndex > 0 ? styles2.blockGap : void 0 }, /* @__PURE__ */ React7__default.createElement(InlineMarkdown, { text: block.text, theme }));
|
|
1131
|
+
}
|
|
1132
|
+
if (block.kind === "heading") {
|
|
1133
|
+
const scale = block.level === 1 ? 1.25 : block.level === 2 ? 1.15 : 1.05;
|
|
1134
|
+
const headingSize = Math.round((theme.fontSize ?? 15) * scale);
|
|
1135
|
+
return /* @__PURE__ */ React7__default.createElement(View, { key: `h-${blockIndex}`, style: blockIndex > 0 ? styles2.blockGap : void 0 }, /* @__PURE__ */ React7__default.createElement(
|
|
1136
|
+
Text,
|
|
1137
|
+
{
|
|
1138
|
+
accessibilityRole: "header",
|
|
1139
|
+
style: [
|
|
1140
|
+
baseTextStyle,
|
|
1141
|
+
styles2.bold,
|
|
1142
|
+
{ fontSize: headingSize, lineHeight: Math.round(headingSize * 1.35) }
|
|
1143
|
+
]
|
|
1144
|
+
},
|
|
1145
|
+
renderInlineSegments(block.text, theme, {
|
|
1146
|
+
...baseTextStyle,
|
|
1147
|
+
fontSize: headingSize,
|
|
1148
|
+
lineHeight: Math.round(headingSize * 1.35)
|
|
1149
|
+
})
|
|
1150
|
+
));
|
|
1151
|
+
}
|
|
1152
|
+
if (block.kind === "codeblock") {
|
|
1153
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
1154
|
+
View,
|
|
1155
|
+
{
|
|
1156
|
+
key: `code-${blockIndex}`,
|
|
1157
|
+
style: [
|
|
1158
|
+
styles2.codeBlock,
|
|
1159
|
+
blockIndex > 0 ? styles2.blockGap : void 0,
|
|
1160
|
+
{
|
|
1161
|
+
backgroundColor: theme.codeBackground ?? (theme.variant === "user" ? "rgba(255,255,255,0.18)" : "rgba(0,0,0,0.055)")
|
|
1162
|
+
}
|
|
1163
|
+
]
|
|
1164
|
+
},
|
|
1165
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1166
|
+
Text,
|
|
1167
|
+
{
|
|
1168
|
+
style: [
|
|
1169
|
+
styles2.code,
|
|
1170
|
+
{
|
|
1171
|
+
color: theme.codeColor ?? theme.color,
|
|
1172
|
+
fontSize: (theme.fontSize ?? 15) - 1,
|
|
1173
|
+
lineHeight: Math.round(((theme.fontSize ?? 15) - 1) * 1.45)
|
|
1174
|
+
}
|
|
1175
|
+
]
|
|
1176
|
+
},
|
|
1177
|
+
block.code
|
|
1178
|
+
)
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
if (block.kind === "bullet") {
|
|
1182
|
+
return /* @__PURE__ */ React7__default.createElement(View, { key: `ul-${blockIndex}`, style: blockIndex > 0 ? styles2.blockGap : void 0 }, block.items.map((item, itemIndex) => /* @__PURE__ */ React7__default.createElement(
|
|
1183
|
+
Text,
|
|
1184
|
+
{
|
|
1185
|
+
key: `ul-${blockIndex}-${itemIndex}`,
|
|
1186
|
+
style: [baseTextStyle, itemIndex > 0 ? styles2.listItemGap : void 0]
|
|
1187
|
+
},
|
|
1188
|
+
"\u2022 ",
|
|
1189
|
+
renderInlineSegments(item, theme, baseTextStyle)
|
|
1190
|
+
)));
|
|
1191
|
+
}
|
|
1192
|
+
return /* @__PURE__ */ React7__default.createElement(View, { key: `ol-${blockIndex}`, style: blockIndex > 0 ? styles2.blockGap : void 0 }, block.items.map((item, itemIndex) => /* @__PURE__ */ React7__default.createElement(
|
|
1193
|
+
Text,
|
|
1194
|
+
{
|
|
1195
|
+
key: `ol-${blockIndex}-${itemIndex}`,
|
|
1196
|
+
style: [baseTextStyle, itemIndex > 0 ? styles2.listItemGap : void 0]
|
|
1197
|
+
},
|
|
1198
|
+
`${itemIndex + 1}. `,
|
|
1199
|
+
renderInlineSegments(item, theme, baseTextStyle)
|
|
1200
|
+
)));
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
var styles2 = StyleSheet.create({
|
|
1204
|
+
root: {
|
|
1205
|
+
flexShrink: 1,
|
|
1206
|
+
alignSelf: "stretch"
|
|
1207
|
+
},
|
|
1208
|
+
blockGap: {
|
|
1209
|
+
marginTop: 8
|
|
1210
|
+
},
|
|
1211
|
+
listItemGap: {
|
|
1212
|
+
marginTop: 4
|
|
1213
|
+
},
|
|
1214
|
+
bold: {
|
|
1215
|
+
fontWeight: "700"
|
|
1216
|
+
},
|
|
1217
|
+
italic: {
|
|
1218
|
+
fontStyle: "italic"
|
|
1219
|
+
},
|
|
1220
|
+
code: {
|
|
1221
|
+
fontFamily: "Menlo",
|
|
1222
|
+
borderRadius: 4,
|
|
1223
|
+
paddingHorizontal: 4,
|
|
1224
|
+
paddingVertical: 1
|
|
1225
|
+
},
|
|
1226
|
+
codeBlock: {
|
|
1227
|
+
borderRadius: 8,
|
|
1228
|
+
paddingHorizontal: 10,
|
|
1229
|
+
paddingVertical: 8,
|
|
1230
|
+
alignSelf: "stretch"
|
|
1231
|
+
},
|
|
1232
|
+
link: {
|
|
1233
|
+
textDecorationLine: "underline"
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
var ThemeContext = createContext(defaultLightTheme);
|
|
1237
|
+
function AppilotsThemeProvider({
|
|
1238
|
+
theme,
|
|
1239
|
+
mode = "auto",
|
|
1240
|
+
children
|
|
1241
|
+
}) {
|
|
1242
|
+
const systemScheme = useColorScheme();
|
|
1243
|
+
const tokens = useMemo(
|
|
1244
|
+
() => mergeThemeTokens(resolveBaseTheme(mode, systemScheme), theme ?? void 0),
|
|
1245
|
+
[mode, systemScheme, theme]
|
|
1246
|
+
);
|
|
1247
|
+
return /* @__PURE__ */ React7__default.createElement(ThemeContext.Provider, { value: tokens }, children);
|
|
1248
|
+
}
|
|
1249
|
+
function useAppilotsTheme() {
|
|
1250
|
+
return useContext(ThemeContext);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/theme/tailwindBridge.ts
|
|
1254
|
+
var PREFERRED_SHADES = ["500", "600", "400", "700", "300", "800", "200", "900"];
|
|
1255
|
+
function pickColor(value) {
|
|
1256
|
+
if (!value) return void 0;
|
|
1257
|
+
if (typeof value === "string") return value;
|
|
1258
|
+
for (const shade of PREFERRED_SHADES) {
|
|
1259
|
+
if (typeof value[shade] === "string") return value[shade];
|
|
1260
|
+
}
|
|
1261
|
+
for (const v of Object.values(value)) {
|
|
1262
|
+
if (typeof v === "string") return v;
|
|
1263
|
+
}
|
|
1264
|
+
return void 0;
|
|
1265
|
+
}
|
|
1266
|
+
function parseSize(value) {
|
|
1267
|
+
if (typeof value === "number") return value;
|
|
1268
|
+
if (Array.isArray(value)) return parseSize(value[0]);
|
|
1269
|
+
if (typeof value !== "string") return void 0;
|
|
1270
|
+
const trimmed = value.trim();
|
|
1271
|
+
if (trimmed.endsWith("rem")) {
|
|
1272
|
+
const n2 = parseFloat(trimmed);
|
|
1273
|
+
if (!Number.isNaN(n2)) return Math.round(n2 * 16);
|
|
1274
|
+
}
|
|
1275
|
+
if (trimmed.endsWith("px")) {
|
|
1276
|
+
const n2 = parseFloat(trimmed);
|
|
1277
|
+
if (!Number.isNaN(n2)) return Math.round(n2);
|
|
1278
|
+
}
|
|
1279
|
+
const n = parseFloat(trimmed);
|
|
1280
|
+
return Number.isNaN(n) ? void 0 : Math.round(n);
|
|
1281
|
+
}
|
|
1282
|
+
function pickFontFamily(value) {
|
|
1283
|
+
if (!value) return void 0;
|
|
1284
|
+
if (typeof value === "string") return value;
|
|
1285
|
+
return value[0];
|
|
1286
|
+
}
|
|
1287
|
+
function appilotsThemeFromTailwind(config) {
|
|
1288
|
+
const colors = config.theme?.colors ?? {};
|
|
1289
|
+
const out = {};
|
|
1290
|
+
const c = {};
|
|
1291
|
+
const primary = pickColor(colors.primary);
|
|
1292
|
+
if (primary) c.primary = primary;
|
|
1293
|
+
const secondary = pickColor(colors.secondary ?? colors.accent);
|
|
1294
|
+
if (secondary) c.secondary = secondary;
|
|
1295
|
+
const bg = pickColor(colors.background ?? colors.white);
|
|
1296
|
+
if (bg) c.background = bg;
|
|
1297
|
+
const surface = pickColor(colors.surface ?? colors.gray ?? colors.slate);
|
|
1298
|
+
if (surface) c.surface = surface;
|
|
1299
|
+
const textColor = pickColor(colors.text ?? colors.foreground ?? colors.black);
|
|
1300
|
+
if (textColor) c.text = textColor;
|
|
1301
|
+
const muted = pickColor(colors.muted ?? colors.gray);
|
|
1302
|
+
if (muted && muted !== c.text) c.textSecondary = muted;
|
|
1303
|
+
const border = pickColor(colors.border ?? colors.gray);
|
|
1304
|
+
if (border) c.border = border;
|
|
1305
|
+
const success = pickColor(colors.success ?? colors.green ?? colors.emerald);
|
|
1306
|
+
if (success) c.success = success;
|
|
1307
|
+
const warning = pickColor(colors.warning ?? colors.yellow ?? colors.amber);
|
|
1308
|
+
if (warning) c.warning = warning;
|
|
1309
|
+
const error = pickColor(colors.error ?? colors.danger ?? colors.red);
|
|
1310
|
+
if (error) c.error = error;
|
|
1311
|
+
if (Object.keys(c).length > 0) out.colors = c;
|
|
1312
|
+
const fontFamily = pickFontFamily(
|
|
1313
|
+
config.theme?.fontFamily?.sans ?? config.theme?.fontFamily?.body
|
|
1314
|
+
);
|
|
1315
|
+
const sizeXs = parseSize(config.theme?.fontSize?.xs);
|
|
1316
|
+
const sizeSm = parseSize(config.theme?.fontSize?.sm);
|
|
1317
|
+
const sizeBase = parseSize(config.theme?.fontSize?.base);
|
|
1318
|
+
const sizeLg = parseSize(config.theme?.fontSize?.lg);
|
|
1319
|
+
const typography = {};
|
|
1320
|
+
if (fontFamily) typography.fontFamily = fontFamily;
|
|
1321
|
+
if (sizeXs) typography.fontSizeXs = sizeXs;
|
|
1322
|
+
if (sizeSm) typography.fontSizeSm = sizeSm;
|
|
1323
|
+
if (sizeBase) typography.fontSizeBase = sizeBase;
|
|
1324
|
+
if (sizeLg) typography.fontSizeLg = sizeLg;
|
|
1325
|
+
if (Object.keys(typography).length > 0) out.typography = typography;
|
|
1326
|
+
const radiiSm = parseSize(config.theme?.borderRadius?.sm);
|
|
1327
|
+
const radiiMd = parseSize(config.theme?.borderRadius?.md);
|
|
1328
|
+
const radiiLg = parseSize(config.theme?.borderRadius?.lg);
|
|
1329
|
+
const radii = {};
|
|
1330
|
+
if (radiiSm) radii.sm = radiiSm;
|
|
1331
|
+
if (radiiMd) radii.md = radiiMd;
|
|
1332
|
+
if (radiiLg) radii.lg = radiiLg;
|
|
1333
|
+
if (Object.keys(radii).length > 0) out.radii = radii;
|
|
1334
|
+
return out;
|
|
1335
|
+
}
|
|
1336
|
+
var appilotsTheme = {
|
|
1337
|
+
fromTailwind: appilotsThemeFromTailwind
|
|
1338
|
+
};
|
|
1339
|
+
|
|
1340
|
+
// src/i18n/bundles/pt-BR.ts
|
|
1341
|
+
var ptBR = {
|
|
1342
|
+
chatTitle: "Assistente Appilots",
|
|
1343
|
+
clearButton: "Limpar",
|
|
1344
|
+
inputPlaceholder: "Digite uma mensagem...",
|
|
1345
|
+
emptyTitle: "Como posso ajudar?",
|
|
1346
|
+
emptySubtitle: "Navego, preencho formul\xE1rios e executo a\xE7\xF5es neste app.",
|
|
1347
|
+
thinking: "Pensando...",
|
|
1348
|
+
statusAnalyzing: "Analisando a tela...",
|
|
1349
|
+
statusWaitingApp: "Aguardando o app responder...",
|
|
1350
|
+
statusAdjusting: "Ajustando o plano...",
|
|
1351
|
+
poweredBy: "Powered by Appilots",
|
|
1352
|
+
approve: "Aprovar",
|
|
1353
|
+
reject: "Rejeitar",
|
|
1354
|
+
cancelledByUser: "Cancelado pelo usu\xE1rio",
|
|
1355
|
+
unknownAction: "A\xE7\xE3o desconhecida",
|
|
1356
|
+
somethingWentWrong: "Algo deu errado",
|
|
1357
|
+
openChatA11y: "Abrir chat do assistente",
|
|
1358
|
+
closeChatA11y: "Fechar chat do assistente",
|
|
1359
|
+
stopGeneratingA11y: "Parar gera\xE7\xE3o",
|
|
1360
|
+
talkToHumanA11y: "Falar com um atendente humano",
|
|
1361
|
+
humanAgentLabel: "Atendente",
|
|
1362
|
+
escalationRequested: "Certo! Chamei um atendente humano. Voc\xEA pode continuar usando o app normalmente enquanto isso.",
|
|
1363
|
+
escalationConnected: "Um atendente entrou na conversa.",
|
|
1364
|
+
escalationResolved: "O atendimento humano foi encerrado. Voltei a te ajudar por aqui.",
|
|
1365
|
+
escalationOffer: "Se preferir, posso chamar uma pessoa para ajudar.",
|
|
1366
|
+
escalationOfferChip: "Falar com um atendente",
|
|
1367
|
+
escalationPendingBanner: "Aguardando um atendente...",
|
|
1368
|
+
escalationActiveBanner: "Voc\xEA est\xE1 falando com um atendente"
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
// src/i18n/bundles/en.ts
|
|
1372
|
+
var en = {
|
|
1373
|
+
chatTitle: "Appilots Assistant",
|
|
1374
|
+
clearButton: "Clear",
|
|
1375
|
+
inputPlaceholder: "Type a message...",
|
|
1376
|
+
emptyTitle: "How can I help?",
|
|
1377
|
+
emptySubtitle: "I navigate, fill forms, and run actions in this app.",
|
|
1378
|
+
thinking: "Thinking...",
|
|
1379
|
+
statusAnalyzing: "Reading the screen...",
|
|
1380
|
+
statusWaitingApp: "Waiting for the app...",
|
|
1381
|
+
statusAdjusting: "Adjusting the plan...",
|
|
1382
|
+
poweredBy: "Powered by Appilots",
|
|
1383
|
+
approve: "Approve",
|
|
1384
|
+
reject: "Reject",
|
|
1385
|
+
cancelledByUser: "Cancelled by user",
|
|
1386
|
+
unknownAction: "Unknown action",
|
|
1387
|
+
somethingWentWrong: "Something went wrong",
|
|
1388
|
+
openChatA11y: "Open assistant chat",
|
|
1389
|
+
closeChatA11y: "Close assistant chat",
|
|
1390
|
+
stopGeneratingA11y: "Stop generating",
|
|
1391
|
+
talkToHumanA11y: "Talk to a human agent",
|
|
1392
|
+
humanAgentLabel: "Agent",
|
|
1393
|
+
escalationRequested: "Done! I've called a human agent. You can keep using the app normally in the meantime.",
|
|
1394
|
+
escalationConnected: "A human agent joined the conversation.",
|
|
1395
|
+
escalationResolved: "The human handoff has ended. I'm back to help you here.",
|
|
1396
|
+
escalationOffer: "If you prefer, I can bring in a person to help.",
|
|
1397
|
+
escalationOfferChip: "Talk to a human",
|
|
1398
|
+
escalationPendingBanner: "Waiting for a human agent...",
|
|
1399
|
+
escalationActiveBanner: "You're talking to a human agent"
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
// src/i18n/bundles/es.ts
|
|
1403
|
+
var es = {
|
|
1404
|
+
chatTitle: "Asistente Appilots",
|
|
1405
|
+
clearButton: "Limpiar",
|
|
1406
|
+
inputPlaceholder: "Escribe un mensaje...",
|
|
1407
|
+
emptyTitle: "\xBFEn qu\xE9 puedo ayudarte?",
|
|
1408
|
+
emptySubtitle: "Navego, relleno formularios y ejecuto acciones en esta app.",
|
|
1409
|
+
thinking: "Pensando...",
|
|
1410
|
+
statusAnalyzing: "Analizando la pantalla...",
|
|
1411
|
+
statusWaitingApp: "Esperando a la app...",
|
|
1412
|
+
statusAdjusting: "Ajustando el plan...",
|
|
1413
|
+
poweredBy: "Hecho con Appilots",
|
|
1414
|
+
approve: "Aprobar",
|
|
1415
|
+
reject: "Rechazar",
|
|
1416
|
+
cancelledByUser: "Cancelado por el usuario",
|
|
1417
|
+
unknownAction: "Acci\xF3n desconocida",
|
|
1418
|
+
somethingWentWrong: "Algo sali\xF3 mal",
|
|
1419
|
+
openChatA11y: "Abrir chat del asistente",
|
|
1420
|
+
closeChatA11y: "Cerrar chat del asistente",
|
|
1421
|
+
stopGeneratingA11y: "Detener generaci\xF3n",
|
|
1422
|
+
talkToHumanA11y: "Hablar con un agente humano",
|
|
1423
|
+
humanAgentLabel: "Agente",
|
|
1424
|
+
escalationRequested: "\xA1Listo! Llam\xE9 a un agente humano. Mientras tanto puedes seguir usando la app con normalidad.",
|
|
1425
|
+
escalationConnected: "Un agente humano se uni\xF3 a la conversaci\xF3n.",
|
|
1426
|
+
escalationResolved: "La atenci\xF3n humana termin\xF3. Vuelvo a ayudarte por aqu\xED.",
|
|
1427
|
+
escalationOffer: "Si prefieres, puedo llamar a una persona para ayudarte.",
|
|
1428
|
+
escalationOfferChip: "Hablar con un agente",
|
|
1429
|
+
escalationPendingBanner: "Esperando a un agente...",
|
|
1430
|
+
escalationActiveBanner: "Est\xE1s hablando con un agente humano"
|
|
1431
|
+
};
|
|
1432
|
+
var BUNDLES = {
|
|
1433
|
+
"pt-BR": ptBR,
|
|
1434
|
+
en,
|
|
1435
|
+
es
|
|
1436
|
+
};
|
|
1437
|
+
var DEFAULT_LOCALE = "pt-BR";
|
|
1438
|
+
function detectDeviceLocale() {
|
|
1439
|
+
try {
|
|
1440
|
+
let raw;
|
|
1441
|
+
if (Platform.OS === "ios") {
|
|
1442
|
+
const settings = NativeModules?.SettingsManager?.settings;
|
|
1443
|
+
raw = settings?.AppleLocale || (Array.isArray(settings?.AppleLanguages) ? settings?.AppleLanguages?.[0] : void 0);
|
|
1444
|
+
} else if (Platform.OS === "android") {
|
|
1445
|
+
raw = NativeModules?.I18nManager?.localeIdentifier;
|
|
1446
|
+
}
|
|
1447
|
+
if (!raw || typeof raw !== "string") return null;
|
|
1448
|
+
const lower = raw.toLowerCase().replace("_", "-");
|
|
1449
|
+
if (lower.startsWith("pt")) return "pt-BR";
|
|
1450
|
+
if (lower.startsWith("es")) return "es";
|
|
1451
|
+
if (lower.startsWith("en")) return "en";
|
|
1452
|
+
return null;
|
|
1453
|
+
} catch {
|
|
1454
|
+
return null;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
function resolveLocale(prop) {
|
|
1458
|
+
if (prop && BUNDLES[prop]) return prop;
|
|
1459
|
+
const detected = detectDeviceLocale();
|
|
1460
|
+
if (detected && BUNDLES[detected]) return detected;
|
|
1461
|
+
return DEFAULT_LOCALE;
|
|
1462
|
+
}
|
|
1463
|
+
var I18nContext = createContext(null);
|
|
1464
|
+
function AppilotsI18nProvider({
|
|
1465
|
+
locale,
|
|
1466
|
+
overrides,
|
|
1467
|
+
children
|
|
1468
|
+
}) {
|
|
1469
|
+
const value = useMemo(() => {
|
|
1470
|
+
const resolved = resolveLocale(locale ?? null);
|
|
1471
|
+
const merged = overrides ? { ...BUNDLES[resolved], ...overrides } : BUNDLES[resolved];
|
|
1472
|
+
return {
|
|
1473
|
+
locale: resolved,
|
|
1474
|
+
strings: merged,
|
|
1475
|
+
t: (key) => merged[key]
|
|
1476
|
+
};
|
|
1477
|
+
}, [locale, overrides]);
|
|
1478
|
+
return /* @__PURE__ */ React7__default.createElement(I18nContext.Provider, { value }, children);
|
|
1479
|
+
}
|
|
1480
|
+
function useAppilotsI18n() {
|
|
1481
|
+
const ctx = useContext(I18nContext);
|
|
1482
|
+
if (ctx) return ctx;
|
|
1483
|
+
return {
|
|
1484
|
+
locale: DEFAULT_LOCALE,
|
|
1485
|
+
strings: BUNDLES[DEFAULT_LOCALE],
|
|
1486
|
+
t: (key) => BUNDLES[DEFAULT_LOCALE][key]
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/components/AppilotsChat.tsx
|
|
1491
|
+
function translateLegacyTheme(legacy) {
|
|
1492
|
+
const out = {};
|
|
1493
|
+
if (legacy.primaryColor || legacy.backgroundColor || legacy.textColor || legacy.inputBackgroundColor) {
|
|
1494
|
+
out.colors = {};
|
|
1495
|
+
if (legacy.primaryColor) out.colors.primary = legacy.primaryColor;
|
|
1496
|
+
if (legacy.backgroundColor) out.colors.background = legacy.backgroundColor;
|
|
1497
|
+
if (legacy.textColor) out.colors.text = legacy.textColor;
|
|
1498
|
+
if (legacy.inputBackgroundColor) out.colors.surface = legacy.inputBackgroundColor;
|
|
1499
|
+
}
|
|
1500
|
+
if (legacy.borderRadius !== void 0) {
|
|
1501
|
+
out.radii = { lg: legacy.borderRadius, md: legacy.borderRadius };
|
|
1502
|
+
}
|
|
1503
|
+
if (legacy.fontFamily) {
|
|
1504
|
+
out.typography = { fontFamily: legacy.fontFamily };
|
|
1505
|
+
}
|
|
1506
|
+
return out;
|
|
1507
|
+
}
|
|
1508
|
+
function isLegacyTheme(theme) {
|
|
1509
|
+
if (!theme || typeof theme !== "object") return false;
|
|
1510
|
+
const t = theme;
|
|
1511
|
+
return "primaryColor" in t || "backgroundColor" in t || "textColor" in t || "inputBackgroundColor" in t || typeof t.borderRadius === "number" || typeof t.fontFamily === "string" && !("typography" in t);
|
|
1512
|
+
}
|
|
1513
|
+
function isImageSource(value) {
|
|
1514
|
+
return typeof value === "number" || /^https?:\/\//i.test(value.trim());
|
|
1515
|
+
}
|
|
1516
|
+
var { height: SCREEN_HEIGHT, width: SCREEN_WIDTH } = Dimensions.get("window");
|
|
1517
|
+
var CHAT_HORIZONTAL_PADDING = 16;
|
|
1518
|
+
var BUBBLE_HORIZONTAL_PADDING = 14;
|
|
1519
|
+
var ASSISTANT_CONTENT_WIDTH = SCREEN_WIDTH - CHAT_HORIZONTAL_PADDING * 2 - BUBBLE_HORIZONTAL_PADDING * 2;
|
|
1520
|
+
var APPILOTS_SKIP_PROPS = {
|
|
1521
|
+
__appilotsSkip: true,
|
|
1522
|
+
__appilotsInternal: true
|
|
1523
|
+
};
|
|
1524
|
+
function AppilotsChat(props) {
|
|
1525
|
+
const { remotePersonalization } = useAppilotsContext();
|
|
1526
|
+
const themeProp = useMemo(() => {
|
|
1527
|
+
if (!props.theme) return void 0;
|
|
1528
|
+
return isLegacyTheme(props.theme) ? translateLegacyTheme(props.theme) : props.theme;
|
|
1529
|
+
}, [props.theme]);
|
|
1530
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
1531
|
+
AppilotsThemeProvider,
|
|
1532
|
+
{
|
|
1533
|
+
theme: resolveTheme(themeProp, remotePersonalization),
|
|
1534
|
+
mode: resolveThemeMode(props.themeMode, remotePersonalization)
|
|
1535
|
+
},
|
|
1536
|
+
/* @__PURE__ */ React7__default.createElement(AppilotsI18nProvider, { locale: resolveLocaleProp(props.locale, remotePersonalization) }, /* @__PURE__ */ React7__default.createElement(AppilotsChatInner, { ...props }))
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
function AppilotsChatInner({
|
|
1540
|
+
visible: controlledVisible,
|
|
1541
|
+
onClose,
|
|
1542
|
+
placeholder,
|
|
1543
|
+
headerTitle: headerTitleProp,
|
|
1544
|
+
autoExecute = true,
|
|
1545
|
+
assistantName: assistantNameProp,
|
|
1546
|
+
assistantAvatar: assistantAvatarProp,
|
|
1547
|
+
emptyStateIcon: emptyStateIconProp,
|
|
1548
|
+
welcomeMessage: welcomeMessageProp,
|
|
1549
|
+
poweredByVisible: poweredByVisibleProp,
|
|
1550
|
+
mode = "bubble",
|
|
1551
|
+
position = "bottom-right",
|
|
1552
|
+
triggerButton = "default",
|
|
1553
|
+
triggerIcon,
|
|
1554
|
+
triggerButtonColor: triggerButtonColorProp,
|
|
1555
|
+
triggerButtonImage: triggerButtonImageProp
|
|
1556
|
+
}) {
|
|
1557
|
+
const theme = useAppilotsTheme();
|
|
1558
|
+
const { strings, t } = useAppilotsI18n();
|
|
1559
|
+
const {
|
|
1560
|
+
messages,
|
|
1561
|
+
isLoading,
|
|
1562
|
+
loadingStatusKey,
|
|
1563
|
+
sendMessage,
|
|
1564
|
+
cancelMessage,
|
|
1565
|
+
clearMessages,
|
|
1566
|
+
escalation,
|
|
1567
|
+
requestHuman
|
|
1568
|
+
} = useAppilotsChat({
|
|
1569
|
+
errorPrefix: t("somethingWentWrong"),
|
|
1570
|
+
escalationStrings: {
|
|
1571
|
+
requested: t("escalationRequested"),
|
|
1572
|
+
connected: t("escalationConnected"),
|
|
1573
|
+
resolved: t("escalationResolved"),
|
|
1574
|
+
offer: t("escalationOffer")
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
const {
|
|
1578
|
+
pendingActions,
|
|
1579
|
+
executingActions,
|
|
1580
|
+
completedActions,
|
|
1581
|
+
approveAction,
|
|
1582
|
+
rejectAction
|
|
1583
|
+
} = useAppilotsActions({ autoExecute });
|
|
1584
|
+
const { emit, remotePersonalization } = useAppilotsContext();
|
|
1585
|
+
const {
|
|
1586
|
+
headerTitle: resolvedTitle,
|
|
1587
|
+
assistantAvatar,
|
|
1588
|
+
emptyStateIcon,
|
|
1589
|
+
welcomeMessage,
|
|
1590
|
+
poweredByVisible,
|
|
1591
|
+
triggerButtonColor,
|
|
1592
|
+
triggerButtonImage
|
|
1593
|
+
} = resolvePersonalization(
|
|
1594
|
+
{
|
|
1595
|
+
headerTitle: headerTitleProp,
|
|
1596
|
+
assistantName: assistantNameProp,
|
|
1597
|
+
assistantAvatar: assistantAvatarProp,
|
|
1598
|
+
emptyStateIcon: emptyStateIconProp,
|
|
1599
|
+
welcomeMessage: welcomeMessageProp,
|
|
1600
|
+
poweredByVisible: poweredByVisibleProp,
|
|
1601
|
+
triggerButtonColor: triggerButtonColorProp,
|
|
1602
|
+
triggerButtonImage: triggerButtonImageProp
|
|
1603
|
+
},
|
|
1604
|
+
remotePersonalization
|
|
1605
|
+
);
|
|
1606
|
+
const allActions = [...pendingActions, ...executingActions, ...completedActions];
|
|
1607
|
+
const timeline = buildChatTimeline(messages, allActions);
|
|
1608
|
+
const lastAssistantMessageId = React7__default.useMemo(() => {
|
|
1609
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1610
|
+
if (messages[i].role === "assistant") return messages[i].id;
|
|
1611
|
+
}
|
|
1612
|
+
return void 0;
|
|
1613
|
+
}, [messages]);
|
|
1614
|
+
const insets = useSafeAreaInsets();
|
|
1615
|
+
const [internalVisible, setInternalVisible] = useState(mode === "inline");
|
|
1616
|
+
const [inputText, setInputText] = useState("");
|
|
1617
|
+
const flatListRef = useRef(null);
|
|
1618
|
+
const slideAnim = useRef(new Animated.Value(SCREEN_HEIGHT)).current;
|
|
1619
|
+
const isVisible = mode === "inline" ? true : controlledVisible !== void 0 ? controlledVisible : internalVisible;
|
|
1620
|
+
const showSuggestedPrompts = shouldShowSuggestedPrompts({
|
|
1621
|
+
isVisible,
|
|
1622
|
+
messageCount: messages.length,
|
|
1623
|
+
inputText
|
|
1624
|
+
});
|
|
1625
|
+
const { prompts: suggestedPrompts } = useSuggestedPrompts({
|
|
1626
|
+
enabled: showSuggestedPrompts,
|
|
1627
|
+
limit: 3
|
|
1628
|
+
});
|
|
1629
|
+
const openChat = useCallback(() => {
|
|
1630
|
+
setInternalVisible(true);
|
|
1631
|
+
emit({ type: "chat:open", timestamp: Date.now(), data: {} });
|
|
1632
|
+
}, [emit]);
|
|
1633
|
+
const closeChat = useCallback(() => {
|
|
1634
|
+
setInternalVisible(false);
|
|
1635
|
+
onClose?.();
|
|
1636
|
+
emit({ type: "chat:close", timestamp: Date.now(), data: {} });
|
|
1637
|
+
}, [onClose, emit]);
|
|
1638
|
+
useEffect(() => {
|
|
1639
|
+
if (mode === "inline") return;
|
|
1640
|
+
Animated.spring(slideAnim, {
|
|
1641
|
+
toValue: isVisible ? 0 : SCREEN_HEIGHT,
|
|
1642
|
+
useNativeDriver: true,
|
|
1643
|
+
tension: 65,
|
|
1644
|
+
friction: 11
|
|
1645
|
+
}).start();
|
|
1646
|
+
}, [isVisible, slideAnim, mode]);
|
|
1647
|
+
useEffect(() => {
|
|
1648
|
+
if (messages.length > 0) {
|
|
1649
|
+
setTimeout(() => flatListRef.current?.scrollToEnd({ animated: true }), 100);
|
|
1650
|
+
}
|
|
1651
|
+
}, [messages.length]);
|
|
1652
|
+
const handleSend = useCallback(() => {
|
|
1653
|
+
if (!inputText.trim()) return;
|
|
1654
|
+
sendMessage(inputText.trim());
|
|
1655
|
+
setInputText("");
|
|
1656
|
+
}, [inputText, sendMessage]);
|
|
1657
|
+
const renderMessage = useCallback(
|
|
1658
|
+
({ item }) => {
|
|
1659
|
+
if (item.role === "system") {
|
|
1660
|
+
const isOffer = item.metadata?.escalationOffer === true;
|
|
1661
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: styles3.systemMessageContainer }, /* @__PURE__ */ React7__default.createElement(
|
|
1662
|
+
Text,
|
|
1663
|
+
{
|
|
1664
|
+
style: [
|
|
1665
|
+
styles3.systemMessageText,
|
|
1666
|
+
{
|
|
1667
|
+
color: theme.colors.textSecondary,
|
|
1668
|
+
fontFamily: theme.typography.fontFamily,
|
|
1669
|
+
fontSize: theme.typography.fontSizeSm
|
|
1670
|
+
}
|
|
1671
|
+
]
|
|
1672
|
+
},
|
|
1673
|
+
item.content
|
|
1674
|
+
), isOffer && !escalation && /* @__PURE__ */ React7__default.createElement(
|
|
1675
|
+
TouchableOpacity,
|
|
1676
|
+
{
|
|
1677
|
+
...APPILOTS_SKIP_PROPS,
|
|
1678
|
+
style: [
|
|
1679
|
+
styles3.chip,
|
|
1680
|
+
styles3.escalationOfferChip,
|
|
1681
|
+
{
|
|
1682
|
+
backgroundColor: theme.colors.surface,
|
|
1683
|
+
borderColor: theme.colors.border,
|
|
1684
|
+
borderRadius: 999
|
|
1685
|
+
}
|
|
1686
|
+
],
|
|
1687
|
+
onPress: () => requestHuman("agent_gave_up"),
|
|
1688
|
+
activeOpacity: 0.7,
|
|
1689
|
+
accessibilityRole: "button",
|
|
1690
|
+
accessibilityLabel: strings.talkToHumanA11y,
|
|
1691
|
+
testID: "escalation-offer-chip"
|
|
1692
|
+
},
|
|
1693
|
+
/* @__PURE__ */ React7__default.createElement(HeadsetIcon, { size: 14, color: theme.colors.textSecondary }),
|
|
1694
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1695
|
+
Text,
|
|
1696
|
+
{
|
|
1697
|
+
style: [
|
|
1698
|
+
styles3.chipText,
|
|
1699
|
+
{
|
|
1700
|
+
color: theme.colors.text,
|
|
1701
|
+
fontFamily: theme.typography.fontFamily,
|
|
1702
|
+
fontSize: theme.typography.fontSizeSm
|
|
1703
|
+
}
|
|
1704
|
+
]
|
|
1705
|
+
},
|
|
1706
|
+
strings.escalationOfferChip
|
|
1707
|
+
)
|
|
1708
|
+
));
|
|
1709
|
+
}
|
|
1710
|
+
const isUser = item.role === "user";
|
|
1711
|
+
const isHumanAgent = item.role === "human_agent";
|
|
1712
|
+
const useWideAssistantLayout = !isUser && needsWideChatLayout(item.content);
|
|
1713
|
+
if (isHumanAgent) {
|
|
1714
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: styles3.humanAgentContainer, testID: "human-agent-message" }, /* @__PURE__ */ React7__default.createElement(View, { style: styles3.humanAgentLabelRow }, /* @__PURE__ */ React7__default.createElement(HeadsetIcon, { size: 11, color: theme.colors.secondary }), /* @__PURE__ */ React7__default.createElement(
|
|
1715
|
+
Text,
|
|
1716
|
+
{
|
|
1717
|
+
style: [
|
|
1718
|
+
styles3.humanAgentLabel,
|
|
1719
|
+
{
|
|
1720
|
+
color: theme.colors.textSecondary,
|
|
1721
|
+
fontFamily: theme.typography.fontFamily
|
|
1722
|
+
}
|
|
1723
|
+
]
|
|
1724
|
+
},
|
|
1725
|
+
escalation?.operatorName ?? strings.humanAgentLabel
|
|
1726
|
+
)), /* @__PURE__ */ React7__default.createElement(
|
|
1727
|
+
View,
|
|
1728
|
+
{
|
|
1729
|
+
accessibilityLabel: item.content,
|
|
1730
|
+
style: [
|
|
1731
|
+
styles3.messageBubble,
|
|
1732
|
+
styles3.assistantBubble,
|
|
1733
|
+
{ backgroundColor: theme.colors.surface }
|
|
1734
|
+
]
|
|
1735
|
+
},
|
|
1736
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1737
|
+
ChatMarkdown,
|
|
1738
|
+
{
|
|
1739
|
+
content: item.content,
|
|
1740
|
+
theme: {
|
|
1741
|
+
color: theme.colors.text,
|
|
1742
|
+
fontFamily: theme.typography.fontFamily,
|
|
1743
|
+
fontSize: theme.typography.fontSizeBase,
|
|
1744
|
+
lineHeight: 20,
|
|
1745
|
+
variant: "assistant"
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
)
|
|
1749
|
+
));
|
|
1750
|
+
}
|
|
1751
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
1752
|
+
View,
|
|
1753
|
+
{
|
|
1754
|
+
testID: isUser ? void 0 : item.id === lastAssistantMessageId ? "assistant-last-message" : "assistant-message",
|
|
1755
|
+
accessibilityLabel: isUser ? void 0 : item.content,
|
|
1756
|
+
style: [
|
|
1757
|
+
styles3.messageBubble,
|
|
1758
|
+
isUser ? [styles3.userBubble, { backgroundColor: theme.colors.primary }] : [
|
|
1759
|
+
styles3.assistantBubble,
|
|
1760
|
+
useWideAssistantLayout && styles3.assistantBubbleWide,
|
|
1761
|
+
{ backgroundColor: theme.colors.surface }
|
|
1762
|
+
]
|
|
1763
|
+
]
|
|
1764
|
+
},
|
|
1765
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1766
|
+
ChatMarkdown,
|
|
1767
|
+
{
|
|
1768
|
+
content: item.content,
|
|
1769
|
+
contentWidth: useWideAssistantLayout ? ASSISTANT_CONTENT_WIDTH : void 0,
|
|
1770
|
+
theme: {
|
|
1771
|
+
color: isUser ? theme.colors.userBubbleText : theme.colors.bubbleText,
|
|
1772
|
+
fontFamily: theme.typography.fontFamily,
|
|
1773
|
+
fontSize: theme.typography.fontSizeBase,
|
|
1774
|
+
lineHeight: 20,
|
|
1775
|
+
variant: isUser ? "user" : "assistant"
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
)
|
|
1779
|
+
);
|
|
1780
|
+
},
|
|
1781
|
+
[theme, lastAssistantMessageId, escalation, requestHuman, strings]
|
|
1782
|
+
);
|
|
1783
|
+
const requireApproval = useCallback(
|
|
1784
|
+
(action) => {
|
|
1785
|
+
if (action.status !== "pending") return false;
|
|
1786
|
+
if (action.type === "confirm") return true;
|
|
1787
|
+
return !autoExecute;
|
|
1788
|
+
},
|
|
1789
|
+
[autoExecute]
|
|
1790
|
+
);
|
|
1791
|
+
const renderBreadcrumb = useCallback(
|
|
1792
|
+
(actions) => /* @__PURE__ */ React7__default.createElement(
|
|
1793
|
+
ActionBreadcrumb,
|
|
1794
|
+
{
|
|
1795
|
+
actions,
|
|
1796
|
+
primaryColor: theme.colors.primary,
|
|
1797
|
+
textColor: theme.colors.text,
|
|
1798
|
+
fontFamily: theme.typography.fontFamily,
|
|
1799
|
+
palette: {
|
|
1800
|
+
textSecondary: theme.colors.textSecondary,
|
|
1801
|
+
surface: theme.colors.surface,
|
|
1802
|
+
border: theme.colors.border,
|
|
1803
|
+
success: theme.colors.success,
|
|
1804
|
+
warning: theme.colors.warning,
|
|
1805
|
+
error: theme.colors.error
|
|
1806
|
+
},
|
|
1807
|
+
requireApprovalFor: requireApproval,
|
|
1808
|
+
onApprove: approveAction,
|
|
1809
|
+
onReject: rejectAction
|
|
1810
|
+
}
|
|
1811
|
+
),
|
|
1812
|
+
[
|
|
1813
|
+
theme.colors.primary,
|
|
1814
|
+
theme.colors.text,
|
|
1815
|
+
theme.colors.textSecondary,
|
|
1816
|
+
theme.colors.surface,
|
|
1817
|
+
theme.colors.border,
|
|
1818
|
+
theme.colors.success,
|
|
1819
|
+
theme.colors.warning,
|
|
1820
|
+
theme.colors.error,
|
|
1821
|
+
theme.typography.fontFamily,
|
|
1822
|
+
requireApproval,
|
|
1823
|
+
approveAction,
|
|
1824
|
+
rejectAction
|
|
1825
|
+
]
|
|
1826
|
+
);
|
|
1827
|
+
const resolvedHeaderTitle = resolvedTitle ?? t("chatTitle");
|
|
1828
|
+
const resolvedPlaceholder = placeholder ?? t("inputPlaceholder");
|
|
1829
|
+
const resolvedEmptyTitle = t("emptyTitle");
|
|
1830
|
+
const resolvedEmptySubtitle = welcomeMessage ?? t("emptySubtitle");
|
|
1831
|
+
const showEmptySubtitle = resolvedEmptySubtitle.length > 0 && !(showSuggestedPrompts && suggestedPrompts.length > 0);
|
|
1832
|
+
const renderAvatar = () => {
|
|
1833
|
+
if (!assistantAvatar) {
|
|
1834
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: [styles3.headerDot, { backgroundColor: theme.colors.primary }] });
|
|
1835
|
+
}
|
|
1836
|
+
if (typeof assistantAvatar === "string") {
|
|
1837
|
+
return /* @__PURE__ */ React7__default.createElement(Image, { source: { uri: assistantAvatar }, style: styles3.headerAvatar });
|
|
1838
|
+
}
|
|
1839
|
+
if (typeof assistantAvatar === "number") {
|
|
1840
|
+
return /* @__PURE__ */ React7__default.createElement(Image, { source: assistantAvatar, style: styles3.headerAvatar });
|
|
1841
|
+
}
|
|
1842
|
+
return /* @__PURE__ */ React7__default.createElement(View, { style: styles3.headerAvatarSlot }, assistantAvatar);
|
|
1843
|
+
};
|
|
1844
|
+
const showTrigger = mode === "bubble" && !isVisible && controlledVisible === void 0 && triggerButton !== "none";
|
|
1845
|
+
const fabPositionStyle = (() => {
|
|
1846
|
+
switch (position) {
|
|
1847
|
+
case "bottom-left":
|
|
1848
|
+
return { bottom: 24, left: 24 };
|
|
1849
|
+
case "top-right":
|
|
1850
|
+
return { top: 24, right: 24 };
|
|
1851
|
+
case "top-left":
|
|
1852
|
+
return { top: 24, left: 24 };
|
|
1853
|
+
case "bottom-right":
|
|
1854
|
+
default:
|
|
1855
|
+
return { bottom: 24, right: 24 };
|
|
1856
|
+
}
|
|
1857
|
+
})();
|
|
1858
|
+
const chatSurface = /* @__PURE__ */ React7__default.createElement(
|
|
1859
|
+
View,
|
|
1860
|
+
{
|
|
1861
|
+
...APPILOTS_SKIP_PROPS,
|
|
1862
|
+
style: [
|
|
1863
|
+
styles3.chatContainer,
|
|
1864
|
+
mode === "fullscreen" && styles3.chatContainerFullscreen,
|
|
1865
|
+
mode === "sidebar" && styles3.chatContainerSidebar,
|
|
1866
|
+
mode === "inline" && styles3.chatContainerInline,
|
|
1867
|
+
{
|
|
1868
|
+
backgroundColor: theme.colors.background,
|
|
1869
|
+
borderTopLeftRadius: theme.radii.lg,
|
|
1870
|
+
borderTopRightRadius: theme.radii.lg
|
|
1871
|
+
}
|
|
1872
|
+
]
|
|
1873
|
+
},
|
|
1874
|
+
/* @__PURE__ */ React7__default.createElement(View, { style: [styles3.header, { borderBottomColor: theme.colors.border }] }, /* @__PURE__ */ React7__default.createElement(View, { style: styles3.headerLeft }, renderAvatar(), /* @__PURE__ */ React7__default.createElement(
|
|
1875
|
+
Text,
|
|
1876
|
+
{
|
|
1877
|
+
style: [
|
|
1878
|
+
styles3.headerTitle,
|
|
1879
|
+
{
|
|
1880
|
+
color: theme.colors.text,
|
|
1881
|
+
fontFamily: theme.typography.fontFamily,
|
|
1882
|
+
fontSize: theme.typography.fontSizeLg
|
|
1883
|
+
}
|
|
1884
|
+
]
|
|
1885
|
+
},
|
|
1886
|
+
resolvedHeaderTitle
|
|
1887
|
+
)), /* @__PURE__ */ React7__default.createElement(View, { style: styles3.headerActions }, /* @__PURE__ */ React7__default.createElement(
|
|
1888
|
+
TouchableOpacity,
|
|
1889
|
+
{
|
|
1890
|
+
...APPILOTS_SKIP_PROPS,
|
|
1891
|
+
onPress: () => requestHuman("user_requested"),
|
|
1892
|
+
style: styles3.headerButton,
|
|
1893
|
+
accessibilityRole: "button",
|
|
1894
|
+
accessibilityLabel: strings.talkToHumanA11y,
|
|
1895
|
+
testID: "escalation-header-button"
|
|
1896
|
+
},
|
|
1897
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1898
|
+
HeadsetIcon,
|
|
1899
|
+
{
|
|
1900
|
+
size: 16,
|
|
1901
|
+
color: escalation ? theme.colors.primary : theme.colors.textSecondary
|
|
1902
|
+
}
|
|
1903
|
+
)
|
|
1904
|
+
), /* @__PURE__ */ React7__default.createElement(TouchableOpacity, { ...APPILOTS_SKIP_PROPS, onPress: clearMessages, style: styles3.headerButton }, /* @__PURE__ */ React7__default.createElement(Text, { style: [styles3.headerButtonText, { color: theme.colors.textSecondary }] }, strings.clearButton)), mode !== "inline" && /* @__PURE__ */ React7__default.createElement(
|
|
1905
|
+
TouchableOpacity,
|
|
1906
|
+
{
|
|
1907
|
+
...APPILOTS_SKIP_PROPS,
|
|
1908
|
+
onPress: closeChat,
|
|
1909
|
+
style: styles3.closeButton,
|
|
1910
|
+
accessibilityLabel: strings.closeChatA11y
|
|
1911
|
+
},
|
|
1912
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: [styles3.closeButtonText, { color: theme.colors.text }] }, "\u2715")
|
|
1913
|
+
))),
|
|
1914
|
+
escalation && /* @__PURE__ */ React7__default.createElement(
|
|
1915
|
+
View,
|
|
1916
|
+
{
|
|
1917
|
+
style: [
|
|
1918
|
+
styles3.escalationBanner,
|
|
1919
|
+
{
|
|
1920
|
+
borderBottomColor: theme.colors.border,
|
|
1921
|
+
backgroundColor: theme.colors.primary + "0d"
|
|
1922
|
+
}
|
|
1923
|
+
],
|
|
1924
|
+
testID: "escalation-banner"
|
|
1925
|
+
},
|
|
1926
|
+
/* @__PURE__ */ React7__default.createElement(HeadsetIcon, { size: 12, color: theme.colors.primary }),
|
|
1927
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1928
|
+
Text,
|
|
1929
|
+
{
|
|
1930
|
+
style: [
|
|
1931
|
+
styles3.escalationBannerText,
|
|
1932
|
+
{
|
|
1933
|
+
color: theme.colors.textSecondary,
|
|
1934
|
+
fontFamily: theme.typography.fontFamily,
|
|
1935
|
+
fontSize: theme.typography.fontSizeXs
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
escalation.status === "pending" ? strings.escalationPendingBanner : strings.escalationActiveBanner
|
|
1940
|
+
)
|
|
1941
|
+
),
|
|
1942
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
1943
|
+
FlatList,
|
|
1944
|
+
{
|
|
1945
|
+
...APPILOTS_SKIP_PROPS,
|
|
1946
|
+
ref: flatListRef,
|
|
1947
|
+
data: timeline,
|
|
1948
|
+
keyExtractor: (item) => item.type === "message" ? item.data.id : item.key,
|
|
1949
|
+
renderItem: ({ item }) => {
|
|
1950
|
+
if (item.type === "message") {
|
|
1951
|
+
return renderMessage({ item: item.data });
|
|
1952
|
+
}
|
|
1953
|
+
if (item.type === "breadcrumb") {
|
|
1954
|
+
return renderBreadcrumb(item.actions);
|
|
1955
|
+
}
|
|
1956
|
+
return null;
|
|
1957
|
+
},
|
|
1958
|
+
contentContainerStyle: styles3.messageList,
|
|
1959
|
+
ListEmptyComponent: /* @__PURE__ */ React7__default.createElement(View, { style: styles3.emptyState }, emptyStateIcon && isImageSource(emptyStateIcon) ? /* @__PURE__ */ React7__default.createElement(
|
|
1960
|
+
Image,
|
|
1961
|
+
{
|
|
1962
|
+
source: typeof emptyStateIcon === "string" ? { uri: emptyStateIcon } : emptyStateIcon,
|
|
1963
|
+
style: styles3.emptyIconImage,
|
|
1964
|
+
resizeMode: "contain"
|
|
1965
|
+
}
|
|
1966
|
+
) : /* @__PURE__ */ React7__default.createElement(
|
|
1967
|
+
View,
|
|
1968
|
+
{
|
|
1969
|
+
style: [
|
|
1970
|
+
styles3.emptyIconBadge,
|
|
1971
|
+
{ backgroundColor: theme.colors.primary + "14" }
|
|
1972
|
+
]
|
|
1973
|
+
},
|
|
1974
|
+
emptyStateIcon ? /* @__PURE__ */ React7__default.createElement(Text, { style: styles3.emptyIconEmoji }, emptyStateIcon) : /* @__PURE__ */ React7__default.createElement(ChatIcon, { size: 30, color: theme.colors.primary })
|
|
1975
|
+
), /* @__PURE__ */ React7__default.createElement(
|
|
1976
|
+
Text,
|
|
1977
|
+
{
|
|
1978
|
+
style: [
|
|
1979
|
+
styles3.emptyTitle,
|
|
1980
|
+
{ color: theme.colors.text, fontFamily: theme.typography.fontFamily }
|
|
1981
|
+
]
|
|
1982
|
+
},
|
|
1983
|
+
resolvedEmptyTitle
|
|
1984
|
+
), showEmptySubtitle && /* @__PURE__ */ React7__default.createElement(
|
|
1985
|
+
Text,
|
|
1986
|
+
{
|
|
1987
|
+
style: [
|
|
1988
|
+
styles3.emptySubtitle,
|
|
1989
|
+
{
|
|
1990
|
+
color: theme.colors.textSecondary,
|
|
1991
|
+
fontFamily: theme.typography.fontFamily
|
|
1992
|
+
}
|
|
1993
|
+
],
|
|
1994
|
+
numberOfLines: 2
|
|
1995
|
+
},
|
|
1996
|
+
resolvedEmptySubtitle
|
|
1997
|
+
), showSuggestedPrompts && suggestedPrompts.length > 0 && /* @__PURE__ */ React7__default.createElement(View, { style: styles3.chipsContainer }, suggestedPrompts.map((prompt) => /* @__PURE__ */ React7__default.createElement(
|
|
1998
|
+
TouchableOpacity,
|
|
1999
|
+
{
|
|
2000
|
+
key: `${prompt.source}:${prompt.text}`,
|
|
2001
|
+
style: [
|
|
2002
|
+
styles3.chip,
|
|
2003
|
+
{
|
|
2004
|
+
backgroundColor: theme.colors.surface,
|
|
2005
|
+
borderColor: theme.colors.border,
|
|
2006
|
+
borderRadius: 999
|
|
2007
|
+
}
|
|
2008
|
+
],
|
|
2009
|
+
onPress: () => setInputText(prompt.text),
|
|
2010
|
+
activeOpacity: 0.7,
|
|
2011
|
+
accessibilityLabel: `Suggested prompt: ${prompt.text}`
|
|
2012
|
+
},
|
|
2013
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2014
|
+
Text,
|
|
2015
|
+
{
|
|
2016
|
+
style: [
|
|
2017
|
+
styles3.chipText,
|
|
2018
|
+
{
|
|
2019
|
+
color: theme.colors.buttonText,
|
|
2020
|
+
fontFamily: theme.typography.fontFamily,
|
|
2021
|
+
fontSize: theme.typography.fontSizeSm
|
|
2022
|
+
}
|
|
2023
|
+
],
|
|
2024
|
+
numberOfLines: 1
|
|
2025
|
+
},
|
|
2026
|
+
prompt.text
|
|
2027
|
+
)
|
|
2028
|
+
)))),
|
|
2029
|
+
ListFooterComponent: isLoading ? /* @__PURE__ */ React7__default.createElement(
|
|
2030
|
+
View,
|
|
2031
|
+
{
|
|
2032
|
+
style: [
|
|
2033
|
+
styles3.messageBubble,
|
|
2034
|
+
styles3.assistantBubble,
|
|
2035
|
+
{ backgroundColor: theme.colors.surface }
|
|
2036
|
+
]
|
|
2037
|
+
},
|
|
2038
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2039
|
+
Text,
|
|
2040
|
+
{
|
|
2041
|
+
style: [
|
|
2042
|
+
styles3.messageText,
|
|
2043
|
+
{ color: theme.colors.textSecondary, fontFamily: theme.typography.fontFamily }
|
|
2044
|
+
]
|
|
2045
|
+
},
|
|
2046
|
+
strings[loadingStatusKey] ?? strings.thinking
|
|
2047
|
+
)
|
|
2048
|
+
) : null
|
|
2049
|
+
}
|
|
2050
|
+
),
|
|
2051
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2052
|
+
View,
|
|
2053
|
+
{
|
|
2054
|
+
style: [
|
|
2055
|
+
styles3.inputContainer,
|
|
2056
|
+
{
|
|
2057
|
+
borderTopColor: theme.colors.border,
|
|
2058
|
+
paddingBottom: Math.max(insets.bottom, 10)
|
|
2059
|
+
}
|
|
2060
|
+
]
|
|
2061
|
+
},
|
|
2062
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2063
|
+
TextInput,
|
|
2064
|
+
{
|
|
2065
|
+
style: [
|
|
2066
|
+
styles3.input,
|
|
2067
|
+
{
|
|
2068
|
+
backgroundColor: theme.colors.surface,
|
|
2069
|
+
color: theme.colors.text,
|
|
2070
|
+
borderRadius: theme.radii.md,
|
|
2071
|
+
fontFamily: theme.typography.fontFamily,
|
|
2072
|
+
fontSize: theme.typography.fontSizeBase,
|
|
2073
|
+
lineHeight: theme.typography.fontSizeBase * 1.35
|
|
2074
|
+
}
|
|
2075
|
+
],
|
|
2076
|
+
placeholder: resolvedPlaceholder,
|
|
2077
|
+
placeholderTextColor: theme.colors.textSecondary,
|
|
2078
|
+
value: inputText,
|
|
2079
|
+
onChangeText: setInputText,
|
|
2080
|
+
multiline: true,
|
|
2081
|
+
blurOnSubmit: false,
|
|
2082
|
+
scrollEnabled: true,
|
|
2083
|
+
editable: !isLoading,
|
|
2084
|
+
testID: "assistant-input",
|
|
2085
|
+
__appilotsInternal: true
|
|
2086
|
+
}
|
|
2087
|
+
),
|
|
2088
|
+
isLoading ? /* @__PURE__ */ React7__default.createElement(
|
|
2089
|
+
TouchableOpacity,
|
|
2090
|
+
{
|
|
2091
|
+
style: [
|
|
2092
|
+
styles3.sendButton,
|
|
2093
|
+
{
|
|
2094
|
+
backgroundColor: theme.colors.primary,
|
|
2095
|
+
borderRadius: theme.radii.md
|
|
2096
|
+
}
|
|
2097
|
+
],
|
|
2098
|
+
onPress: cancelMessage,
|
|
2099
|
+
accessibilityRole: "button",
|
|
2100
|
+
accessibilityLabel: strings.stopGeneratingA11y,
|
|
2101
|
+
testID: "appilots-stop-button",
|
|
2102
|
+
activeOpacity: 0.7
|
|
2103
|
+
},
|
|
2104
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: styles3.sendButtonText }, "\u25A0")
|
|
2105
|
+
) : /* @__PURE__ */ React7__default.createElement(
|
|
2106
|
+
TouchableOpacity,
|
|
2107
|
+
{
|
|
2108
|
+
style: [
|
|
2109
|
+
styles3.sendButton,
|
|
2110
|
+
{
|
|
2111
|
+
backgroundColor: inputText.trim() ? theme.colors.primary : theme.colors.primary + "40",
|
|
2112
|
+
borderRadius: theme.radii.md
|
|
2113
|
+
}
|
|
2114
|
+
],
|
|
2115
|
+
onPress: handleSend,
|
|
2116
|
+
disabled: !inputText.trim(),
|
|
2117
|
+
activeOpacity: 0.7,
|
|
2118
|
+
accessibilityRole: "button",
|
|
2119
|
+
testID: "assistant-send"
|
|
2120
|
+
},
|
|
2121
|
+
/* @__PURE__ */ React7__default.createElement(Text, { style: styles3.sendButtonText }, "\u2191")
|
|
2122
|
+
)
|
|
2123
|
+
),
|
|
2124
|
+
poweredByVisible && /* @__PURE__ */ React7__default.createElement(View, { style: styles3.poweredByContainer }, /* @__PURE__ */ React7__default.createElement(
|
|
2125
|
+
Text,
|
|
2126
|
+
{
|
|
2127
|
+
style: [
|
|
2128
|
+
styles3.poweredByText,
|
|
2129
|
+
{
|
|
2130
|
+
color: theme.colors.textSecondary,
|
|
2131
|
+
fontFamily: theme.typography.fontFamily,
|
|
2132
|
+
fontSize: theme.typography.fontSizeXs
|
|
2133
|
+
}
|
|
2134
|
+
]
|
|
2135
|
+
},
|
|
2136
|
+
strings.poweredBy
|
|
2137
|
+
))
|
|
2138
|
+
);
|
|
2139
|
+
if (mode === "inline") {
|
|
2140
|
+
return chatSurface;
|
|
2141
|
+
}
|
|
2142
|
+
const renderTrigger = () => {
|
|
2143
|
+
if (triggerButton === "none") return null;
|
|
2144
|
+
if (triggerButton !== "default" && React7__default.isValidElement(triggerButton)) {
|
|
2145
|
+
return /* @__PURE__ */ React7__default.createElement(View, { ...APPILOTS_SKIP_PROPS, style: [styles3.fabWrapper, fabPositionStyle] }, /* @__PURE__ */ React7__default.createElement(
|
|
2146
|
+
TouchableOpacity,
|
|
2147
|
+
{
|
|
2148
|
+
...APPILOTS_SKIP_PROPS,
|
|
2149
|
+
onPress: openChat,
|
|
2150
|
+
activeOpacity: 0.8,
|
|
2151
|
+
accessibilityRole: "button",
|
|
2152
|
+
accessibilityLabel: strings.openChatA11y,
|
|
2153
|
+
testID: "assistant-launcher"
|
|
2154
|
+
},
|
|
2155
|
+
triggerButton
|
|
2156
|
+
));
|
|
2157
|
+
}
|
|
2158
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
2159
|
+
TouchableOpacity,
|
|
2160
|
+
{
|
|
2161
|
+
...APPILOTS_SKIP_PROPS,
|
|
2162
|
+
style: [
|
|
2163
|
+
styles3.fab,
|
|
2164
|
+
fabPositionStyle,
|
|
2165
|
+
{ backgroundColor: triggerButtonColor ?? theme.colors.primary }
|
|
2166
|
+
],
|
|
2167
|
+
onPress: openChat,
|
|
2168
|
+
activeOpacity: 0.8,
|
|
2169
|
+
accessibilityRole: "button",
|
|
2170
|
+
accessibilityLabel: strings.openChatA11y,
|
|
2171
|
+
testID: "assistant-launcher"
|
|
2172
|
+
},
|
|
2173
|
+
triggerIcon ?? (triggerButtonImage ? /* @__PURE__ */ React7__default.createElement(
|
|
2174
|
+
Image,
|
|
2175
|
+
{
|
|
2176
|
+
source: typeof triggerButtonImage === "string" ? { uri: triggerButtonImage } : triggerButtonImage,
|
|
2177
|
+
style: styles3.fabImage,
|
|
2178
|
+
resizeMode: "contain"
|
|
2179
|
+
}
|
|
2180
|
+
) : /* @__PURE__ */ React7__default.createElement(ChatIcon, { size: 26, color: "#ffffff" }))
|
|
2181
|
+
);
|
|
2182
|
+
};
|
|
2183
|
+
return /* @__PURE__ */ React7__default.createElement(React7__default.Fragment, null, showTrigger && renderTrigger(), /* @__PURE__ */ React7__default.createElement(
|
|
2184
|
+
Modal,
|
|
2185
|
+
{
|
|
2186
|
+
...APPILOTS_SKIP_PROPS,
|
|
2187
|
+
visible: isVisible,
|
|
2188
|
+
transparent: true,
|
|
2189
|
+
animationType: "none",
|
|
2190
|
+
onRequestClose: closeChat
|
|
2191
|
+
},
|
|
2192
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2193
|
+
Animated.View,
|
|
2194
|
+
{
|
|
2195
|
+
...APPILOTS_SKIP_PROPS,
|
|
2196
|
+
style: [
|
|
2197
|
+
mode === "fullscreen" ? styles3.modalContainerFullscreen : styles3.modalContainer,
|
|
2198
|
+
mode === "sidebar" && styles3.modalContainerSidebar,
|
|
2199
|
+
{ transform: [{ translateY: slideAnim }] }
|
|
2200
|
+
]
|
|
2201
|
+
},
|
|
2202
|
+
/* @__PURE__ */ React7__default.createElement(
|
|
2203
|
+
KeyboardAvoidingView,
|
|
2204
|
+
{
|
|
2205
|
+
style: styles3.keyboardView,
|
|
2206
|
+
behavior: Platform.OS === "ios" ? "padding" : "height",
|
|
2207
|
+
keyboardVerticalOffset: 0
|
|
2208
|
+
},
|
|
2209
|
+
chatSurface
|
|
2210
|
+
)
|
|
2211
|
+
)
|
|
2212
|
+
));
|
|
2213
|
+
}
|
|
2214
|
+
var styles3 = StyleSheet.create({
|
|
2215
|
+
fab: {
|
|
2216
|
+
position: "absolute",
|
|
2217
|
+
width: 56,
|
|
2218
|
+
height: 56,
|
|
2219
|
+
borderRadius: 28,
|
|
2220
|
+
alignItems: "center",
|
|
2221
|
+
justifyContent: "center",
|
|
2222
|
+
elevation: 6,
|
|
2223
|
+
shadowColor: "#000",
|
|
2224
|
+
shadowOffset: { width: 0, height: 3 },
|
|
2225
|
+
shadowOpacity: 0.27,
|
|
2226
|
+
shadowRadius: 4.65,
|
|
2227
|
+
zIndex: 999
|
|
2228
|
+
},
|
|
2229
|
+
fabWrapper: {
|
|
2230
|
+
position: "absolute",
|
|
2231
|
+
zIndex: 999
|
|
2232
|
+
},
|
|
2233
|
+
fabIcon: {
|
|
2234
|
+
fontSize: 24
|
|
2235
|
+
},
|
|
2236
|
+
fabImage: {
|
|
2237
|
+
width: 32,
|
|
2238
|
+
height: 32,
|
|
2239
|
+
borderRadius: 16
|
|
2240
|
+
},
|
|
2241
|
+
modalContainer: {
|
|
2242
|
+
flex: 1,
|
|
2243
|
+
justifyContent: "flex-end",
|
|
2244
|
+
backgroundColor: "rgba(0,0,0,0.4)"
|
|
2245
|
+
},
|
|
2246
|
+
modalContainerFullscreen: {
|
|
2247
|
+
flex: 1,
|
|
2248
|
+
backgroundColor: "rgba(0,0,0,0.4)"
|
|
2249
|
+
},
|
|
2250
|
+
modalContainerSidebar: {
|
|
2251
|
+
// Right-pinned half-width on tablet/web. Stays as bottom-sheet on
|
|
2252
|
+
// narrow phones so we don't hand the user a 50% sliver.
|
|
2253
|
+
flex: 1,
|
|
2254
|
+
flexDirection: "row",
|
|
2255
|
+
justifyContent: "flex-end"
|
|
2256
|
+
},
|
|
2257
|
+
keyboardView: {
|
|
2258
|
+
flex: 1,
|
|
2259
|
+
justifyContent: "flex-end"
|
|
2260
|
+
},
|
|
2261
|
+
chatContainer: {
|
|
2262
|
+
height: SCREEN_HEIGHT * 0.75,
|
|
2263
|
+
shadowColor: "#000",
|
|
2264
|
+
shadowOffset: { width: 0, height: -3 },
|
|
2265
|
+
shadowOpacity: 0.1,
|
|
2266
|
+
shadowRadius: 12,
|
|
2267
|
+
elevation: 10
|
|
2268
|
+
},
|
|
2269
|
+
chatContainerFullscreen: {
|
|
2270
|
+
height: SCREEN_HEIGHT
|
|
2271
|
+
},
|
|
2272
|
+
chatContainerSidebar: {
|
|
2273
|
+
height: SCREEN_HEIGHT,
|
|
2274
|
+
width: SCREEN_WIDTH > 600 ? SCREEN_WIDTH * 0.4 : SCREEN_WIDTH
|
|
2275
|
+
},
|
|
2276
|
+
chatContainerInline: {
|
|
2277
|
+
height: "100%",
|
|
2278
|
+
width: "100%",
|
|
2279
|
+
flex: 1
|
|
2280
|
+
},
|
|
2281
|
+
header: {
|
|
2282
|
+
flexDirection: "row",
|
|
2283
|
+
alignItems: "center",
|
|
2284
|
+
justifyContent: "space-between",
|
|
2285
|
+
paddingHorizontal: 16,
|
|
2286
|
+
paddingVertical: 14,
|
|
2287
|
+
borderBottomWidth: 1
|
|
2288
|
+
},
|
|
2289
|
+
headerLeft: {
|
|
2290
|
+
flexDirection: "row",
|
|
2291
|
+
alignItems: "center",
|
|
2292
|
+
gap: 8
|
|
2293
|
+
},
|
|
2294
|
+
headerDot: {
|
|
2295
|
+
width: 8,
|
|
2296
|
+
height: 8,
|
|
2297
|
+
borderRadius: 4
|
|
2298
|
+
},
|
|
2299
|
+
headerAvatar: {
|
|
2300
|
+
width: 24,
|
|
2301
|
+
height: 24,
|
|
2302
|
+
borderRadius: 12
|
|
2303
|
+
},
|
|
2304
|
+
headerAvatarSlot: {
|
|
2305
|
+
width: 24,
|
|
2306
|
+
height: 24,
|
|
2307
|
+
alignItems: "center",
|
|
2308
|
+
justifyContent: "center"
|
|
2309
|
+
},
|
|
2310
|
+
headerTitle: {
|
|
2311
|
+
fontWeight: "600"
|
|
2312
|
+
},
|
|
2313
|
+
headerActions: {
|
|
2314
|
+
flexDirection: "row",
|
|
2315
|
+
alignItems: "center",
|
|
2316
|
+
gap: 12
|
|
2317
|
+
},
|
|
2318
|
+
headerButton: {
|
|
2319
|
+
paddingHorizontal: 8,
|
|
2320
|
+
paddingVertical: 4
|
|
2321
|
+
},
|
|
2322
|
+
headerButtonText: {
|
|
2323
|
+
fontSize: 13
|
|
2324
|
+
},
|
|
2325
|
+
closeButton: {
|
|
2326
|
+
width: 28,
|
|
2327
|
+
height: 28,
|
|
2328
|
+
borderRadius: 14,
|
|
2329
|
+
alignItems: "center",
|
|
2330
|
+
justifyContent: "center"
|
|
2331
|
+
},
|
|
2332
|
+
closeButtonText: {
|
|
2333
|
+
fontSize: 18,
|
|
2334
|
+
fontWeight: "300"
|
|
2335
|
+
},
|
|
2336
|
+
messageList: {
|
|
2337
|
+
paddingHorizontal: 16,
|
|
2338
|
+
paddingTop: 12,
|
|
2339
|
+
paddingBottom: 8,
|
|
2340
|
+
flexGrow: 1
|
|
2341
|
+
},
|
|
2342
|
+
messageBubble: {
|
|
2343
|
+
paddingHorizontal: BUBBLE_HORIZONTAL_PADDING,
|
|
2344
|
+
paddingVertical: 10,
|
|
2345
|
+
borderRadius: 18,
|
|
2346
|
+
marginBottom: 8
|
|
2347
|
+
},
|
|
2348
|
+
userBubble: {
|
|
2349
|
+
alignSelf: "flex-end",
|
|
2350
|
+
maxWidth: "82%",
|
|
2351
|
+
borderBottomRightRadius: 4
|
|
2352
|
+
},
|
|
2353
|
+
assistantBubble: {
|
|
2354
|
+
alignSelf: "flex-start",
|
|
2355
|
+
maxWidth: "94%",
|
|
2356
|
+
borderBottomLeftRadius: 4
|
|
2357
|
+
},
|
|
2358
|
+
assistantBubbleWide: {
|
|
2359
|
+
width: "100%",
|
|
2360
|
+
maxWidth: "100%"
|
|
2361
|
+
},
|
|
2362
|
+
messageText: {
|
|
2363
|
+
lineHeight: 20
|
|
2364
|
+
},
|
|
2365
|
+
emptyState: {
|
|
2366
|
+
flex: 1,
|
|
2367
|
+
alignItems: "center",
|
|
2368
|
+
justifyContent: "center",
|
|
2369
|
+
paddingVertical: 48,
|
|
2370
|
+
paddingHorizontal: 28
|
|
2371
|
+
},
|
|
2372
|
+
emptyIconBadge: {
|
|
2373
|
+
width: 64,
|
|
2374
|
+
height: 64,
|
|
2375
|
+
borderRadius: 32,
|
|
2376
|
+
alignItems: "center",
|
|
2377
|
+
justifyContent: "center",
|
|
2378
|
+
marginBottom: 20
|
|
2379
|
+
},
|
|
2380
|
+
emptyIconImage: {
|
|
2381
|
+
width: 64,
|
|
2382
|
+
height: 64,
|
|
2383
|
+
marginBottom: 20
|
|
2384
|
+
},
|
|
2385
|
+
emptyIconEmoji: {
|
|
2386
|
+
fontSize: 30,
|
|
2387
|
+
textAlign: "center"
|
|
2388
|
+
},
|
|
2389
|
+
emptyTitle: {
|
|
2390
|
+
fontSize: 20,
|
|
2391
|
+
fontWeight: "600",
|
|
2392
|
+
marginBottom: 6,
|
|
2393
|
+
textAlign: "center"
|
|
2394
|
+
},
|
|
2395
|
+
emptySubtitle: {
|
|
2396
|
+
fontSize: 14,
|
|
2397
|
+
textAlign: "center",
|
|
2398
|
+
lineHeight: 20,
|
|
2399
|
+
maxWidth: 280
|
|
2400
|
+
},
|
|
2401
|
+
chipsContainer: {
|
|
2402
|
+
flexDirection: "row",
|
|
2403
|
+
flexWrap: "wrap",
|
|
2404
|
+
justifyContent: "center",
|
|
2405
|
+
gap: 8,
|
|
2406
|
+
marginTop: 20,
|
|
2407
|
+
maxWidth: 320
|
|
2408
|
+
},
|
|
2409
|
+
chip: {
|
|
2410
|
+
paddingHorizontal: 16,
|
|
2411
|
+
paddingVertical: 10,
|
|
2412
|
+
borderWidth: 1,
|
|
2413
|
+
maxWidth: "100%"
|
|
2414
|
+
},
|
|
2415
|
+
chipText: {
|
|
2416
|
+
fontWeight: "500"
|
|
2417
|
+
},
|
|
2418
|
+
escalationBanner: {
|
|
2419
|
+
flexDirection: "row",
|
|
2420
|
+
alignItems: "center",
|
|
2421
|
+
justifyContent: "center",
|
|
2422
|
+
gap: 6,
|
|
2423
|
+
paddingVertical: 6,
|
|
2424
|
+
paddingHorizontal: 16,
|
|
2425
|
+
borderBottomWidth: 1
|
|
2426
|
+
},
|
|
2427
|
+
escalationBannerText: {
|
|
2428
|
+
fontWeight: "500"
|
|
2429
|
+
},
|
|
2430
|
+
systemMessageContainer: {
|
|
2431
|
+
alignItems: "center",
|
|
2432
|
+
marginVertical: 8,
|
|
2433
|
+
paddingHorizontal: 12,
|
|
2434
|
+
gap: 8
|
|
2435
|
+
},
|
|
2436
|
+
systemMessageText: {
|
|
2437
|
+
textAlign: "center",
|
|
2438
|
+
lineHeight: 18
|
|
2439
|
+
},
|
|
2440
|
+
escalationOfferChip: {
|
|
2441
|
+
flexDirection: "row",
|
|
2442
|
+
alignItems: "center",
|
|
2443
|
+
gap: 6
|
|
2444
|
+
},
|
|
2445
|
+
humanAgentContainer: {
|
|
2446
|
+
alignSelf: "flex-start",
|
|
2447
|
+
maxWidth: "94%"
|
|
2448
|
+
},
|
|
2449
|
+
humanAgentLabelRow: {
|
|
2450
|
+
flexDirection: "row",
|
|
2451
|
+
alignItems: "center",
|
|
2452
|
+
gap: 4,
|
|
2453
|
+
marginBottom: 2,
|
|
2454
|
+
marginLeft: 4
|
|
2455
|
+
},
|
|
2456
|
+
humanAgentLabel: {
|
|
2457
|
+
fontSize: 11,
|
|
2458
|
+
fontWeight: "600",
|
|
2459
|
+
textTransform: "uppercase",
|
|
2460
|
+
letterSpacing: 0.4
|
|
2461
|
+
},
|
|
2462
|
+
inputContainer: {
|
|
2463
|
+
flexDirection: "row",
|
|
2464
|
+
alignItems: "flex-end",
|
|
2465
|
+
paddingHorizontal: 12,
|
|
2466
|
+
paddingVertical: 10,
|
|
2467
|
+
borderTopWidth: 1,
|
|
2468
|
+
gap: 8
|
|
2469
|
+
},
|
|
2470
|
+
input: {
|
|
2471
|
+
flex: 1,
|
|
2472
|
+
minHeight: 38,
|
|
2473
|
+
maxHeight: 120,
|
|
2474
|
+
paddingHorizontal: 16,
|
|
2475
|
+
paddingTop: Platform.OS === "ios" ? 9 : 8,
|
|
2476
|
+
paddingBottom: Platform.OS === "ios" ? 9 : 8,
|
|
2477
|
+
textAlignVertical: "top"
|
|
2478
|
+
},
|
|
2479
|
+
sendButton: {
|
|
2480
|
+
width: 38,
|
|
2481
|
+
height: 38,
|
|
2482
|
+
alignItems: "center",
|
|
2483
|
+
justifyContent: "center"
|
|
2484
|
+
},
|
|
2485
|
+
sendButtonText: {
|
|
2486
|
+
color: "#ffffff",
|
|
2487
|
+
fontSize: 18,
|
|
2488
|
+
fontWeight: "700"
|
|
2489
|
+
},
|
|
2490
|
+
poweredByContainer: {
|
|
2491
|
+
alignItems: "center",
|
|
2492
|
+
paddingVertical: 4
|
|
2493
|
+
},
|
|
2494
|
+
poweredByText: {
|
|
2495
|
+
fontWeight: "500"
|
|
2496
|
+
}
|
|
2497
|
+
});
|
|
2498
|
+
|
|
2499
|
+
// src/components/ConfirmDialog.tsx
|
|
2500
|
+
function ConfirmDialog(_props = {}) {
|
|
2501
|
+
return null;
|
|
2502
|
+
}
|
|
2503
|
+
function normalizeId(label) {
|
|
2504
|
+
return label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase());
|
|
2505
|
+
}
|
|
2506
|
+
function createAppilotsInput(WrappedComponent) {
|
|
2507
|
+
const AppilotsInput = React7__default.forwardRef(
|
|
2508
|
+
(props, ref) => {
|
|
2509
|
+
const {
|
|
2510
|
+
appilotsId,
|
|
2511
|
+
appilotsScreen,
|
|
2512
|
+
appilotsFieldType = "text",
|
|
2513
|
+
appilotsEnabled = true,
|
|
2514
|
+
label,
|
|
2515
|
+
value,
|
|
2516
|
+
onChangeText,
|
|
2517
|
+
...rest
|
|
2518
|
+
} = props;
|
|
2519
|
+
const fieldId = appilotsId || (label ? normalizeId(label) : null);
|
|
2520
|
+
const valueRef = useRef(value ?? "");
|
|
2521
|
+
const onChangeRef = useRef(onChangeText);
|
|
2522
|
+
const innerRef = useRef(null);
|
|
2523
|
+
valueRef.current = value ?? "";
|
|
2524
|
+
onChangeRef.current = onChangeText;
|
|
2525
|
+
const getValue = useCallback(() => valueRef.current, []);
|
|
2526
|
+
const setValue = useCallback((v) => onChangeRef.current?.(v), []);
|
|
2527
|
+
const focus = useCallback(() => {
|
|
2528
|
+
innerRef.current?.focus?.();
|
|
2529
|
+
}, []);
|
|
2530
|
+
useEffect(() => {
|
|
2531
|
+
if (!fieldId || !appilotsEnabled) return;
|
|
2532
|
+
const entry = {
|
|
2533
|
+
kind: "field",
|
|
2534
|
+
getValue,
|
|
2535
|
+
setValue,
|
|
2536
|
+
focus,
|
|
2537
|
+
fieldType: appilotsFieldType,
|
|
2538
|
+
label: label ?? fieldId,
|
|
2539
|
+
screen: appilotsScreen
|
|
2540
|
+
};
|
|
2541
|
+
componentRegistry.register(fieldId, entry);
|
|
2542
|
+
return () => {
|
|
2543
|
+
componentRegistry.unregister(fieldId);
|
|
2544
|
+
};
|
|
2545
|
+
}, [fieldId, appilotsEnabled, getValue, setValue, focus, appilotsFieldType, label, appilotsScreen]);
|
|
2546
|
+
const mergedRef = useCallback(
|
|
2547
|
+
(instance) => {
|
|
2548
|
+
innerRef.current = instance;
|
|
2549
|
+
if (typeof ref === "function") ref(instance);
|
|
2550
|
+
else if (ref) ref.current = instance;
|
|
2551
|
+
},
|
|
2552
|
+
[ref]
|
|
2553
|
+
);
|
|
2554
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
2555
|
+
WrappedComponent,
|
|
2556
|
+
{
|
|
2557
|
+
...{ label, value, onChangeText, ...rest },
|
|
2558
|
+
ref: mergedRef
|
|
2559
|
+
}
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
);
|
|
2563
|
+
AppilotsInput.displayName = `AppilotsInput(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
|
|
2564
|
+
return AppilotsInput;
|
|
2565
|
+
}
|
|
2566
|
+
function normalizeId2(label) {
|
|
2567
|
+
return label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase());
|
|
2568
|
+
}
|
|
2569
|
+
function createAppilotsButton(WrappedComponent) {
|
|
2570
|
+
const AppilotsButton = React7__default.forwardRef(
|
|
2571
|
+
(props, ref) => {
|
|
2572
|
+
const {
|
|
2573
|
+
appilotsId,
|
|
2574
|
+
appilotsScreen,
|
|
2575
|
+
appilotsEnabled = true,
|
|
2576
|
+
title,
|
|
2577
|
+
onPress,
|
|
2578
|
+
...rest
|
|
2579
|
+
} = props;
|
|
2580
|
+
const targetId = appilotsId || (title ? normalizeId2(title) : null);
|
|
2581
|
+
const onPressRef = useRef(onPress);
|
|
2582
|
+
onPressRef.current = onPress;
|
|
2583
|
+
const press = useCallback(() => onPressRef.current?.(), []);
|
|
2584
|
+
useEffect(() => {
|
|
2585
|
+
if (!targetId || !appilotsEnabled) return;
|
|
2586
|
+
const entry = {
|
|
2587
|
+
kind: "target",
|
|
2588
|
+
press,
|
|
2589
|
+
label: title ?? targetId,
|
|
2590
|
+
screen: appilotsScreen
|
|
2591
|
+
};
|
|
2592
|
+
componentRegistry.register(targetId, entry);
|
|
2593
|
+
return () => {
|
|
2594
|
+
componentRegistry.unregister(targetId);
|
|
2595
|
+
};
|
|
2596
|
+
}, [targetId, appilotsEnabled, press, title, appilotsScreen]);
|
|
2597
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
2598
|
+
WrappedComponent,
|
|
2599
|
+
{
|
|
2600
|
+
...{ title, onPress, ...rest },
|
|
2601
|
+
ref
|
|
2602
|
+
}
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2605
|
+
);
|
|
2606
|
+
AppilotsButton.displayName = `AppilotsButton(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
|
|
2607
|
+
return AppilotsButton;
|
|
2608
|
+
}
|
|
2609
|
+
function normalizeId3(label) {
|
|
2610
|
+
return label.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9\s]/g, "").trim().replace(/\s+(.)/g, (_, c) => c.toUpperCase());
|
|
2611
|
+
}
|
|
2612
|
+
function createAppilotsSwitch(WrappedComponent) {
|
|
2613
|
+
const AppilotsSwitch = React7__default.forwardRef(
|
|
2614
|
+
(props, ref) => {
|
|
2615
|
+
const {
|
|
2616
|
+
appilotsId,
|
|
2617
|
+
appilotsScreen,
|
|
2618
|
+
appilotsEnabled = true,
|
|
2619
|
+
label,
|
|
2620
|
+
value,
|
|
2621
|
+
onValueChange,
|
|
2622
|
+
...rest
|
|
2623
|
+
} = props;
|
|
2624
|
+
const toggleId = appilotsId || (label ? normalizeId3(label) : null);
|
|
2625
|
+
const valueRef = useRef(value ?? false);
|
|
2626
|
+
const onChangeRef = useRef(onValueChange);
|
|
2627
|
+
valueRef.current = value ?? false;
|
|
2628
|
+
onChangeRef.current = onValueChange;
|
|
2629
|
+
const getValue = useCallback(() => valueRef.current, []);
|
|
2630
|
+
const setValue = useCallback((v) => onChangeRef.current?.(v), []);
|
|
2631
|
+
useEffect(() => {
|
|
2632
|
+
if (!toggleId || !appilotsEnabled) return;
|
|
2633
|
+
const entry = {
|
|
2634
|
+
kind: "toggle",
|
|
2635
|
+
getValue,
|
|
2636
|
+
setValue,
|
|
2637
|
+
label: label ?? toggleId,
|
|
2638
|
+
screen: appilotsScreen
|
|
2639
|
+
};
|
|
2640
|
+
componentRegistry.register(toggleId, entry);
|
|
2641
|
+
return () => {
|
|
2642
|
+
componentRegistry.unregister(toggleId);
|
|
2643
|
+
};
|
|
2644
|
+
}, [toggleId, appilotsEnabled, getValue, setValue, label, appilotsScreen]);
|
|
2645
|
+
return /* @__PURE__ */ React7__default.createElement(
|
|
2646
|
+
WrappedComponent,
|
|
2647
|
+
{
|
|
2648
|
+
...{ label, value, onValueChange, ...rest },
|
|
2649
|
+
ref
|
|
2650
|
+
}
|
|
2651
|
+
);
|
|
2652
|
+
}
|
|
2653
|
+
);
|
|
2654
|
+
AppilotsSwitch.displayName = `AppilotsSwitch(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
|
|
2655
|
+
return AppilotsSwitch;
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
export { ActionBreadcrumb, AppilotsChat, AppilotsI18nProvider, AppilotsThemeProvider, ChatIcon, ConfirmDialog, HeadsetIcon, appilotsTheme, appilotsThemeFromTailwind, createAppilotsButton, createAppilotsInput, createAppilotsSwitch, defaultDarkTheme, defaultLightTheme, describeAction, detectDeviceLocale, humanizeError, mergeThemeTokens, resolveLocale, useAppilotsI18n, useAppilotsTheme };
|