@appilots/sdk 0.11.3 → 0.14.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/control-transformer.js +69 -0
- package/dist/.build-meta.json +3 -3
- package/dist/{chunk-2ZDELL54.mjs → chunk-6JWU3NBT.mjs} +860 -208
- package/dist/{chunk-ROXXCDIG.mjs → chunk-AKAMWKO6.mjs} +5795 -546
- package/dist/{chunk-5UVMNKPT.js → chunk-GDCBLSYT.js} +8 -8
- package/dist/{chunk-UFMTRGML.js → chunk-JGUDXRXN.js} +1143 -477
- package/dist/{chunk-JKLE5SLN.mjs → chunk-N66DUP54.mjs} +1 -1
- package/dist/{chunk-SUMIABJK.js → chunk-QAZ4TN3Z.js} +5817 -544
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.js +11 -11
- package/dist/hooks/index.mjs +2 -2
- package/dist/{index-BR-33WMN.d.mts → index-wMMetyrj.d.mts} +169 -660
- package/dist/{index-BR-33WMN.d.ts → index-wMMetyrj.d.ts} +169 -660
- package/dist/index.d.mts +1500 -76
- package/dist/index.d.ts +1500 -76
- package/dist/index.js +1442 -720
- package/dist/index.mjs +1271 -612
- package/dist/navigation/index.d.mts +22 -1
- package/dist/navigation/index.d.ts +22 -1
- package/dist/navigation/index.js +13 -13
- package/dist/navigation/index.mjs +2 -2
- package/metro.d.ts +3 -5
- package/metro.js +46 -5
- package/package.json +16 -2
|
@@ -1,6 +1,277 @@
|
|
|
1
|
-
import { createSnapshotDeltaTracker, getFiberRoot, appilotsDebugWarn, recordIntrospectionFailure, getCurrentScreen, walkFiber, getActiveRouteNames, clampSnapshotToWireLimits, deriveInteractionElements, rectForNode, toWireElement, attachElementIdsToChoiceGroups, elementRegistry, appilotsDebugLog, getLastWalkDiagnostics, SDK_VERSION, geometryStats, geometryAvailability, getNameManglingProbe, emptyDetectorCounts, getAutoTrackingState, getIntrospectionDiagnostics, getDefaultRegistry, useAppilotsContext, ChatSessionMachine, getCurrentScreenSignature, ActionQueueMachine, getScreenMetadata, setCurrentScreen, useResolvedRegistry, totalDetectorHits,
|
|
2
|
-
import React, { useMemo, useRef, useSyncExternalStore, useEffect, useCallback, useState } from 'react';
|
|
3
|
-
import { Alert } from 'react-native';
|
|
1
|
+
import { createSnapshotDeltaTracker, normalizeAppilotsLocale, getFiberRoot, appilotsDebugWarn, recordIntrospectionFailure, getCurrentScreen, walkFiber, getActiveRouteNames, clampSnapshotToWireLimits, deriveInteractionElements, rectForNode, toWireElement, attachElementIdsToChoiceGroups, elementRegistry, appilotsDebugLog, getLastWalkDiagnostics, SDK_VERSION, geometryStats, geometryAvailability, getNameManglingProbe, emptyDetectorCounts, getAutoTrackingState, getIntrospectionDiagnostics, getGeometryHost, isRectOnScreen, listRegistry, resolveAgentPermissions, getDefaultRegistry, useAppilotsContext, ChatSessionMachine, getCurrentScreenSignature, ActionQueueMachine, getScreenMetadata, setCurrentScreen, useResolvedRegistry, totalDetectorHits, getOpenNativeDialog, componentRegistry, permissionDeniedMessage, probeLoadingState, getLastAnsweredNativeDialog, isListItemPressTarget, waitForLoadingSettle, describeValue, waitForScreenSettle, screenDepartedBaseline, routesBelongToSameFeature, actionPressTargetId, getNavigationStateSnapshot, getNavigationRef, isOptionalStepAutomationFailure, OPTIONAL_STEP_AUTOMATION_HINT, isSourceControlId, resolveTarget, isSwitchLike, sourceControlTarget, rectOfFiber, idLooselyMatches, normalize3, matchScore, FORM_VALIDATION_TEXT_RE, parseStableElementId, shouldSkipSubtree, isAlwaysListContainer, isHeuristicListContainer, findListItems, getTypeName, foldForCompare, isModalFiber, sourceControlId, endTextInputEditing, switchValue, getAllScreens, snapshotShowsOpenCreateForm, isReferenceable, looksDestructiveActionLabel, isAppilotsSensitiveMarked, isGenericSelectValue, SUBMIT_CONTROL_RE, isVisibleValidationText, FORM_WRITE_SUCCESS_TEXT_RE, TOUCHABLE_NAMES } from './chunk-AKAMWKO6.mjs';
|
|
2
|
+
import React, { createContext, useMemo, useContext, useRef, useSyncExternalStore, useEffect, useCallback, useState } from 'react';
|
|
3
|
+
import { Platform, NativeModules, AppState, Alert } from 'react-native';
|
|
4
|
+
|
|
5
|
+
// src/i18n/bundles/pt-BR.ts
|
|
6
|
+
var ptBR = {
|
|
7
|
+
chatTitle: "Assistente Appilots",
|
|
8
|
+
clearButton: "Limpar",
|
|
9
|
+
inputPlaceholder: "Digite uma mensagem...",
|
|
10
|
+
emptyTitle: "Como posso ajudar?",
|
|
11
|
+
emptySubtitle: "Navego, preencho formul\xE1rios e executo a\xE7\xF5es neste app.",
|
|
12
|
+
thinking: "Pensando...",
|
|
13
|
+
statusAnalyzing: "Analisando a tela...",
|
|
14
|
+
statusWaitingApp: "Aguardando o app responder...",
|
|
15
|
+
statusAdjusting: "Ajustando o plano...",
|
|
16
|
+
stillWorking: "Ainda trabalhando\u2026 {seconds} s",
|
|
17
|
+
poweredBy: "Powered by Appilots",
|
|
18
|
+
approve: "Aprovar",
|
|
19
|
+
reject: "Rejeitar",
|
|
20
|
+
cancelledByUser: "Cancelado pelo usu\xE1rio",
|
|
21
|
+
confirmDestructiveTitle: "A\xE7\xE3o destrutiva",
|
|
22
|
+
confirmTitle: "Confirma\xE7\xE3o necess\xE1ria",
|
|
23
|
+
confirmMessage: 'Tem certeza que deseja "{action}"?',
|
|
24
|
+
confirmDestructiveApprove: "Sim, executar",
|
|
25
|
+
confirmApprove: "Confirmar",
|
|
26
|
+
confirmCancel: "Cancelar",
|
|
27
|
+
confirmThisAction: "esta a\xE7\xE3o",
|
|
28
|
+
confirmExpired: "Esta confirma\xE7\xE3o expirou. Pe\xE7a de novo ao assistente para continuar.",
|
|
29
|
+
unknownAction: "A\xE7\xE3o desconhecida",
|
|
30
|
+
somethingWentWrong: "Algo deu errado",
|
|
31
|
+
openChatA11y: "Abrir chat do assistente",
|
|
32
|
+
closeChatA11y: "Fechar chat do assistente",
|
|
33
|
+
stopGeneratingA11y: "Parar gera\xE7\xE3o",
|
|
34
|
+
talkToHumanA11y: "Falar com um atendente humano",
|
|
35
|
+
humanAgentLabel: "Atendente",
|
|
36
|
+
escalationRequested: "Certo! Chamei um atendente humano. Voc\xEA pode continuar usando o app normalmente enquanto isso.",
|
|
37
|
+
escalationConnected: "Um atendente entrou na conversa.",
|
|
38
|
+
escalationResolved: "O atendimento humano foi encerrado. Voltei a te ajudar por aqui.",
|
|
39
|
+
escalationOffer: "Se preferir, posso chamar uma pessoa para ajudar.",
|
|
40
|
+
escalationOfferChip: "Falar com um atendente",
|
|
41
|
+
escalationPendingBanner: "Aguardando um atendente...",
|
|
42
|
+
escalationActiveBanner: "Voc\xEA est\xE1 falando com um atendente",
|
|
43
|
+
pillWorking: "Trabalhando...",
|
|
44
|
+
pillExpandA11y: "Abrir a conversa do assistente",
|
|
45
|
+
pillStopA11y: "Parar o assistente",
|
|
46
|
+
missionPause: "Pausar",
|
|
47
|
+
missionResume: "Retomar tarefa",
|
|
48
|
+
missionCancel: "Cancelar tarefa",
|
|
49
|
+
missionPaused: "Pausada",
|
|
50
|
+
pillDone: "Pronto \u2014 toque para ver",
|
|
51
|
+
a11yAwaitingApproval: "O assistente precisa da sua confirma\xE7\xE3o para continuar.",
|
|
52
|
+
a11yTurnDone: "O assistente terminou.",
|
|
53
|
+
micStartA11y: "Segurar para falar",
|
|
54
|
+
micStopA11y: "Soltar para enviar",
|
|
55
|
+
micListening: "Ouvindo..."
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/i18n/bundles/en.ts
|
|
59
|
+
var en = {
|
|
60
|
+
chatTitle: "Appilots Assistant",
|
|
61
|
+
clearButton: "Clear",
|
|
62
|
+
inputPlaceholder: "Type a message...",
|
|
63
|
+
emptyTitle: "How can I help?",
|
|
64
|
+
emptySubtitle: "I navigate, fill forms, and run actions in this app.",
|
|
65
|
+
thinking: "Thinking...",
|
|
66
|
+
statusAnalyzing: "Reading the screen...",
|
|
67
|
+
statusWaitingApp: "Waiting for the app...",
|
|
68
|
+
statusAdjusting: "Adjusting the plan...",
|
|
69
|
+
stillWorking: "Still working\u2026 {seconds} s",
|
|
70
|
+
poweredBy: "Powered by Appilots",
|
|
71
|
+
approve: "Approve",
|
|
72
|
+
reject: "Reject",
|
|
73
|
+
cancelledByUser: "Cancelled by user",
|
|
74
|
+
confirmDestructiveTitle: "Destructive action",
|
|
75
|
+
confirmTitle: "Confirmation needed",
|
|
76
|
+
confirmMessage: 'Are you sure you want to "{action}"?',
|
|
77
|
+
confirmDestructiveApprove: "Yes, do it",
|
|
78
|
+
confirmApprove: "Confirm",
|
|
79
|
+
confirmCancel: "Cancel",
|
|
80
|
+
confirmThisAction: "this action",
|
|
81
|
+
confirmExpired: "This confirmation expired. Ask the assistant again to continue.",
|
|
82
|
+
unknownAction: "Unknown action",
|
|
83
|
+
somethingWentWrong: "Something went wrong",
|
|
84
|
+
openChatA11y: "Open assistant chat",
|
|
85
|
+
closeChatA11y: "Close assistant chat",
|
|
86
|
+
stopGeneratingA11y: "Stop generating",
|
|
87
|
+
talkToHumanA11y: "Talk to a human agent",
|
|
88
|
+
humanAgentLabel: "Agent",
|
|
89
|
+
escalationRequested: "Done! I've called a human agent. You can keep using the app normally in the meantime.",
|
|
90
|
+
escalationConnected: "A human agent joined the conversation.",
|
|
91
|
+
escalationResolved: "The human handoff has ended. I'm back to help you here.",
|
|
92
|
+
escalationOffer: "If you prefer, I can bring in a person to help.",
|
|
93
|
+
escalationOfferChip: "Talk to a human",
|
|
94
|
+
escalationPendingBanner: "Waiting for a human agent...",
|
|
95
|
+
escalationActiveBanner: "You're talking to a human agent",
|
|
96
|
+
pillWorking: "Working...",
|
|
97
|
+
pillExpandA11y: "Open the assistant conversation",
|
|
98
|
+
pillStopA11y: "Stop the assistant",
|
|
99
|
+
missionPause: "Pause",
|
|
100
|
+
missionResume: "Resume task",
|
|
101
|
+
missionCancel: "Cancel task",
|
|
102
|
+
missionPaused: "Paused",
|
|
103
|
+
pillDone: "Done \u2014 tap to read",
|
|
104
|
+
a11yAwaitingApproval: "The assistant needs your confirmation to continue.",
|
|
105
|
+
a11yTurnDone: "The assistant finished.",
|
|
106
|
+
micStartA11y: "Hold to talk",
|
|
107
|
+
micStopA11y: "Release to send",
|
|
108
|
+
micListening: "Listening..."
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/i18n/bundles/es.ts
|
|
112
|
+
var es = {
|
|
113
|
+
chatTitle: "Asistente Appilots",
|
|
114
|
+
clearButton: "Limpiar",
|
|
115
|
+
inputPlaceholder: "Escribe un mensaje...",
|
|
116
|
+
emptyTitle: "\xBFEn qu\xE9 puedo ayudarte?",
|
|
117
|
+
emptySubtitle: "Navego, relleno formularios y ejecuto acciones en esta app.",
|
|
118
|
+
thinking: "Pensando...",
|
|
119
|
+
statusAnalyzing: "Analizando la pantalla...",
|
|
120
|
+
statusWaitingApp: "Esperando a la app...",
|
|
121
|
+
statusAdjusting: "Ajustando el plan...",
|
|
122
|
+
stillWorking: "Sigo trabajando\u2026 {seconds} s",
|
|
123
|
+
poweredBy: "Hecho con Appilots",
|
|
124
|
+
approve: "Aprobar",
|
|
125
|
+
reject: "Rechazar",
|
|
126
|
+
cancelledByUser: "Cancelado por el usuario",
|
|
127
|
+
confirmDestructiveTitle: "Acci\xF3n destructiva",
|
|
128
|
+
confirmTitle: "Confirmaci\xF3n necesaria",
|
|
129
|
+
confirmMessage: '\xBFSeguro que quieres "{action}"?',
|
|
130
|
+
confirmDestructiveApprove: "S\xED, ejecutar",
|
|
131
|
+
confirmApprove: "Confirmar",
|
|
132
|
+
confirmCancel: "Cancelar",
|
|
133
|
+
confirmThisAction: "esta acci\xF3n",
|
|
134
|
+
confirmExpired: "Esta confirmaci\xF3n expir\xF3. Pide de nuevo al asistente para continuar.",
|
|
135
|
+
unknownAction: "Acci\xF3n desconocida",
|
|
136
|
+
somethingWentWrong: "Algo sali\xF3 mal",
|
|
137
|
+
openChatA11y: "Abrir chat del asistente",
|
|
138
|
+
closeChatA11y: "Cerrar chat del asistente",
|
|
139
|
+
stopGeneratingA11y: "Detener generaci\xF3n",
|
|
140
|
+
talkToHumanA11y: "Hablar con un agente humano",
|
|
141
|
+
humanAgentLabel: "Agente",
|
|
142
|
+
escalationRequested: "\xA1Listo! Llam\xE9 a un agente humano. Mientras tanto puedes seguir usando la app con normalidad.",
|
|
143
|
+
escalationConnected: "Un agente humano se uni\xF3 a la conversaci\xF3n.",
|
|
144
|
+
escalationResolved: "La atenci\xF3n humana termin\xF3. Vuelvo a ayudarte por aqu\xED.",
|
|
145
|
+
escalationOffer: "Si prefieres, puedo llamar a una persona para ayudarte.",
|
|
146
|
+
escalationOfferChip: "Hablar con un agente",
|
|
147
|
+
escalationPendingBanner: "Esperando a un agente...",
|
|
148
|
+
escalationActiveBanner: "Est\xE1s hablando con un agente humano",
|
|
149
|
+
pillWorking: "Trabajando...",
|
|
150
|
+
pillExpandA11y: "Abrir la conversaci\xF3n del asistente",
|
|
151
|
+
pillStopA11y: "Detener el asistente",
|
|
152
|
+
missionPause: "Pausar",
|
|
153
|
+
missionResume: "Reanudar tarea",
|
|
154
|
+
missionCancel: "Cancelar tarea",
|
|
155
|
+
missionPaused: "Pausada",
|
|
156
|
+
pillDone: "Listo \u2014 toca para ver",
|
|
157
|
+
a11yAwaitingApproval: "El asistente necesita tu confirmaci\xF3n para continuar.",
|
|
158
|
+
a11yTurnDone: "El asistente termin\xF3.",
|
|
159
|
+
micStartA11y: "Mantener para hablar",
|
|
160
|
+
micStopA11y: "Soltar para enviar",
|
|
161
|
+
micListening: "Escuchando..."
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/i18n/bundles/fr.ts
|
|
165
|
+
var fr = {
|
|
166
|
+
chatTitle: "Assistant Appilots",
|
|
167
|
+
clearButton: "Effacer",
|
|
168
|
+
inputPlaceholder: "\xC9crivez un message\u2026",
|
|
169
|
+
emptyTitle: "Comment puis-je vous aider ?",
|
|
170
|
+
emptySubtitle: "Je navigue, remplis les formulaires et effectue des actions dans cette application.",
|
|
171
|
+
thinking: "R\xE9flexion en cours\u2026",
|
|
172
|
+
statusAnalyzing: "Lecture de l\u2019\xE9cran\u2026",
|
|
173
|
+
statusWaitingApp: "En attente de l\u2019application\u2026",
|
|
174
|
+
statusAdjusting: "Ajustement du plan\u2026",
|
|
175
|
+
stillWorking: "Toujours en cours\u2026 {seconds} s",
|
|
176
|
+
poweredBy: "Propuls\xE9 par Appilots",
|
|
177
|
+
approve: "Approuver",
|
|
178
|
+
reject: "Refuser",
|
|
179
|
+
cancelledByUser: "Annul\xE9 par l\u2019utilisateur",
|
|
180
|
+
confirmDestructiveTitle: "Action destructive",
|
|
181
|
+
confirmTitle: "Confirmation requise",
|
|
182
|
+
confirmMessage: "Voulez-vous vraiment \xAB {action} \xBB ?",
|
|
183
|
+
confirmDestructiveApprove: "Oui, ex\xE9cuter",
|
|
184
|
+
confirmApprove: "Confirmer",
|
|
185
|
+
confirmCancel: "Annuler",
|
|
186
|
+
confirmThisAction: "cette action",
|
|
187
|
+
confirmExpired: "Cette confirmation a expir\xE9. Redemandez \xE0 l\u2019assistant pour continuer.",
|
|
188
|
+
unknownAction: "Action inconnue",
|
|
189
|
+
somethingWentWrong: "Une erreur est survenue",
|
|
190
|
+
openChatA11y: "Ouvrir la conversation avec l\u2019assistant",
|
|
191
|
+
closeChatA11y: "Fermer la conversation",
|
|
192
|
+
stopGeneratingA11y: "Arr\xEAter la g\xE9n\xE9ration",
|
|
193
|
+
talkToHumanA11y: "Parler \xE0 un conseiller",
|
|
194
|
+
humanAgentLabel: "Conseiller",
|
|
195
|
+
escalationRequested: "J\u2019ai contact\xE9 un conseiller. Vous pouvez continuer \xE0 utiliser l\u2019application en attendant.",
|
|
196
|
+
escalationConnected: "Un conseiller a rejoint la conversation.",
|
|
197
|
+
escalationResolved: "L\u2019intervention du conseiller est termin\xE9e. Je peux \xE0 nouveau vous aider.",
|
|
198
|
+
escalationOffer: "Si vous pr\xE9f\xE9rez, je peux faire appel \xE0 un conseiller.",
|
|
199
|
+
escalationOfferChip: "Parler \xE0 un conseiller",
|
|
200
|
+
escalationPendingBanner: "En attente d\u2019un conseiller\u2026",
|
|
201
|
+
escalationActiveBanner: "Vous discutez avec un conseiller",
|
|
202
|
+
pillWorking: "En cours\u2026",
|
|
203
|
+
pillExpandA11y: "Ouvrir la conversation avec l\u2019assistant",
|
|
204
|
+
pillStopA11y: "Arr\xEAter l\u2019assistant",
|
|
205
|
+
missionPause: "Mettre en pause",
|
|
206
|
+
missionResume: "Reprendre la t\xE2che",
|
|
207
|
+
missionCancel: "Annuler la t\xE2che",
|
|
208
|
+
missionPaused: "En pause",
|
|
209
|
+
pillDone: "Termin\xE9 \u2014 touchez pour lire",
|
|
210
|
+
a11yAwaitingApproval: "L\u2019assistant a besoin de votre confirmation pour continuer.",
|
|
211
|
+
a11yTurnDone: "L\u2019assistant a termin\xE9.",
|
|
212
|
+
micStartA11y: "Maintenez pour parler",
|
|
213
|
+
micStopA11y: "Rel\xE2chez pour envoyer",
|
|
214
|
+
micListening: "\xC9coute en cours\u2026"
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/i18n/I18nProvider.tsx
|
|
218
|
+
var BUNDLES = {
|
|
219
|
+
"pt-BR": ptBR,
|
|
220
|
+
en,
|
|
221
|
+
es,
|
|
222
|
+
fr
|
|
223
|
+
};
|
|
224
|
+
var DEFAULT_LOCALE = "en";
|
|
225
|
+
function detectDeviceLocale() {
|
|
226
|
+
try {
|
|
227
|
+
let raw;
|
|
228
|
+
if (Platform.OS === "ios") {
|
|
229
|
+
const manager = NativeModules?.SettingsManager;
|
|
230
|
+
const settings = manager?.getConstants?.()?.settings ?? manager?.settings;
|
|
231
|
+
const languages = Array.isArray(settings?.AppleLanguages) ? settings.AppleLanguages : [];
|
|
232
|
+
for (const language of languages) {
|
|
233
|
+
const locale = normalizeAppilotsLocale(language);
|
|
234
|
+
if (locale) return locale;
|
|
235
|
+
}
|
|
236
|
+
raw = settings?.AppleLocale;
|
|
237
|
+
} else if (Platform.OS === "android") {
|
|
238
|
+
const manager = NativeModules?.I18nManager;
|
|
239
|
+
raw = manager?.getConstants?.()?.localeIdentifier ?? manager?.localeIdentifier;
|
|
240
|
+
}
|
|
241
|
+
if (!raw || typeof raw !== "string") return null;
|
|
242
|
+
return normalizeAppilotsLocale(raw) ?? null;
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function resolveLocale(prop) {
|
|
248
|
+
if (prop && BUNDLES[prop]) return prop;
|
|
249
|
+
const detected = detectDeviceLocale();
|
|
250
|
+
if (detected && BUNDLES[detected]) return detected;
|
|
251
|
+
return DEFAULT_LOCALE;
|
|
252
|
+
}
|
|
253
|
+
var I18nContext = createContext(null);
|
|
254
|
+
function AppilotsI18nProvider({ locale, overrides, children }) {
|
|
255
|
+
const value = useMemo(() => {
|
|
256
|
+
const resolved = resolveLocale(locale ?? null);
|
|
257
|
+
const merged = overrides ? { ...BUNDLES[resolved], ...overrides } : BUNDLES[resolved];
|
|
258
|
+
return {
|
|
259
|
+
locale: resolved,
|
|
260
|
+
strings: merged,
|
|
261
|
+
t: (key) => merged[key]
|
|
262
|
+
};
|
|
263
|
+
}, [locale, overrides]);
|
|
264
|
+
return /* @__PURE__ */ React.createElement(I18nContext.Provider, { value }, children);
|
|
265
|
+
}
|
|
266
|
+
function useAppilotsI18n() {
|
|
267
|
+
const ctx = useContext(I18nContext);
|
|
268
|
+
if (ctx) return ctx;
|
|
269
|
+
return {
|
|
270
|
+
locale: DEFAULT_LOCALE,
|
|
271
|
+
strings: BUNDLES[DEFAULT_LOCALE],
|
|
272
|
+
t: (key) => BUNDLES[DEFAULT_LOCALE][key]
|
|
273
|
+
};
|
|
274
|
+
}
|
|
4
275
|
|
|
5
276
|
// src/introspection/captureSnapshot.ts
|
|
6
277
|
function normalizeSliderKey(value) {
|
|
@@ -74,20 +345,35 @@ function mergeRegisteredLists(snapshot) {
|
|
|
74
345
|
}
|
|
75
346
|
for (const entry of registered) {
|
|
76
347
|
if (entry.kind === "scroll") continue;
|
|
348
|
+
const existing = byId.get(entry.id);
|
|
349
|
+
if (!existing && !(entry.screen && entry.screen === snapshot.route)) continue;
|
|
77
350
|
const metrics = entry.getScrollMetrics?.();
|
|
78
351
|
const scrollFacts = metrics ? {
|
|
79
352
|
scrollOffsetY: Math.max(0, Math.round(metrics.offset)),
|
|
80
353
|
canScrollUp: metrics.offset > 8,
|
|
81
354
|
canScrollDown: metrics.offset + metrics.visibleLength < metrics.contentLength - 8
|
|
82
355
|
} : void 0;
|
|
83
|
-
const existing = byId.get(entry.id);
|
|
84
356
|
if (existing) {
|
|
85
357
|
existing.itemCount = existing.itemCount ?? entry.itemCount;
|
|
358
|
+
existing.totalItemCount = existing.totalItemCount ?? entry.totalItemCount;
|
|
86
359
|
existing.refreshing = existing.refreshing ?? entry.refreshing;
|
|
87
360
|
existing.empty = existing.empty ?? entry.empty;
|
|
88
361
|
existing.label = existing.label ?? entry.label;
|
|
89
362
|
existing.source = existing.source ?? "auto-tracked";
|
|
90
363
|
existing.visibleItemCount = existing.items.length;
|
|
364
|
+
const viewport = entry.measureViewport?.();
|
|
365
|
+
if (viewport && viewport.width > 0 && viewport.height > 0) {
|
|
366
|
+
let measured = 0;
|
|
367
|
+
let inViewport = 0;
|
|
368
|
+
for (const item of existing.items) {
|
|
369
|
+
const rect = rectForNode(item);
|
|
370
|
+
if (!rect) continue;
|
|
371
|
+
measured++;
|
|
372
|
+
item.onScreen = rect.width > 0 && rect.height > 0 && rect.x < viewport.x + viewport.width && rect.x + rect.width > viewport.x && rect.y < viewport.y + viewport.height && rect.y + rect.height > viewport.y;
|
|
373
|
+
if (item.onScreen) inViewport++;
|
|
374
|
+
}
|
|
375
|
+
if (measured === existing.items.length) existing.viewportItemCount = inViewport;
|
|
376
|
+
}
|
|
91
377
|
if (entry.dataPreview && entry.dataPreview.length > 0) {
|
|
92
378
|
existing.dataPreview = entry.dataPreview;
|
|
93
379
|
}
|
|
@@ -104,6 +390,7 @@ function mergeRegisteredLists(snapshot) {
|
|
|
104
390
|
containerType: entry.component,
|
|
105
391
|
source: "registry",
|
|
106
392
|
itemCount: entry.itemCount,
|
|
393
|
+
totalItemCount: entry.totalItemCount,
|
|
107
394
|
visibleItemCount: 0,
|
|
108
395
|
refreshing: entry.refreshing,
|
|
109
396
|
empty: entry.empty,
|
|
@@ -169,6 +456,43 @@ function recordBlindWalk() {
|
|
|
169
456
|
if (totalDetectorHits(walk.detectors) > 0) return;
|
|
170
457
|
recordIntrospectionFailure("walk-recognized-nothing", null);
|
|
171
458
|
}
|
|
459
|
+
function mergeRegisteredFieldValues(snapshot) {
|
|
460
|
+
const present = new Set(snapshot.inputs.map((i) => normalizeFieldKey(i.id)));
|
|
461
|
+
const onScreenIds = [
|
|
462
|
+
...snapshot.buttons.map((b) => (b.id ?? "").toLowerCase()),
|
|
463
|
+
...snapshot.toggles.map((t) => (t.id ?? "").toLowerCase())
|
|
464
|
+
].filter(Boolean);
|
|
465
|
+
const onScreenLabels = new Set(
|
|
466
|
+
[...snapshot.buttons.map((b) => b.label), ...snapshot.texts].filter((l) => !!l).map((l) => l.toLowerCase())
|
|
467
|
+
);
|
|
468
|
+
for (const { id, kind, screen, label } of componentRegistry.snapshot()) {
|
|
469
|
+
if (kind !== "field") continue;
|
|
470
|
+
const key = normalizeFieldKey(id);
|
|
471
|
+
if (!key || present.has(key)) continue;
|
|
472
|
+
const entry = componentRegistry.getField(id);
|
|
473
|
+
if (!entry) continue;
|
|
474
|
+
const fieldType = entry.fieldType ?? "";
|
|
475
|
+
if (fieldType === "text" || fieldType === "") continue;
|
|
476
|
+
const declaresRoute = typeof screen === "string" && screen === snapshot.route;
|
|
477
|
+
if (screen && !declaresRoute && !getActiveRouteNames().includes(screen)) continue;
|
|
478
|
+
const idEvidence = onScreenIds.some((candidate) => candidate.split(/[^a-z0-9]+/).includes(key));
|
|
479
|
+
const labelEvidence = !!entry.label && onScreenLabels.has(entry.label.toLowerCase());
|
|
480
|
+
if (!declaresRoute && !idEvidence && !labelEvidence) continue;
|
|
481
|
+
let value;
|
|
482
|
+
try {
|
|
483
|
+
value = String(entry.getValue() ?? "");
|
|
484
|
+
} catch {
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
snapshot.inputs.push({
|
|
488
|
+
id,
|
|
489
|
+
label: entry.label ?? label,
|
|
490
|
+
value,
|
|
491
|
+
type: fieldType
|
|
492
|
+
});
|
|
493
|
+
present.add(key);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
172
496
|
function captureSnapshot() {
|
|
173
497
|
const root = getFiberRoot();
|
|
174
498
|
if (!root) {
|
|
@@ -201,6 +525,7 @@ function captureSnapshot() {
|
|
|
201
525
|
mergeNativeDialog(snapshot);
|
|
202
526
|
reconcileSliders(snapshot);
|
|
203
527
|
canonicalizeRegisteredFields(snapshot);
|
|
528
|
+
mergeRegisteredFieldValues(snapshot);
|
|
204
529
|
clampSnapshotToWireLimits(snapshot);
|
|
205
530
|
const entries = deriveInteractionElements(snapshot, { rectOf: rectForNode });
|
|
206
531
|
const rectById = new Map(
|
|
@@ -303,6 +628,11 @@ function flipSwitchFiber(fiber, next) {
|
|
|
303
628
|
return { ok: true, via: handler.via };
|
|
304
629
|
}
|
|
305
630
|
|
|
631
|
+
// src/introspection/positionalIdentity.ts
|
|
632
|
+
function isPositionalControlId(value) {
|
|
633
|
+
return /^(?:list-\d+-item-\d+-)?(?:btn|input|slider|toggle)-\d+$/i.test(value.trim());
|
|
634
|
+
}
|
|
635
|
+
|
|
306
636
|
// src/introspection/findInteractive.ts
|
|
307
637
|
function getTypeName2(type) {
|
|
308
638
|
if (!type) return null;
|
|
@@ -387,9 +717,17 @@ function shouldSkipSubtree2(fiber) {
|
|
|
387
717
|
}
|
|
388
718
|
return false;
|
|
389
719
|
}
|
|
720
|
+
function isSecureInput(props) {
|
|
721
|
+
return !!props.secureTextEntry || isAppilotsSensitiveMarked(props);
|
|
722
|
+
}
|
|
390
723
|
var normalize = foldForCompare;
|
|
391
724
|
function scoreMatchDetail(fiber, queryNorm, queryRaw) {
|
|
392
725
|
const props = fiber.memoizedProps ?? {};
|
|
726
|
+
if (isSourceControlId(queryRaw)) {
|
|
727
|
+
const matches = sourceControlId(fiber) === queryRaw;
|
|
728
|
+
const disabled = props.disabled === true || props.accessibilityState?.disabled === true;
|
|
729
|
+
return { score: matches ? disabled ? 60 : 120 : 0, named: matches, disabled };
|
|
730
|
+
}
|
|
393
731
|
const candidates = [];
|
|
394
732
|
if (typeof props.testID === "string") candidates.push({ val: props.testID, weight: 100 });
|
|
395
733
|
if (typeof props.accessibilityLabel === "string")
|
|
@@ -403,16 +741,22 @@ function scoreMatchDetail(fiber, queryNorm, queryRaw) {
|
|
|
403
741
|
if (childText) candidates.push({ val: childText, weight: 80 });
|
|
404
742
|
}
|
|
405
743
|
let best = 0;
|
|
744
|
+
let exact = false;
|
|
406
745
|
for (const { val, weight } of candidates) {
|
|
407
746
|
const vNorm = normalize(val);
|
|
408
747
|
if (!vNorm) continue;
|
|
409
|
-
if (val === queryRaw)
|
|
410
|
-
|
|
411
|
-
|
|
748
|
+
if (val === queryRaw) {
|
|
749
|
+
best = Math.max(best, weight + 20);
|
|
750
|
+
exact = true;
|
|
751
|
+
} else if (vNorm === queryNorm) {
|
|
752
|
+
best = Math.max(best, weight + 10);
|
|
753
|
+
exact = true;
|
|
754
|
+
} else if (!isPositionalControlId(queryRaw) && queryNorm && (vNorm.includes(queryNorm) || queryNorm.includes(vNorm)))
|
|
412
755
|
best = Math.max(best, weight - 20);
|
|
413
756
|
}
|
|
757
|
+
if (!exact && isSecureInput(props)) best = 0;
|
|
414
758
|
const named = best >= IDENTITY_MATCH_SCORE;
|
|
415
|
-
const disabledProp = props.disabled === true;
|
|
759
|
+
const disabledProp = props.disabled === true || props.editable === false || props.readOnly === true;
|
|
416
760
|
const disabledA11y = typeof props.accessibilityState === "object" && props.accessibilityState !== null && props.accessibilityState.disabled === true;
|
|
417
761
|
if (best > 0 && disabledProp) {
|
|
418
762
|
best = Math.max(1, best - 60);
|
|
@@ -422,12 +766,18 @@ function scoreMatchDetail(fiber, queryNorm, queryRaw) {
|
|
|
422
766
|
}
|
|
423
767
|
return { score: best, named, disabled: disabledProp || disabledA11y };
|
|
424
768
|
}
|
|
769
|
+
var inputIdentities = /* @__PURE__ */ new WeakMap();
|
|
425
770
|
function wrapTextInput(fiber) {
|
|
771
|
+
const identity = inputIdentities.get(fiber) ?? (fiber.alternate && inputIdentities.get(fiber.alternate)) ?? {};
|
|
772
|
+
inputIdentities.set(fiber, identity);
|
|
773
|
+
if (fiber.alternate) inputIdentities.set(fiber.alternate, identity);
|
|
426
774
|
const getProps = () => fiber.memoizedProps ?? {};
|
|
427
775
|
return {
|
|
428
776
|
kind: "field",
|
|
777
|
+
controlIdentity: identity,
|
|
429
778
|
label: getProps().accessibilityLabel ?? getProps().label ?? getProps().placeholder ?? getProps().testID,
|
|
430
779
|
getValue: () => getProps().value,
|
|
780
|
+
finishEditing: () => endTextInputEditing(getProps()),
|
|
431
781
|
setValue: (value) => {
|
|
432
782
|
const { onChangeText, onChange } = getProps();
|
|
433
783
|
if (typeof onChangeText === "function") {
|
|
@@ -508,10 +858,10 @@ function describeCandidate(fiber, kind) {
|
|
|
508
858
|
const props = fiber?.memoizedProps ?? {};
|
|
509
859
|
return props.testID ?? props.accessibilityLabel ?? props.placeholder ?? (kind === "target" ? findChildText(fiber) : void 0);
|
|
510
860
|
}
|
|
511
|
-
function isDescendantOf(fiber, ancestor) {
|
|
861
|
+
function isDescendantOf(fiber, ancestor, parents) {
|
|
512
862
|
if (!fiber || !ancestor) return false;
|
|
513
|
-
let node = fiber
|
|
514
|
-
for (let depth = 0; node && depth < 200; depth++, node = node
|
|
863
|
+
let node = parents.get(fiber);
|
|
864
|
+
for (let depth = 0; node && depth < 200; depth++, node = parents.get(node)) {
|
|
515
865
|
if (node === ancestor) return true;
|
|
516
866
|
}
|
|
517
867
|
return false;
|
|
@@ -532,13 +882,15 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
532
882
|
let bestDisabled = false;
|
|
533
883
|
let tied = [];
|
|
534
884
|
const visited = /* @__PURE__ */ new WeakSet();
|
|
885
|
+
const parents = /* @__PURE__ */ new WeakMap();
|
|
535
886
|
const stack = [];
|
|
536
|
-
if (root.child) stack.push(root.child);
|
|
887
|
+
if (root.child) stack.push({ fiber: root.child, parent: root });
|
|
537
888
|
while (stack.length > 0) {
|
|
538
|
-
const fiber = stack.pop();
|
|
889
|
+
const { fiber, parent } = stack.pop();
|
|
539
890
|
if (!fiber || visited.has(fiber)) continue;
|
|
540
891
|
visited.add(fiber);
|
|
541
|
-
|
|
892
|
+
parents.set(fiber, parent);
|
|
893
|
+
if (fiber.sibling) stack.push({ fiber: fiber.sibling, parent });
|
|
542
894
|
if (shouldSkipSubtree2(fiber)) continue;
|
|
543
895
|
const props = fiber.memoizedProps ?? {};
|
|
544
896
|
let kind = null;
|
|
@@ -552,7 +904,7 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
552
904
|
if (score > bestScore) tied = [{ fiber, kind }];
|
|
553
905
|
else if (score === bestScore) tied.push({ fiber, kind });
|
|
554
906
|
}
|
|
555
|
-
const beatsBest = score > bestScore || score > 0 && score === bestScore && isDescendantOf(fiber, bestFiber);
|
|
907
|
+
const beatsBest = score > bestScore || score > 0 && score === bestScore && isDescendantOf(fiber, bestFiber, parents);
|
|
556
908
|
if (beatsBest) {
|
|
557
909
|
bestScore = score;
|
|
558
910
|
bestFiber = fiber;
|
|
@@ -561,11 +913,15 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
561
913
|
bestId = describeCandidate(fiber, kind);
|
|
562
914
|
if (kind === "field") bestHandle = wrapTextInput(fiber);
|
|
563
915
|
else if (kind === "toggle") bestHandle = wrapSwitch(fiber);
|
|
564
|
-
else
|
|
916
|
+
else if (isSourceControlId(queryRaw)) {
|
|
917
|
+
const bound = sourceControlTarget(fiber);
|
|
918
|
+
if (!bound || bound.disabled) bestHandle = null;
|
|
919
|
+
else bestHandle = wrapTouchable(bound.fiber, findChildText(fiber));
|
|
920
|
+
} else bestHandle = wrapTouchable(fiber, findChildText(fiber));
|
|
565
921
|
}
|
|
566
922
|
}
|
|
567
923
|
if (kind === "toggle") continue;
|
|
568
|
-
if (fiber.child) stack.push(fiber.child);
|
|
924
|
+
if (fiber.child) stack.push({ fiber: fiber.child, parent: fiber });
|
|
569
925
|
}
|
|
570
926
|
if (bestHandle && bestScore < (options.minScore ?? 0)) {
|
|
571
927
|
appilotsDebugLog(
|
|
@@ -576,12 +932,14 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
576
932
|
if (bestHandle) {
|
|
577
933
|
const independent = tied.filter(
|
|
578
934
|
(candidate) => !tied.some(
|
|
579
|
-
(other) => other.fiber !== candidate.fiber && isDescendantOf(other.fiber, candidate.fiber)
|
|
935
|
+
(other) => other.fiber !== candidate.fiber && isDescendantOf(other.fiber, candidate.fiber, parents)
|
|
580
936
|
)
|
|
581
937
|
);
|
|
582
938
|
bestHandle.matchCount = independent.length;
|
|
583
939
|
bestHandle.named = bestNamed;
|
|
584
940
|
bestHandle.disabled = bestDisabled;
|
|
941
|
+
const measured = bestFiber;
|
|
942
|
+
bestHandle.measure = () => rectOfFiber(measured);
|
|
585
943
|
const candidates = independent.map(({ fiber, kind }) => describeCandidate(fiber, kind)).filter((id) => typeof id === "string" && id.length > 0).slice(0, 8);
|
|
586
944
|
if (candidates.length > 0) bestHandle.matchCandidates = candidates;
|
|
587
945
|
appilotsDebugLog(
|
|
@@ -593,6 +951,127 @@ function findInteractiveByIdentifier(query, options = {}) {
|
|
|
593
951
|
return bestHandle;
|
|
594
952
|
}
|
|
595
953
|
|
|
954
|
+
// src/introspection/spotlightTarget.ts
|
|
955
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
956
|
+
var announcementListeners = /* @__PURE__ */ new Set();
|
|
957
|
+
var current = null;
|
|
958
|
+
var seq = 0;
|
|
959
|
+
var expiry = null;
|
|
960
|
+
var SPOTLIGHT_TTL_MS = 1200;
|
|
961
|
+
var mode = "off";
|
|
962
|
+
function setSpotlightMode(next) {
|
|
963
|
+
mode = next;
|
|
964
|
+
}
|
|
965
|
+
function getSpotlightMode() {
|
|
966
|
+
return mode;
|
|
967
|
+
}
|
|
968
|
+
function emit() {
|
|
969
|
+
for (const listener of listeners) listener(current);
|
|
970
|
+
}
|
|
971
|
+
function setSpotlightTarget(rect, label, verb) {
|
|
972
|
+
if (label && verb) for (const listener of announcementListeners) listener(label, verb);
|
|
973
|
+
if (expiry) {
|
|
974
|
+
clearTimeout(expiry);
|
|
975
|
+
expiry = null;
|
|
976
|
+
}
|
|
977
|
+
if (!rect) {
|
|
978
|
+
appilotsDebugLog(
|
|
979
|
+
`setSpotlightTarget: cleared${label ? ` (was about to mark "${label}")` : ""}`
|
|
980
|
+
);
|
|
981
|
+
if (current === null) return;
|
|
982
|
+
current = null;
|
|
983
|
+
emit();
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
current = { rect, label, seq: ++seq };
|
|
987
|
+
appilotsDebugLog(
|
|
988
|
+
`setSpotlightTarget: seq=${current.seq} label="${label ?? ""}" rect=${Math.round(rect.x)},${Math.round(rect.y)} ${Math.round(rect.width)}x${Math.round(rect.height)}`
|
|
989
|
+
);
|
|
990
|
+
emit();
|
|
991
|
+
expiry = setTimeout(() => {
|
|
992
|
+
expiry = null;
|
|
993
|
+
current = null;
|
|
994
|
+
emit();
|
|
995
|
+
}, SPOTLIGHT_TTL_MS);
|
|
996
|
+
}
|
|
997
|
+
function getSpotlightTarget() {
|
|
998
|
+
return current;
|
|
999
|
+
}
|
|
1000
|
+
function subscribeSpotlight(listener) {
|
|
1001
|
+
listeners.add(listener);
|
|
1002
|
+
return () => {
|
|
1003
|
+
listeners.delete(listener);
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function subscribeSpotlightAnnouncement(listener) {
|
|
1007
|
+
announcementListeners.add(listener);
|
|
1008
|
+
return () => {
|
|
1009
|
+
announcementListeners.delete(listener);
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/introspection/scrollIntoView.ts
|
|
1014
|
+
var REVEAL_MARGIN = 24;
|
|
1015
|
+
function offsetToReveal({ rect, viewport, metrics }) {
|
|
1016
|
+
const maxOffset = Math.max(0, metrics.contentLength - metrics.visibleLength);
|
|
1017
|
+
const viewTop = viewport.top + REVEAL_MARGIN;
|
|
1018
|
+
const viewBottom = viewport.top + viewport.height - REVEAL_MARGIN;
|
|
1019
|
+
const tallerThanViewport = rect.height > viewport.height - REVEAL_MARGIN * 2;
|
|
1020
|
+
let delta;
|
|
1021
|
+
if (tallerThanViewport) {
|
|
1022
|
+
if (rect.y >= viewTop && rect.y <= viewBottom) return null;
|
|
1023
|
+
delta = rect.y - viewTop;
|
|
1024
|
+
} else if (rect.y < viewTop) {
|
|
1025
|
+
delta = rect.y - viewTop;
|
|
1026
|
+
} else if (rect.y + rect.height > viewBottom) {
|
|
1027
|
+
delta = rect.y + rect.height - viewBottom;
|
|
1028
|
+
} else {
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
const target = clamp(metrics.offset + delta, 0, maxOffset);
|
|
1032
|
+
if (Math.abs(target - metrics.offset) < 1) return null;
|
|
1033
|
+
return target;
|
|
1034
|
+
}
|
|
1035
|
+
function clamp(value, min, max) {
|
|
1036
|
+
return Math.min(max, Math.max(min, value));
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// src/introspection/revealTarget.ts
|
|
1040
|
+
function revealTarget(rect) {
|
|
1041
|
+
const host = getGeometryHost();
|
|
1042
|
+
if (!host) return { rect, scrolled: false };
|
|
1043
|
+
if (isRectOnScreen(rect, host.window)) return { rect, scrolled: false };
|
|
1044
|
+
const scrollable = resolveScrollable();
|
|
1045
|
+
if (!scrollable) return { rect, scrolled: false };
|
|
1046
|
+
const metrics = scrollable.getScrollMetrics?.();
|
|
1047
|
+
if (!metrics) return { rect, scrolled: false };
|
|
1048
|
+
const box = scrollable.measureViewport?.();
|
|
1049
|
+
const viewport = box ? { top: box.y, height: box.height } : { top: 0, height: host.window.height };
|
|
1050
|
+
const target = offsetToReveal({ rect, viewport, metrics });
|
|
1051
|
+
if (target === null) return { rect, scrolled: false };
|
|
1052
|
+
const moved = scrollable.scrollToOffset?.(target) ?? false;
|
|
1053
|
+
if (!moved) {
|
|
1054
|
+
appilotsDebugLog(`revealTarget: "${scrollable.id}" refused scrollToOffset(${target})`);
|
|
1055
|
+
return { rect, scrolled: false };
|
|
1056
|
+
}
|
|
1057
|
+
const delta = target - metrics.offset;
|
|
1058
|
+
appilotsDebugLog(
|
|
1059
|
+
`revealTarget: scrolled "${scrollable.id}" by ${Math.round(delta)}px (${Math.round(metrics.offset)} \u2192 ${Math.round(target)})`
|
|
1060
|
+
);
|
|
1061
|
+
return { rect: { ...rect, y: rect.y - delta }, scrolled: true };
|
|
1062
|
+
}
|
|
1063
|
+
function resolveScrollable() {
|
|
1064
|
+
const screen = getCurrentScreen();
|
|
1065
|
+
const candidates = listRegistry.snapshot().filter((entry) => entry.scrollToOffset && entry.getScrollMetrics).filter((entry) => !screen || !entry.screen || entry.screen === screen);
|
|
1066
|
+
if (candidates.length === 1) return candidates[0];
|
|
1067
|
+
if (candidates.length > 1) {
|
|
1068
|
+
appilotsDebugLog(
|
|
1069
|
+
`revealTarget: ${candidates.length} scrollables on "${screen ?? "?"}" \u2014 cannot tell which holds the target, so not scrolling.`
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
596
1075
|
// src/executor/handlers/navigateHandler.ts
|
|
597
1076
|
var _reportedMissingNavRef = false;
|
|
598
1077
|
function reportMissingNavigationRef() {
|
|
@@ -635,7 +1114,18 @@ function screenExistsAnywhere(state, screenName) {
|
|
|
635
1114
|
return false;
|
|
636
1115
|
}
|
|
637
1116
|
function safeNavigate(nav, screenName, params, path) {
|
|
1117
|
+
const state = nav.getState?.();
|
|
1118
|
+
const rootState = nav.getRootState?.() ?? state;
|
|
638
1119
|
if (path && path.length > 0) {
|
|
1120
|
+
if (Array.isArray(rootState?.routeNames) && !screenExistsAnywhere(rootState, path[0]) && !state?.routeNames?.includes(path[0])) {
|
|
1121
|
+
if (state?.routeNames?.includes(screenName)) {
|
|
1122
|
+
nav.navigate(screenName, params);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
throw new Error(
|
|
1126
|
+
`Route "${path[0]}" is not registered in any navigator. Cannot resolve the supplied path to "${screenName}".`
|
|
1127
|
+
);
|
|
1128
|
+
}
|
|
639
1129
|
const { root, nestedParams } = buildNestedParams(path, screenName, params);
|
|
640
1130
|
appilotsDebugLog(
|
|
641
1131
|
`safeNavigate: using path-based navigation \u2014 root="${root}", nested=${JSON.stringify(nestedParams)}`
|
|
@@ -643,12 +1133,10 @@ function safeNavigate(nav, screenName, params, path) {
|
|
|
643
1133
|
nav.navigate(root, nestedParams);
|
|
644
1134
|
return;
|
|
645
1135
|
}
|
|
646
|
-
const state = nav.getState?.();
|
|
647
1136
|
if (state?.routeNames?.includes(screenName)) {
|
|
648
1137
|
nav.navigate(screenName, params);
|
|
649
1138
|
return;
|
|
650
1139
|
}
|
|
651
|
-
const rootState = nav.getRootState?.() ?? state;
|
|
652
1140
|
if (!screenExistsAnywhere(rootState, screenName)) {
|
|
653
1141
|
throw new Error(
|
|
654
1142
|
`Screen "${screenName}" is not registered in any navigator. The screen may have been removed, may live inside a flow that is not currently mounted (e.g. Auth stack after login), or the MCP doc is stale.`
|
|
@@ -670,8 +1158,8 @@ function navigateHandler(payload, context) {
|
|
|
670
1158
|
if (!permissions.canNavigate) {
|
|
671
1159
|
return {
|
|
672
1160
|
success: false,
|
|
673
|
-
error: "Navigation
|
|
674
|
-
diagnose: { category: "unknown", screen: payload.screenName }
|
|
1161
|
+
error: permissionDeniedMessage("Navigation", "canNavigate"),
|
|
1162
|
+
diagnose: { category: "unknown", screen: payload.screenName, recoverable: false }
|
|
675
1163
|
};
|
|
676
1164
|
}
|
|
677
1165
|
const isGoBack = payload.navigationAction === "goBack";
|
|
@@ -1028,8 +1516,10 @@ function pressResolvedListItem(list, itemFiber, listIndex, itemIndex) {
|
|
|
1028
1516
|
(candidate) => candidate !== touchable && containsFiber(touchable, candidate)
|
|
1029
1517
|
);
|
|
1030
1518
|
if (primaryWrapsNestedTargets) {
|
|
1519
|
+
const outer = describeTouchable(touchable) ?? "?";
|
|
1520
|
+
const nested = safeIdentifiableTouchables.filter((candidate) => candidate !== touchable && containsFiber(touchable, candidate)).map((candidate) => describeTouchable(candidate) ?? "?").slice(0, 6);
|
|
1031
1521
|
appilotsDebugLog(
|
|
1032
|
-
`listItemDispatch: item "list-${listIndex}-item-${itemIndex}" has nested pressables; pressing outer row target`
|
|
1522
|
+
`listItemDispatch: item "list-${listIndex}-item-${itemIndex}" has nested pressables; pressing outer row target [outer=${outer}] [nested=${nested.join(", ")}] [outerHasOnPress=${typeof touchable?.memoizedProps?.onPress === "function"}]`
|
|
1033
1523
|
);
|
|
1034
1524
|
} else {
|
|
1035
1525
|
const candidates = safeIdentifiableTouchables.map(describeTouchable).filter((d) => !!d).slice(0, 8);
|
|
@@ -1096,10 +1586,12 @@ function scoreItemCandidate(item, zeroBasedIndex, locator) {
|
|
|
1096
1586
|
if (locator.itemKey) {
|
|
1097
1587
|
if (itemKey === locator.itemKey) score += 1e3;
|
|
1098
1588
|
else if (itemKey && normalize2(itemKey) === normalize2(locator.itemKey)) score += 950;
|
|
1589
|
+
else return 0;
|
|
1099
1590
|
}
|
|
1100
1591
|
if (locator.reactKey) {
|
|
1101
1592
|
if (reactKey === locator.reactKey) score += 900;
|
|
1102
1593
|
else if (reactKey && normalize2(reactKey) === normalize2(locator.reactKey)) score += 850;
|
|
1594
|
+
else if (!locator.itemKey) return 0;
|
|
1103
1595
|
}
|
|
1104
1596
|
if (locator.itemIndex && zeroBasedIndex === locator.itemIndex - 1) score += 750;
|
|
1105
1597
|
if (locator.itemIndex && markedIndex !== void 0 && (markedIndex === locator.itemIndex || markedIndex === locator.itemIndex - 1)) {
|
|
@@ -1169,7 +1661,7 @@ function resolveListItemByIdentity(locator) {
|
|
|
1169
1661
|
scoredCandidates.sort((a, b) => b.score - a.score);
|
|
1170
1662
|
const best = scoredCandidates[0];
|
|
1171
1663
|
const tiedCandidates = scoredCandidates.filter((c) => c.score === best.score);
|
|
1172
|
-
if (tiedCandidates.length > 1 && best.score < 750) {
|
|
1664
|
+
if (tiedCandidates.length > 1 && (best.score < 750 || Boolean(effective.itemKey || effective.reactKey))) {
|
|
1173
1665
|
const candidates = tiedCandidates.map((c) => describeItemCandidate(c.listIndex, c.itemIndex - 1, c.item)).slice(0, 8);
|
|
1174
1666
|
return {
|
|
1175
1667
|
ok: false,
|
|
@@ -1237,11 +1729,11 @@ function toggleListItemByIdentity(locator, options) {
|
|
|
1237
1729
|
}
|
|
1238
1730
|
const target = switches[0];
|
|
1239
1731
|
const props = target?.memoizedProps ?? {};
|
|
1240
|
-
const
|
|
1241
|
-
if (options?.expected !== void 0 &&
|
|
1242
|
-
return { ok: true, containerType: list.containerType, value:
|
|
1732
|
+
const current2 = switchValue(props);
|
|
1733
|
+
if (options?.expected !== void 0 && current2 === options.expected) {
|
|
1734
|
+
return { ok: true, containerType: list.containerType, value: current2, noop: true };
|
|
1243
1735
|
}
|
|
1244
|
-
const flipped = flipSwitchFiber(target, !
|
|
1736
|
+
const flipped = flipSwitchFiber(target, !current2);
|
|
1245
1737
|
if (!flipped.ok) {
|
|
1246
1738
|
if (flipped.reason === "no-handler") {
|
|
1247
1739
|
return {
|
|
@@ -1258,7 +1750,7 @@ function toggleListItemByIdentity(locator, options) {
|
|
|
1258
1750
|
containerType: list.containerType
|
|
1259
1751
|
};
|
|
1260
1752
|
}
|
|
1261
|
-
return { ok: true, containerType: list.containerType, value: !
|
|
1753
|
+
return { ok: true, containerType: list.containerType, value: !current2 };
|
|
1262
1754
|
}
|
|
1263
1755
|
function pressListItemByIdentity(locator) {
|
|
1264
1756
|
const resolved = resolveListItemByIdentity(locator);
|
|
@@ -1453,6 +1945,13 @@ function dispatchFiberTarget(targetId, action) {
|
|
|
1453
1945
|
diagnose: screenDiagnose(targetId)
|
|
1454
1946
|
};
|
|
1455
1947
|
}
|
|
1948
|
+
const measured = handle.measure?.() ?? null;
|
|
1949
|
+
const verb = action === "scroll" ? void 0 : action === "toggle" || action === "focus" ? action : "press";
|
|
1950
|
+
if (measured && getSpotlightMode() === "guided") {
|
|
1951
|
+
setSpotlightTarget(revealTarget(measured).rect, handle.label, verb);
|
|
1952
|
+
} else {
|
|
1953
|
+
setSpotlightTarget(measured, handle.label, verb);
|
|
1954
|
+
}
|
|
1456
1955
|
try {
|
|
1457
1956
|
if (isPressLike(action) && handle.kind === "target" && handle.press) {
|
|
1458
1957
|
handle.press();
|
|
@@ -1503,6 +2002,13 @@ function dispatchLegacyTarget(targetId, registry, action) {
|
|
|
1503
2002
|
};
|
|
1504
2003
|
}
|
|
1505
2004
|
function dispatchElement(element, registry, action) {
|
|
2005
|
+
if (element.dispatchable === false) {
|
|
2006
|
+
return {
|
|
2007
|
+
success: false,
|
|
2008
|
+
error: `Element "${element.id}" is observation-only and has no exact live control binding.`,
|
|
2009
|
+
diagnose: { ...screenDiagnose(element.id, "ambiguous-target"), recoverable: false }
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
1506
2012
|
if (element.disabled === true) {
|
|
1507
2013
|
return {
|
|
1508
2014
|
success: false,
|
|
@@ -1521,6 +2027,13 @@ function dispatchElement(element, registry, action) {
|
|
|
1521
2027
|
texts: element.texts
|
|
1522
2028
|
});
|
|
1523
2029
|
if (pressed.ok) return { success: true };
|
|
2030
|
+
if (!pressed.refusal && (element.listContext.itemKey || element.listContext.reactKey)) {
|
|
2031
|
+
return {
|
|
2032
|
+
success: false,
|
|
2033
|
+
error: pressed.error ?? `The observed list entity "${element.label ?? element.id}" could not be resolved. Reobserve; do not substitute its old position.`,
|
|
2034
|
+
diagnose: { ...screenDiagnose(element.id, "ambiguous-target"), recoverable: true }
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
1524
2037
|
if (pressed.refusal) {
|
|
1525
2038
|
return {
|
|
1526
2039
|
success: false,
|
|
@@ -1704,19 +2217,9 @@ function collectKnownFormFields(registry) {
|
|
|
1704
2217
|
}
|
|
1705
2218
|
return registryFields;
|
|
1706
2219
|
}
|
|
1707
|
-
function isMisplacedFieldId(fieldId
|
|
2220
|
+
function isMisplacedFieldId(fieldId) {
|
|
1708
2221
|
const trimmed = fieldId.trim();
|
|
1709
|
-
|
|
1710
|
-
if (LIST_ORDINAL_RE.test(trimmed)) return true;
|
|
1711
|
-
if (/^\d+$/.test(trimmed)) return true;
|
|
1712
|
-
const normalized = normalize4(trimmed);
|
|
1713
|
-
if (knownIds.has(normalized)) return false;
|
|
1714
|
-
for (const known of knownIds) {
|
|
1715
|
-
if (known.length >= 4 && (known.includes(normalized) || normalized.includes(known))) {
|
|
1716
|
-
return false;
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
return true;
|
|
2222
|
+
return !trimmed || LIST_ORDINAL_RE.test(trimmed) || /^\d+$/.test(trimmed);
|
|
1720
2223
|
}
|
|
1721
2224
|
function valueMatchesOption(field, rawValue) {
|
|
1722
2225
|
const value = normalize4(String(rawValue ?? ""));
|
|
@@ -1773,6 +2276,7 @@ function fieldTypeMatchesShape(field, shape) {
|
|
|
1773
2276
|
function fieldAcceptsFreeText(field, shape) {
|
|
1774
2277
|
const type = normalize4(field.type ?? "text");
|
|
1775
2278
|
if (CHOICE_FIELD_TYPES.has(type)) return false;
|
|
2279
|
+
if (type === "password") return false;
|
|
1776
2280
|
if (NUMERIC_FIELD_TYPES.has(type)) return shape === "number";
|
|
1777
2281
|
if (DATE_FIELD_TYPES.has(type)) return shape === "date";
|
|
1778
2282
|
if (EMAIL_FIELD_TYPES.has(type)) return shape === "email";
|
|
@@ -1793,12 +2297,11 @@ function inferFieldIdForValue(rawValue, fields, used) {
|
|
|
1793
2297
|
function rewriteMisplacedFormFillFields(fields, registry) {
|
|
1794
2298
|
const knownFields = collectKnownFormFields(registry);
|
|
1795
2299
|
if (knownFields.length === 0) return fields;
|
|
1796
|
-
const
|
|
1797
|
-
const needsRewrite = fields.some((field) => isMisplacedFieldId(field.fieldId, knownIds));
|
|
2300
|
+
const needsRewrite = fields.some((field) => isMisplacedFieldId(field.fieldId));
|
|
1798
2301
|
if (!needsRewrite) return fields;
|
|
1799
2302
|
const used = /* @__PURE__ */ new Set();
|
|
1800
2303
|
return fields.map((field) => {
|
|
1801
|
-
if (!isMisplacedFieldId(field.fieldId
|
|
2304
|
+
if (!isMisplacedFieldId(field.fieldId)) {
|
|
1802
2305
|
used.add(field.fieldId);
|
|
1803
2306
|
return field;
|
|
1804
2307
|
}
|
|
@@ -1838,12 +2341,18 @@ function normalizeText(value) {
|
|
|
1838
2341
|
function containsWord(haystack, needle) {
|
|
1839
2342
|
return ` ${haystack} `.includes(` ${needle} `);
|
|
1840
2343
|
}
|
|
1841
|
-
function readVisibleFormValidation(snap) {
|
|
2344
|
+
function readVisibleFormValidation(snap, beforeTexts) {
|
|
1842
2345
|
if (!snap) return void 0;
|
|
2346
|
+
const invalid = snap.inputs?.find((input) => input.invalid === true);
|
|
2347
|
+
const hasSubmitControl = snap.buttons?.some(
|
|
2348
|
+
(button) => SUBMIT_CONTROL_RE.test(button.label ?? button.id ?? "")
|
|
2349
|
+
);
|
|
1843
2350
|
for (const text of snap.texts ?? []) {
|
|
1844
|
-
if (!text?.trim() || !
|
|
2351
|
+
if (!text?.trim() || !isVisibleValidationText(text, { invalid: !!invalid, hasSubmitControl, beforeTexts }))
|
|
2352
|
+
continue;
|
|
1845
2353
|
return { fieldId: inferValidationFieldId(text, snap.inputs), message: text };
|
|
1846
2354
|
}
|
|
2355
|
+
if (invalid) return { fieldId: invalid.id, message: "The app marks this field as invalid." };
|
|
1847
2356
|
return void 0;
|
|
1848
2357
|
}
|
|
1849
2358
|
function readVisibleFormSuccess(snap) {
|
|
@@ -1856,15 +2365,18 @@ function submitAsyncStillPending(submitTargetId) {
|
|
|
1856
2365
|
if (submitTargetId && probe.pressedFound && probe.pressedDisabled) return true;
|
|
1857
2366
|
return false;
|
|
1858
2367
|
}
|
|
1859
|
-
function evaluatePostSubmitSnapshot(snap, activePath) {
|
|
1860
|
-
const validation = readVisibleFormValidation(snap);
|
|
2368
|
+
function evaluatePostSubmitSnapshot(snap, activePath, beforeTexts) {
|
|
2369
|
+
const validation = readVisibleFormValidation(snap, beforeTexts);
|
|
1861
2370
|
if (validation) {
|
|
1862
2371
|
return { status: "failure", fieldId: validation.fieldId, message: validation.message };
|
|
1863
2372
|
}
|
|
1864
2373
|
const successText = readVisibleFormSuccess(snap);
|
|
1865
2374
|
if (successText) return { status: "success", message: successText };
|
|
1866
2375
|
if (!snapshotShowsOpenCreateForm(snap, activePath)) {
|
|
1867
|
-
return {
|
|
2376
|
+
return {
|
|
2377
|
+
status: "unverified",
|
|
2378
|
+
message: "The form closed without a visible success receipt. Inspect the resulting record before claiming the goal is complete."
|
|
2379
|
+
};
|
|
1868
2380
|
}
|
|
1869
2381
|
return void 0;
|
|
1870
2382
|
}
|
|
@@ -1880,7 +2392,7 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
1880
2392
|
}
|
|
1881
2393
|
const snap2 = captureSnapshot();
|
|
1882
2394
|
const activePath2 = getActiveRouteNames();
|
|
1883
|
-
const outcome2 = evaluatePostSubmitSnapshot(snap2, activePath2);
|
|
2395
|
+
const outcome2 = evaluatePostSubmitSnapshot(snap2, activePath2, options?.beforeTexts);
|
|
1884
2396
|
if (outcome2) return outcome2;
|
|
1885
2397
|
await sleep(pollMs);
|
|
1886
2398
|
}
|
|
@@ -1892,7 +2404,7 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
1892
2404
|
}
|
|
1893
2405
|
const snap = captureSnapshot();
|
|
1894
2406
|
const activePath = getActiveRouteNames();
|
|
1895
|
-
const outcome = evaluatePostSubmitSnapshot(snap, activePath);
|
|
2407
|
+
const outcome = evaluatePostSubmitSnapshot(snap, activePath, options?.beforeTexts);
|
|
1896
2408
|
if (outcome) return outcome;
|
|
1897
2409
|
return {
|
|
1898
2410
|
status: "failure",
|
|
@@ -1900,91 +2412,6 @@ async function waitForPostSubmitOutcome(options) {
|
|
|
1900
2412
|
};
|
|
1901
2413
|
}
|
|
1902
2414
|
|
|
1903
|
-
// ../shared/dist/chunk-ERITSWKE.mjs
|
|
1904
|
-
var DEFAULT_FUZZY_FLOOR = 4;
|
|
1905
|
-
function normalizeId(value) {
|
|
1906
|
-
return String(value ?? "").normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1907
|
-
}
|
|
1908
|
-
var KIND_PREFIX_WORDS = "button|switch|toggle|input|field|link|btn|slider";
|
|
1909
|
-
var KEBAB_SNAKE_PREFIX_RE = new RegExp(`^(?:${KIND_PREFIX_WORDS})[-_]`, "i");
|
|
1910
|
-
var CAMEL_PREFIX_RE = new RegExp(`^(?:${KIND_PREFIX_WORDS})(?=[A-Z])`, "");
|
|
1911
|
-
function stripKindPrefix(id) {
|
|
1912
|
-
const raw = String(id ?? "");
|
|
1913
|
-
if (KEBAB_SNAKE_PREFIX_RE.test(raw)) return raw.replace(KEBAB_SNAKE_PREFIX_RE, "");
|
|
1914
|
-
if (CAMEL_PREFIX_RE.test(raw)) return raw.replace(CAMEL_PREFIX_RE, "");
|
|
1915
|
-
return raw;
|
|
1916
|
-
}
|
|
1917
|
-
function actionAllowsKind(action, kind) {
|
|
1918
|
-
if (!action || !kind) return true;
|
|
1919
|
-
switch (action) {
|
|
1920
|
-
case "press":
|
|
1921
|
-
case "longPress":
|
|
1922
|
-
return kind !== "field" && kind !== "input" && kind !== "slider";
|
|
1923
|
-
case "set_value":
|
|
1924
|
-
return kind === "slider" || kind === "field" || kind === "input";
|
|
1925
|
-
case "focus":
|
|
1926
|
-
return kind === "field" || kind === "input";
|
|
1927
|
-
case "toggle":
|
|
1928
|
-
return kind === "toggle";
|
|
1929
|
-
default:
|
|
1930
|
-
return true;
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
function distinctIds(candidates) {
|
|
1934
|
-
return [...new Set(candidates.map((c) => c.id))];
|
|
1935
|
-
}
|
|
1936
|
-
function resolveTarget(requested, candidates, opts = {}) {
|
|
1937
|
-
const floor = opts.fuzzyFloor ?? DEFAULT_FUZZY_FLOOR;
|
|
1938
|
-
const nReq = normalizeId(requested);
|
|
1939
|
-
if (!nReq || candidates.length === 0) {
|
|
1940
|
-
return { status: "unverified", reason: "not-found" };
|
|
1941
|
-
}
|
|
1942
|
-
for (const c of candidates) {
|
|
1943
|
-
if (normalizeId(c.id) === nReq) {
|
|
1944
|
-
return { status: "matched", id: c.id, tier: "exact" };
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
const labelExact = distinctIds(
|
|
1948
|
-
candidates.filter((c) => c.label && normalizeId(c.label) === nReq)
|
|
1949
|
-
);
|
|
1950
|
-
if (labelExact.length === 1) return { status: "matched", id: labelExact[0], tier: "exact" };
|
|
1951
|
-
if (labelExact.length > 1) return { status: "unverified", reason: "ambiguous" };
|
|
1952
|
-
const nReqStripped = normalizeId(stripKindPrefix(requested));
|
|
1953
|
-
for (const c of candidates) {
|
|
1954
|
-
const nId = normalizeId(c.id);
|
|
1955
|
-
const nIdStripped = normalizeId(stripKindPrefix(c.id));
|
|
1956
|
-
if (nReqStripped && (nReqStripped === nId || nReqStripped === nIdStripped) || nIdStripped && nIdStripped === nReq) {
|
|
1957
|
-
return { status: "matched", id: c.id, tier: "prefix" };
|
|
1958
|
-
}
|
|
1959
|
-
}
|
|
1960
|
-
if (opts.destructive) {
|
|
1961
|
-
return { status: "unverified", reason: "destructive-inexact" };
|
|
1962
|
-
}
|
|
1963
|
-
if (nReq.length < floor) {
|
|
1964
|
-
return { status: "unverified", reason: "not-found" };
|
|
1965
|
-
}
|
|
1966
|
-
const fuzzy = candidates.filter((c) => {
|
|
1967
|
-
for (const s of [c.id, c.label]) {
|
|
1968
|
-
if (!s) continue;
|
|
1969
|
-
const ns = normalizeId(s);
|
|
1970
|
-
if (ns.length >= floor && (ns.includes(nReq) || nReq.includes(ns))) return true;
|
|
1971
|
-
}
|
|
1972
|
-
return false;
|
|
1973
|
-
});
|
|
1974
|
-
if (fuzzy.length === 0) {
|
|
1975
|
-
return { status: "unverified", reason: "not-found" };
|
|
1976
|
-
}
|
|
1977
|
-
const typed = fuzzy.filter((c) => actionAllowsKind(opts.action, c.kind));
|
|
1978
|
-
if (typed.length === 0) {
|
|
1979
|
-
return { status: "unverified", reason: "type-mismatch" };
|
|
1980
|
-
}
|
|
1981
|
-
const ids = distinctIds(typed);
|
|
1982
|
-
if (ids.length > 1) {
|
|
1983
|
-
return { status: "unverified", reason: "ambiguous" };
|
|
1984
|
-
}
|
|
1985
|
-
return { status: "matched", id: ids[0], tier: "fuzzy" };
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
2415
|
// src/executor/handlers/uiInteractionHandler.ts
|
|
1989
2416
|
function findSnapshotButton(targetId) {
|
|
1990
2417
|
try {
|
|
@@ -2057,6 +2484,7 @@ function stripComponentPrefix(componentId) {
|
|
|
2057
2484
|
return null;
|
|
2058
2485
|
}
|
|
2059
2486
|
function resolveComponentId(componentId, registry, action) {
|
|
2487
|
+
if (isSourceControlId(componentId)) return componentId;
|
|
2060
2488
|
if (registry.getTarget(componentId) || registry.getToggle(componentId) || registry.getField(componentId) || registry.getSlider(componentId)) {
|
|
2061
2489
|
return componentId;
|
|
2062
2490
|
}
|
|
@@ -2070,6 +2498,7 @@ function resolveComponentId(componentId, registry, action) {
|
|
|
2070
2498
|
return comp.id;
|
|
2071
2499
|
}
|
|
2072
2500
|
}
|
|
2501
|
+
if (isPositionalControlId(componentId)) return componentId;
|
|
2073
2502
|
const currentScreen = getCurrentScreen();
|
|
2074
2503
|
if (currentScreen) {
|
|
2075
2504
|
const screenMeta = getScreenMetadata(currentScreen);
|
|
@@ -2315,11 +2744,14 @@ function uiInteractionHandler(payload, context) {
|
|
|
2315
2744
|
if (!permissions.canInteractUI) {
|
|
2316
2745
|
return {
|
|
2317
2746
|
success: false,
|
|
2318
|
-
error: "UI interaction
|
|
2319
|
-
diagnose:
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2747
|
+
error: permissionDeniedMessage("UI interaction", "canInteractUI"),
|
|
2748
|
+
diagnose: {
|
|
2749
|
+
...buildUiDiagnose(
|
|
2750
|
+
payload.componentId ?? payload.targetId ?? "",
|
|
2751
|
+
"unknown"
|
|
2752
|
+
),
|
|
2753
|
+
recoverable: false
|
|
2754
|
+
}
|
|
2323
2755
|
};
|
|
2324
2756
|
}
|
|
2325
2757
|
const { action } = payload;
|
|
@@ -2549,6 +2981,28 @@ function uiInteractionHandler(payload, context) {
|
|
|
2549
2981
|
};
|
|
2550
2982
|
}
|
|
2551
2983
|
|
|
2984
|
+
// src/executor/completeTextEdit.ts
|
|
2985
|
+
var sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2986
|
+
async function completeTextEdit(fieldId, expected, registered, origin, resolveRegistered) {
|
|
2987
|
+
await sleep2(80);
|
|
2988
|
+
const current2 = findInteractiveByIdentifier(fieldId, { kind: "field" });
|
|
2989
|
+
if (origin?.controlIdentity && current2?.controlIdentity !== origin.controlIdentity) return false;
|
|
2990
|
+
if (registered && resolveRegistered && resolveRegistered() !== registered) return false;
|
|
2991
|
+
if (registered?.finishEditing) registered.finishEditing();
|
|
2992
|
+
else if (current2 && (current2.matchCount ?? 1) === 1 && !current2.disabled)
|
|
2993
|
+
current2.finishEditing?.();
|
|
2994
|
+
const started = Date.now();
|
|
2995
|
+
do {
|
|
2996
|
+
await sleep2(80);
|
|
2997
|
+
const live = findInteractiveByIdentifier(fieldId, { kind: "field" });
|
|
2998
|
+
if (origin?.controlIdentity && live?.controlIdentity !== origin.controlIdentity) return false;
|
|
2999
|
+
if (registered && resolveRegistered && resolveRegistered() !== registered) return false;
|
|
3000
|
+
const value = registered ? registered.getValue() : live?.getValue?.();
|
|
3001
|
+
if (value !== void 0 && value !== null && String(value) === String(expected)) return true;
|
|
3002
|
+
} while (Date.now() - started < 480);
|
|
3003
|
+
return false;
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2552
3006
|
// src/executor/formSubmitResolve.ts
|
|
2553
3007
|
var SUBMIT_LABEL_HINT_RE = /\b(submit|save|send|continue|next|sign\s*in|log\s*in|register|salvar|enviar|entrar|cadastrar|continuar|proximo|próximo|avancar|avançar|confirmar|guardar|registrarse|iniciar\s+sesi[óo]n|enregistrer|envoyer|valider|soumettre|confirmer|se\s+connecter|s['e]\s*inscrire)\b/i;
|
|
2554
3008
|
var AGENT_CHROME_TARGET_RE = /\b(fechar|close|dismiss|cancelar|cancel|limpar|clear|chat\s+do\s+assistente|assistant\s+chat)\b/i;
|
|
@@ -2695,8 +3149,8 @@ function selectValueApplied(fieldId, requestedValue, currentValue, registry) {
|
|
|
2695
3149
|
function getMetadataField(fieldId, registry) {
|
|
2696
3150
|
const screens = /* @__PURE__ */ new Set();
|
|
2697
3151
|
for (const name of getActiveRouteNames()) screens.add(name);
|
|
2698
|
-
const
|
|
2699
|
-
if (
|
|
3152
|
+
const current2 = getCurrentScreen();
|
|
3153
|
+
if (current2) screens.add(current2);
|
|
2700
3154
|
if (registry) {
|
|
2701
3155
|
for (const comp of registry.snapshot()) {
|
|
2702
3156
|
if (comp.kind === "field" && comp.screen) screens.add(comp.screen);
|
|
@@ -2737,7 +3191,7 @@ async function tryOpenSelectModal(fieldId, registry) {
|
|
|
2737
3191
|
if (!target) continue;
|
|
2738
3192
|
try {
|
|
2739
3193
|
target.press();
|
|
2740
|
-
await
|
|
3194
|
+
await sleep3(180);
|
|
2741
3195
|
return true;
|
|
2742
3196
|
} catch {
|
|
2743
3197
|
}
|
|
@@ -3016,6 +3470,10 @@ function findField(fieldId, registry) {
|
|
|
3016
3470
|
return registry.getField(comp.id);
|
|
3017
3471
|
}
|
|
3018
3472
|
}
|
|
3473
|
+
if (findInteractiveByIdentifier(fieldId, { kind: "field", minScore: IDENTITY_MATCH_SCORE })) {
|
|
3474
|
+
appilotsDebugLog(`findField: "${fieldId}" names a live input \u2014 not guessing in the registry`);
|
|
3475
|
+
return void 0;
|
|
3476
|
+
}
|
|
3019
3477
|
const currentScreenForTieBreak = getCurrentScreen();
|
|
3020
3478
|
const needle = normalize3(fieldId);
|
|
3021
3479
|
let bestComp;
|
|
@@ -3122,7 +3580,7 @@ function findSlider(fieldId, registry) {
|
|
|
3122
3580
|
}
|
|
3123
3581
|
return void 0;
|
|
3124
3582
|
}
|
|
3125
|
-
var
|
|
3583
|
+
var sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3126
3584
|
async function formFillHandler(payload, context) {
|
|
3127
3585
|
const { permissions } = context;
|
|
3128
3586
|
const registry = context.registry ?? componentRegistry;
|
|
@@ -3131,8 +3589,8 @@ async function formFillHandler(payload, context) {
|
|
|
3131
3589
|
if (!permissions.canFillForms) {
|
|
3132
3590
|
return {
|
|
3133
3591
|
success: false,
|
|
3134
|
-
error: "Form filling
|
|
3135
|
-
diagnose: { category: "unknown", screen: screenPath }
|
|
3592
|
+
error: permissionDeniedMessage("Form filling", "canFillForms"),
|
|
3593
|
+
diagnose: { category: "unknown", screen: screenPath, recoverable: false }
|
|
3136
3594
|
};
|
|
3137
3595
|
}
|
|
3138
3596
|
if (!payload.fields || payload.fields.length === 0) {
|
|
@@ -3189,14 +3647,51 @@ async function formFillHandler(payload, context) {
|
|
|
3189
3647
|
const fieldEntry = findField(fieldId, registry);
|
|
3190
3648
|
if (fieldEntry) {
|
|
3191
3649
|
try {
|
|
3650
|
+
const nativeField = findInteractiveByIdentifier(fieldId, { kind: "field" });
|
|
3651
|
+
if (nativeField?.disabled || (nativeField?.matchCount ?? 1) > 1) {
|
|
3652
|
+
fieldResults.push({
|
|
3653
|
+
fieldId,
|
|
3654
|
+
success: false,
|
|
3655
|
+
error: nativeField?.disabled ? "The field is disabled" : "The field identifier matches multiple controls",
|
|
3656
|
+
diagnose: {
|
|
3657
|
+
category: nativeField?.disabled ? "disabled" : "ambiguous-target",
|
|
3658
|
+
fieldId,
|
|
3659
|
+
screen: screenPath
|
|
3660
|
+
}
|
|
3661
|
+
});
|
|
3662
|
+
allSuccess = false;
|
|
3663
|
+
continue;
|
|
3664
|
+
}
|
|
3192
3665
|
appilotsDebugLog(
|
|
3193
3666
|
`formFillHandler: found field "${fieldId}" via registry, calling setValue(<${String(value).length} chars>)`
|
|
3194
3667
|
);
|
|
3195
3668
|
const beforeValue = effectiveFieldType === "date" ? fieldEntry.getValue?.() ?? "" : void 0;
|
|
3196
3669
|
fieldEntry.focus?.();
|
|
3197
3670
|
fieldEntry.setValue(String(value));
|
|
3671
|
+
if (effectiveFieldType === "text" || effectiveFieldType === "number") {
|
|
3672
|
+
if (!await completeTextEdit(
|
|
3673
|
+
fieldId,
|
|
3674
|
+
value,
|
|
3675
|
+
fieldEntry,
|
|
3676
|
+
nativeField,
|
|
3677
|
+
() => findField(fieldId, registry)
|
|
3678
|
+
)) {
|
|
3679
|
+
fieldResults.push({
|
|
3680
|
+
fieldId,
|
|
3681
|
+
success: false,
|
|
3682
|
+
error: "The field did not retain the requested value after editing ended. Inspect its current value before retrying.",
|
|
3683
|
+
diagnose: { category: "validation", fieldId, screen: screenPath }
|
|
3684
|
+
});
|
|
3685
|
+
allSuccess = false;
|
|
3686
|
+
continue;
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3198
3689
|
if (effectiveFieldType === "date") {
|
|
3199
|
-
|
|
3690
|
+
let afterValue = fieldEntry.getValue?.() ?? "";
|
|
3691
|
+
if (afterValue === beforeValue) {
|
|
3692
|
+
await sleep3(80);
|
|
3693
|
+
afterValue = fieldEntry.getValue?.() ?? "";
|
|
3694
|
+
}
|
|
3200
3695
|
if (afterValue === beforeValue) {
|
|
3201
3696
|
fieldResults.push({
|
|
3202
3697
|
fieldId,
|
|
@@ -3216,7 +3711,7 @@ async function formFillHandler(payload, context) {
|
|
|
3216
3711
|
if (effectiveFieldType === "select") {
|
|
3217
3712
|
let after = fieldEntry.getValue?.() ?? "";
|
|
3218
3713
|
if (!selectValueApplied(fieldId, value, after, registry)) {
|
|
3219
|
-
await
|
|
3714
|
+
await sleep3(80);
|
|
3220
3715
|
after = fieldEntry.getValue?.() ?? "";
|
|
3221
3716
|
}
|
|
3222
3717
|
if (!selectValueApplied(fieldId, value, after, registry)) {
|
|
@@ -3337,7 +3832,7 @@ async function formFillHandler(payload, context) {
|
|
|
3337
3832
|
if (!selectResult.success && selectedChoiceThisBatch) {
|
|
3338
3833
|
const nextResult = pressContinuationButton(registry);
|
|
3339
3834
|
if (nextResult.success) {
|
|
3340
|
-
await
|
|
3835
|
+
await sleep3(180);
|
|
3341
3836
|
selectResult = await selectVisibleChoice(fieldId, value, registry, {
|
|
3342
3837
|
allowFirstVisibleWhenNoCue: true
|
|
3343
3838
|
});
|
|
@@ -3348,7 +3843,7 @@ async function formFillHandler(payload, context) {
|
|
|
3348
3843
|
appilotsDebugLog(
|
|
3349
3844
|
`formFillHandler: selected visible option for "${fieldId}"` + (selectResult.selectedLabel ? ` \u2192 "${selectResult.selectedLabel}"` : "")
|
|
3350
3845
|
);
|
|
3351
|
-
await
|
|
3846
|
+
await sleep3(120);
|
|
3352
3847
|
fieldResults.push({ fieldId, success: true });
|
|
3353
3848
|
continue;
|
|
3354
3849
|
}
|
|
@@ -3360,8 +3855,34 @@ async function formFillHandler(payload, context) {
|
|
|
3360
3855
|
});
|
|
3361
3856
|
if (handle?.setValue) {
|
|
3362
3857
|
try {
|
|
3858
|
+
if (handle.disabled || (handle.matchCount ?? 1) > 1) {
|
|
3859
|
+
fieldResults.push({
|
|
3860
|
+
fieldId,
|
|
3861
|
+
success: false,
|
|
3862
|
+
error: handle.disabled ? "The field is disabled" : "The field identifier matches multiple controls",
|
|
3863
|
+
diagnose: {
|
|
3864
|
+
category: handle.disabled ? "disabled" : "ambiguous-target",
|
|
3865
|
+
fieldId,
|
|
3866
|
+
screen: screenPath,
|
|
3867
|
+
candidates: handle.matchCandidates
|
|
3868
|
+
}
|
|
3869
|
+
});
|
|
3870
|
+
allSuccess = false;
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
setSpotlightTarget(handle.measure?.() ?? null, handle.label, "fill");
|
|
3363
3874
|
handle.focus?.();
|
|
3364
3875
|
handle.setValue(value);
|
|
3876
|
+
if (handle.kind === "field" && !await completeTextEdit(fieldId, value, void 0, handle)) {
|
|
3877
|
+
fieldResults.push({
|
|
3878
|
+
fieldId,
|
|
3879
|
+
success: false,
|
|
3880
|
+
error: "The field did not retain the requested value after editing ended. Inspect its current value before retrying.",
|
|
3881
|
+
diagnose: { category: "validation", fieldId, screen: screenPath }
|
|
3882
|
+
});
|
|
3883
|
+
allSuccess = false;
|
|
3884
|
+
continue;
|
|
3885
|
+
}
|
|
3365
3886
|
appilotsDebugLog(
|
|
3366
3887
|
`formFillHandler: fiber fallback succeeded for "${fieldId}" \u2192 label="${handle.label}"`
|
|
3367
3888
|
);
|
|
@@ -3413,10 +3934,11 @@ async function formFillHandler(payload, context) {
|
|
|
3413
3934
|
"formFillHandler: submitAfterFill=true requested but some fields failed; NOT submitting" + (failedIds.length > 0 ? ` (failed: ${failedIds.join(", ")})` : "")
|
|
3414
3935
|
);
|
|
3415
3936
|
} else {
|
|
3416
|
-
await
|
|
3937
|
+
await sleep3(120);
|
|
3417
3938
|
if (!permissions.canSubmitForms) {
|
|
3418
3939
|
return { success: false, error: "Form submission is not permitted", fieldResults };
|
|
3419
3940
|
}
|
|
3941
|
+
const beforeSubmitTexts = captureSnapshot().texts;
|
|
3420
3942
|
const submitResult = pressSubmitButton(
|
|
3421
3943
|
{
|
|
3422
3944
|
screen,
|
|
@@ -3436,7 +3958,8 @@ async function formFillHandler(payload, context) {
|
|
|
3436
3958
|
};
|
|
3437
3959
|
}
|
|
3438
3960
|
const postSubmit = await waitForPostSubmitOutcome({
|
|
3439
|
-
submitTargetId: submitResult.pressedTargetId
|
|
3961
|
+
submitTargetId: submitResult.pressedTargetId,
|
|
3962
|
+
beforeTexts: beforeSubmitTexts
|
|
3440
3963
|
});
|
|
3441
3964
|
if (postSubmit.status === "pending") {
|
|
3442
3965
|
appilotsDebugWarn(`formFillHandler: submit still pending \u2014 ${postSubmit.message}`);
|
|
@@ -3463,6 +3986,9 @@ async function formFillHandler(payload, context) {
|
|
|
3463
3986
|
}
|
|
3464
3987
|
};
|
|
3465
3988
|
}
|
|
3989
|
+
if (postSubmit.status === "unverified") {
|
|
3990
|
+
return { success: true, fieldResults, effect: "unknown" };
|
|
3991
|
+
}
|
|
3466
3992
|
appilotsDebugLog("formFillHandler: submitAfterFill \u2014 submit completed");
|
|
3467
3993
|
}
|
|
3468
3994
|
}
|
|
@@ -3600,7 +4126,21 @@ function aggregateFieldDiagnoses(results, screen) {
|
|
|
3600
4126
|
var FALLBACK_PAGE_PX = 600;
|
|
3601
4127
|
var PAGE_FRACTION = 0.85;
|
|
3602
4128
|
var POST_SCROLL_MOUNT_MS = 400;
|
|
3603
|
-
var
|
|
4129
|
+
var PAGINATION_MAX_WAIT_MS = 4e3;
|
|
4130
|
+
var PAGINATION_POLL_MS = 150;
|
|
4131
|
+
var sleep4 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
4132
|
+
async function settleAfterScroll() {
|
|
4133
|
+
await sleep4(POST_SCROLL_MOUNT_MS);
|
|
4134
|
+
if (!probeLoadingState(void 0, getCurrentScreen()).loading) return;
|
|
4135
|
+
const deadline = Date.now() + PAGINATION_MAX_WAIT_MS;
|
|
4136
|
+
while (Date.now() < deadline) {
|
|
4137
|
+
await sleep4(PAGINATION_POLL_MS);
|
|
4138
|
+
if (!probeLoadingState(void 0, getCurrentScreen()).loading) {
|
|
4139
|
+
await sleep4(POST_SCROLL_MOUNT_MS);
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
3604
4144
|
function normalizeId2(value) {
|
|
3605
4145
|
return String(value ?? "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "");
|
|
3606
4146
|
}
|
|
@@ -3636,13 +4176,26 @@ function pageOffset(metrics, direction) {
|
|
|
3636
4176
|
}
|
|
3637
4177
|
return next;
|
|
3638
4178
|
}
|
|
4179
|
+
async function finishScroll(entry, before) {
|
|
4180
|
+
await settleAfterScroll();
|
|
4181
|
+
const current2 = listRegistry.get(entry.id) ?? entry;
|
|
4182
|
+
const after = current2.getScrollMetrics?.();
|
|
4183
|
+
const grew = typeof entry.itemCount === "number" && typeof current2.itemCount === "number" && current2.itemCount > entry.itemCount;
|
|
4184
|
+
const moved = grew || !!(before && after && (Math.abs(after.offset - before.offset) > 2 || after.contentLength > before.contentLength + 2));
|
|
4185
|
+
const outcome = moved ? "moved" : before && after ? "no-progress" : "unverified";
|
|
4186
|
+
current2.exploration?.recordScroll(outcome);
|
|
4187
|
+
return {
|
|
4188
|
+
success: true,
|
|
4189
|
+
effect: moved ? "changed" : outcome === "no-progress" ? "none" : "unknown"
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
3639
4192
|
async function scrollListHandler(payload, context) {
|
|
3640
4193
|
const { permissions } = context;
|
|
3641
4194
|
if (!permissions.canInteractUI) {
|
|
3642
4195
|
return {
|
|
3643
4196
|
success: false,
|
|
3644
|
-
error: "UI interaction
|
|
3645
|
-
diagnose: buildDiagnose("unknown", payload.listId)
|
|
4197
|
+
error: permissionDeniedMessage("UI interaction", "canInteractUI"),
|
|
4198
|
+
diagnose: { ...buildDiagnose("unknown", payload.listId), recoverable: false }
|
|
3646
4199
|
};
|
|
3647
4200
|
}
|
|
3648
4201
|
const toIndex = typeof payload.toIndex === "number" ? payload.toIndex : void 0;
|
|
@@ -3670,6 +4223,15 @@ async function scrollListHandler(payload, context) {
|
|
|
3670
4223
|
diagnose: buildDiagnose("component-not-found", payload.listId, candidates)
|
|
3671
4224
|
};
|
|
3672
4225
|
}
|
|
4226
|
+
const exploration = entry.exploration?.snapshot();
|
|
4227
|
+
if (exploration && (exploration.remainingScrollSteps === 0 || exploration.consecutiveNoProgress >= 2)) {
|
|
4228
|
+
return {
|
|
4229
|
+
success: false,
|
|
4230
|
+
effect: "none",
|
|
4231
|
+
error: "List exploration stopped: scroll budget exhausted or repeated no progress. Use the current loaded count/coverage and report any remaining uncertainty; do not repeat this scroll.",
|
|
4232
|
+
diagnose: { ...buildDiagnose("validation", entry.id), recoverable: false }
|
|
4233
|
+
};
|
|
4234
|
+
}
|
|
3673
4235
|
if (toIndex !== void 0) {
|
|
3674
4236
|
if (entry.kind === "scroll") {
|
|
3675
4237
|
return {
|
|
@@ -3685,17 +4247,16 @@ async function scrollListHandler(payload, context) {
|
|
|
3685
4247
|
diagnose: buildDiagnose("validation", entry.id)
|
|
3686
4248
|
};
|
|
3687
4249
|
}
|
|
4250
|
+
const before = entry.getScrollMetrics?.();
|
|
3688
4251
|
const zeroBased = toIndex - 1;
|
|
3689
4252
|
if (entry.scrollToIndex?.(zeroBased)) {
|
|
3690
|
-
|
|
3691
|
-
return { success: true };
|
|
4253
|
+
return finishScroll(entry, before);
|
|
3692
4254
|
}
|
|
3693
4255
|
const metrics2 = entry.getScrollMetrics?.();
|
|
3694
4256
|
if (metrics2 && metrics2.contentLength > 0 && typeof entry.itemCount === "number" && entry.itemCount > 0) {
|
|
3695
4257
|
const estimated = zeroBased / entry.itemCount * metrics2.contentLength;
|
|
3696
4258
|
if (entry.scrollToOffset?.(Math.max(0, estimated))) {
|
|
3697
|
-
|
|
3698
|
-
return { success: true };
|
|
4259
|
+
return finishScroll(entry, before);
|
|
3699
4260
|
}
|
|
3700
4261
|
}
|
|
3701
4262
|
return {
|
|
@@ -3705,10 +4266,21 @@ async function scrollListHandler(payload, context) {
|
|
|
3705
4266
|
};
|
|
3706
4267
|
}
|
|
3707
4268
|
const metrics = entry.getScrollMetrics?.();
|
|
4269
|
+
if (!metrics && exploration?.lastScroll === "unverified") {
|
|
4270
|
+
return {
|
|
4271
|
+
success: false,
|
|
4272
|
+
effect: "unknown",
|
|
4273
|
+
error: "Scroll offset is still unavailable after one probe. Cannot safely page again; do not infer the end or repeat the same absolute offset.",
|
|
4274
|
+
diagnose: { ...buildDiagnose("validation", entry.id), recoverable: false }
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
3708
4277
|
const target = pageOffset(metrics, direction);
|
|
4278
|
+
if (metrics && Math.abs(target - metrics.offset) <= 2) {
|
|
4279
|
+
entry.exploration?.recordScroll("boundary");
|
|
4280
|
+
return { success: true, effect: "none" };
|
|
4281
|
+
}
|
|
3709
4282
|
if (entry.scrollToOffset?.(target)) {
|
|
3710
|
-
|
|
3711
|
-
return { success: true };
|
|
4283
|
+
return finishScroll(entry, metrics);
|
|
3712
4284
|
}
|
|
3713
4285
|
return {
|
|
3714
4286
|
success: false,
|
|
@@ -3792,7 +4364,7 @@ function hashFieldValue(value) {
|
|
|
3792
4364
|
}
|
|
3793
4365
|
return (hash >>> 0).toString(36);
|
|
3794
4366
|
}
|
|
3795
|
-
var DISCRETE_FIELD_TYPES = /* @__PURE__ */ new Set(["select", "date"]);
|
|
4367
|
+
var DISCRETE_FIELD_TYPES = /* @__PURE__ */ new Set(["select", "date", "number"]);
|
|
3796
4368
|
function discreteFieldSignature() {
|
|
3797
4369
|
const sigs = [];
|
|
3798
4370
|
for (const { id, kind } of componentRegistry.snapshot()) {
|
|
@@ -3806,12 +4378,23 @@ function discreteFieldSignature() {
|
|
|
3806
4378
|
}
|
|
3807
4379
|
return sigs.sort().join(",");
|
|
3808
4380
|
}
|
|
3809
|
-
|
|
4381
|
+
function capturePreInteractionBaseline(payload) {
|
|
4382
|
+
const targetId = targetIdFromPayload(payload);
|
|
4383
|
+
const screen = getCurrentScreen();
|
|
4384
|
+
const probe = probeLoadingState(targetId || null, screen);
|
|
4385
|
+
return {
|
|
4386
|
+
fingerprint: probe.fingerprint,
|
|
4387
|
+
fields: discreteFieldSignature(),
|
|
4388
|
+
screen,
|
|
4389
|
+
readable: probe.fingerprint !== ""
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
async function waitForPostInteractionSettle(payload, preBaseline) {
|
|
3810
4393
|
const targetId = targetIdFromPayload(payload);
|
|
3811
|
-
const
|
|
3812
|
-
const
|
|
3813
|
-
const baselineFields =
|
|
3814
|
-
const readable = baseline.
|
|
4394
|
+
const baseline = preBaseline ?? capturePreInteractionBaseline(payload);
|
|
4395
|
+
const fromScreen = baseline.screen;
|
|
4396
|
+
const baselineFields = baseline.fields;
|
|
4397
|
+
const readable = baseline.readable;
|
|
3815
4398
|
await sleepMs(100);
|
|
3816
4399
|
const afterPress = probeLoadingState(targetId || null, getCurrentScreen());
|
|
3817
4400
|
const modalOrLoading = afterPress.modalOpen || afterPress.loading || afterPress.pressedDisabled;
|
|
@@ -3843,12 +4426,6 @@ async function waitForPostInteractionSettle(payload) {
|
|
|
3843
4426
|
}
|
|
3844
4427
|
|
|
3845
4428
|
// src/executor/ActionExecutor.ts
|
|
3846
|
-
var DEFAULT_PERMISSIONS = {
|
|
3847
|
-
canNavigate: true,
|
|
3848
|
-
canFillForms: true,
|
|
3849
|
-
canInteractUI: true,
|
|
3850
|
-
canSubmitForms: true
|
|
3851
|
-
};
|
|
3852
4429
|
function validateActionPayload(action) {
|
|
3853
4430
|
const invalid = (message, ids) => ({
|
|
3854
4431
|
success: false,
|
|
@@ -3919,7 +4496,7 @@ async function executeAction(action, context) {
|
|
|
3919
4496
|
appilotsDebugLog(
|
|
3920
4497
|
`executeAction called: type="${action.type}", id="${action.id}", payload=${JSON.stringify(action.payload)}`
|
|
3921
4498
|
);
|
|
3922
|
-
const permissions = context.permissions
|
|
4499
|
+
const permissions = resolveAgentPermissions(context.permissions);
|
|
3923
4500
|
context.emit({
|
|
3924
4501
|
type: "agent:action:start",
|
|
3925
4502
|
timestamp: Date.now(),
|
|
@@ -3977,12 +4554,13 @@ async function executeAction(action, context) {
|
|
|
3977
4554
|
break;
|
|
3978
4555
|
case "ui_interaction": {
|
|
3979
4556
|
const payload = action.payload;
|
|
4557
|
+
const baseline = capturePreInteractionBaseline(payload);
|
|
3980
4558
|
result = uiInteractionHandler(payload, {
|
|
3981
4559
|
permissions,
|
|
3982
4560
|
registry: context.registry ?? getDefaultRegistry()
|
|
3983
4561
|
});
|
|
3984
4562
|
if (result.success) {
|
|
3985
|
-
const settle = await waitForPostInteractionSettle(payload);
|
|
4563
|
+
const settle = await waitForPostInteractionSettle(payload, baseline);
|
|
3986
4564
|
result.effect = result.effect ?? effectFor(payload, settle.effect);
|
|
3987
4565
|
}
|
|
3988
4566
|
break;
|
|
@@ -4070,18 +4648,28 @@ var _patchedAlert = (title, message, buttons, options) => {
|
|
|
4070
4648
|
return suppression.originalAlert(title, message, buttons, options);
|
|
4071
4649
|
}
|
|
4072
4650
|
suppression.consumed = true;
|
|
4651
|
+
const report = (handlerCompleted) => suppression.onHandled?.({
|
|
4652
|
+
title: title.slice(0, 300),
|
|
4653
|
+
message: message?.slice(0, 1e3),
|
|
4654
|
+
buttonLabel: button.text?.slice(0, 160),
|
|
4655
|
+
handlerCompleted
|
|
4656
|
+
});
|
|
4073
4657
|
appilotsDebugLog("Suppressing native Alert during already-confirmed destructive agent action");
|
|
4074
4658
|
try {
|
|
4075
|
-
suppression.pending = Promise.resolve(button.onPress()).then(() =>
|
|
4659
|
+
suppression.pending = Promise.resolve(button.onPress()).then(() => report(true)).catch((err) => {
|
|
4660
|
+
reportHandlerFailure(err);
|
|
4661
|
+
report(false);
|
|
4662
|
+
});
|
|
4076
4663
|
} catch (err) {
|
|
4077
4664
|
reportHandlerFailure(err);
|
|
4078
4665
|
suppression.pending = null;
|
|
4666
|
+
report(false);
|
|
4079
4667
|
}
|
|
4080
4668
|
};
|
|
4081
4669
|
function reportHandlerFailure(err) {
|
|
4082
4670
|
console.error("[Appilots] The app\u2019s Alert handler threw while being auto-confirmed", err);
|
|
4083
4671
|
}
|
|
4084
|
-
async function runWithConfirmedDestructiveContext(enabled, host, fn) {
|
|
4672
|
+
async function runWithConfirmedDestructiveContext(enabled, host, fn, onHandled) {
|
|
4085
4673
|
if (!enabled) return fn();
|
|
4086
4674
|
if (_active) {
|
|
4087
4675
|
_active.depth += 1;
|
|
@@ -4102,6 +4690,7 @@ async function runWithConfirmedDestructiveContext(enabled, host, fn) {
|
|
|
4102
4690
|
depth: 1,
|
|
4103
4691
|
consumed: false,
|
|
4104
4692
|
pending: null,
|
|
4693
|
+
onHandled,
|
|
4105
4694
|
timer: null
|
|
4106
4695
|
};
|
|
4107
4696
|
_active = suppression;
|
|
@@ -4135,9 +4724,14 @@ function resolveScreenMetadata() {
|
|
|
4135
4724
|
return screen ? getScreenMetadata(screen) : void 0;
|
|
4136
4725
|
}
|
|
4137
4726
|
function buildAgentContext(extras = {}) {
|
|
4138
|
-
if (extras.newMission === true)
|
|
4727
|
+
if (extras.newMission === true) {
|
|
4728
|
+
deltaTracker.reset();
|
|
4729
|
+
for (const entry of listRegistry.snapshot()) entry.exploration?.reset();
|
|
4730
|
+
}
|
|
4139
4731
|
const context = {};
|
|
4140
4732
|
context.platform = "react-native";
|
|
4733
|
+
context.missionProtocol = 1;
|
|
4734
|
+
context.deviceLocale = detectDeviceLocale() ?? "en";
|
|
4141
4735
|
const screen = getCurrentScreen();
|
|
4142
4736
|
if (screen) context.currentScreen = screen;
|
|
4143
4737
|
const navigationState = getNavigationStateSnapshot();
|
|
@@ -4146,6 +4740,12 @@ function buildAgentContext(extras = {}) {
|
|
|
4146
4740
|
if (screenMeta) context.screenMetadata = screenMeta;
|
|
4147
4741
|
try {
|
|
4148
4742
|
const snapshot = captureSnapshot();
|
|
4743
|
+
for (const list of snapshot.lists) {
|
|
4744
|
+
const memory = list.id ? listRegistry.get(list.id)?.exploration : void 0;
|
|
4745
|
+
if (!memory) continue;
|
|
4746
|
+
memory.observe(list.items);
|
|
4747
|
+
list.exploration = memory.snapshot();
|
|
4748
|
+
}
|
|
4149
4749
|
const delta = deltaTracker.observe(snapshot);
|
|
4150
4750
|
if (delta) snapshot.delta = delta;
|
|
4151
4751
|
context.snapshot = snapshot;
|
|
@@ -4157,9 +4757,20 @@ function buildAgentContext(extras = {}) {
|
|
|
4157
4757
|
if (filtered.length > 0) context.registeredComponents = filtered;
|
|
4158
4758
|
const registeredLists = listRegistry.snapshot();
|
|
4159
4759
|
if (registeredLists.length > 0) {
|
|
4160
|
-
|
|
4161
|
-
|
|
4760
|
+
const snapshot = context.snapshot;
|
|
4761
|
+
const activeIds = new Set(
|
|
4762
|
+
[...snapshot?.lists ?? [], ...snapshot?.scrollables ?? []].map((l) => l.id)
|
|
4162
4763
|
);
|
|
4764
|
+
context.registeredLists = registeredLists.filter((entry) => activeIds.has(entry.id)).map(({ id, kind, component, itemCount, totalItemCount, label, refreshing, empty }) => ({
|
|
4765
|
+
id,
|
|
4766
|
+
kind,
|
|
4767
|
+
component,
|
|
4768
|
+
itemCount,
|
|
4769
|
+
totalItemCount,
|
|
4770
|
+
label,
|
|
4771
|
+
refreshing,
|
|
4772
|
+
empty
|
|
4773
|
+
}));
|
|
4163
4774
|
}
|
|
4164
4775
|
if (extras.loadingPending === true) context.loadingPending = true;
|
|
4165
4776
|
return context;
|
|
@@ -4169,6 +4780,7 @@ async function settleTurn(input) {
|
|
|
4169
4780
|
let loadingPending = false;
|
|
4170
4781
|
const { turnActions, pressedComponentId: pressedId, navigateTargetScreen } = input;
|
|
4171
4782
|
const successById = new Map(input.results.map((r) => [r.actionId, r.success]));
|
|
4783
|
+
const effectById = new Map(input.results.map((r) => [r.actionId, r.effect]));
|
|
4172
4784
|
const typeById = new Map(input.results.map((r) => [r.actionId, r.type]));
|
|
4173
4785
|
const pressedAction = turnActions.find(
|
|
4174
4786
|
(a) => a.type === "ui_interaction" && a.payload?.componentId === pressedId
|
|
@@ -4244,6 +4856,7 @@ async function settleTurn(input) {
|
|
|
4244
4856
|
);
|
|
4245
4857
|
if (!postPressSettle.transitioned && baselineFingerprint === afterFingerprint) {
|
|
4246
4858
|
for (const action of listPressActions) {
|
|
4859
|
+
if (effectById.get(action.id) === "changed") continue;
|
|
4247
4860
|
if (successById.get(action.id)) {
|
|
4248
4861
|
toolWithoutEffectIds.add(action.id);
|
|
4249
4862
|
}
|
|
@@ -4261,18 +4874,40 @@ var reactNativeChatAdapter = {
|
|
|
4261
4874
|
};
|
|
4262
4875
|
function createReactNativeActionRunner(getOptions) {
|
|
4263
4876
|
return {
|
|
4877
|
+
buildMissionContext: () => reactNativeChatAdapter.buildContext(),
|
|
4264
4878
|
async execute(action, { confirmedDestructive }) {
|
|
4265
|
-
const { permissions, emit, navigationRef, suppressNativeConfirm } = getOptions();
|
|
4879
|
+
const { permissions, emit: emit2, navigationRef, suppressNativeConfirm } = getOptions();
|
|
4266
4880
|
const navRef = navigationRef?.current ? navigationRef : { current: getNavigationRef() };
|
|
4267
|
-
|
|
4881
|
+
let receipt;
|
|
4882
|
+
let terminal;
|
|
4883
|
+
const result = await runWithConfirmedDestructiveContext(
|
|
4268
4884
|
confirmedDestructive && suppressNativeConfirm !== false,
|
|
4269
4885
|
Alert,
|
|
4270
4886
|
() => executeAction(action, {
|
|
4271
4887
|
navigationRef: navRef,
|
|
4272
4888
|
permissions,
|
|
4273
|
-
emit
|
|
4274
|
-
|
|
4889
|
+
emit: (event) => {
|
|
4890
|
+
if (confirmedDestructive && (event.type === "agent:action:complete" || event.type === "agent:action:error"))
|
|
4891
|
+
terminal = event;
|
|
4892
|
+
else emit2(event);
|
|
4893
|
+
}
|
|
4894
|
+
}),
|
|
4895
|
+
(handled) => {
|
|
4896
|
+
receipt = handled;
|
|
4897
|
+
}
|
|
4275
4898
|
);
|
|
4899
|
+
const failed = receipt?.handlerCompleted === false;
|
|
4900
|
+
if (terminal)
|
|
4901
|
+
emit2({
|
|
4902
|
+
...terminal,
|
|
4903
|
+
type: failed ? "agent:action:error" : terminal.type,
|
|
4904
|
+
data: {
|
|
4905
|
+
...terminal.data,
|
|
4906
|
+
...receipt ? { nativeConfirmation: receipt } : {},
|
|
4907
|
+
...failed ? { success: false, error: "Native confirmation callback failed" } : {}
|
|
4908
|
+
}
|
|
4909
|
+
});
|
|
4910
|
+
return failed ? { ...result, success: false, error: "Native confirmation callback failed" } : result;
|
|
4276
4911
|
},
|
|
4277
4912
|
getCurrentScreen,
|
|
4278
4913
|
getCurrentScreenSignature,
|
|
@@ -4285,16 +4920,16 @@ function createReactNativeActionRunner(getOptions) {
|
|
|
4285
4920
|
|
|
4286
4921
|
// src/hooks/useAppilotsChat.ts
|
|
4287
4922
|
function useAppilotsChat(options = {}) {
|
|
4288
|
-
const { client, emit, subscribe } = useAppilotsContext();
|
|
4923
|
+
const { client, emit: emit2, subscribe } = useAppilotsContext();
|
|
4289
4924
|
const machine = useMemo(
|
|
4290
4925
|
() => new ChatSessionMachine(
|
|
4291
|
-
{ client, adapter: reactNativeChatAdapter, emit, warn: appilotsDebugWarn },
|
|
4926
|
+
{ client, adapter: reactNativeChatAdapter, emit: emit2, warn: appilotsDebugWarn },
|
|
4292
4927
|
options
|
|
4293
4928
|
),
|
|
4294
4929
|
// A new machine per client/provider instance — options are pushed in
|
|
4295
4930
|
// below so changing copy never resets the transcript.
|
|
4296
4931
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4297
|
-
[client,
|
|
4932
|
+
[client, emit2]
|
|
4298
4933
|
);
|
|
4299
4934
|
const optionsRef = useRef(options);
|
|
4300
4935
|
optionsRef.current = options;
|
|
@@ -4302,6 +4937,14 @@ function useAppilotsChat(options = {}) {
|
|
|
4302
4937
|
const state = useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
|
|
4303
4938
|
useEffect(() => subscribe(machine.handleEvent), [subscribe, machine]);
|
|
4304
4939
|
useEffect(() => () => machine.dispose(), [machine]);
|
|
4940
|
+
useEffect(() => {
|
|
4941
|
+
void machine.restoreMission();
|
|
4942
|
+
const subscription = AppState?.addEventListener?.("change", (next) => {
|
|
4943
|
+
if (next !== "active" && machine.getState().mission?.status === "running")
|
|
4944
|
+
void machine.pauseMission();
|
|
4945
|
+
});
|
|
4946
|
+
return () => subscription?.remove();
|
|
4947
|
+
}, [machine]);
|
|
4305
4948
|
const requestHuman = useCallback(
|
|
4306
4949
|
(reason) => machine.requestHuman(reason),
|
|
4307
4950
|
[machine]
|
|
@@ -4311,7 +4954,13 @@ function useAppilotsChat(options = {}) {
|
|
|
4311
4954
|
const clearMessages = useCallback(() => machine.clearMessages(), [machine]);
|
|
4312
4955
|
const clearError = useCallback(() => machine.clearError(), [machine]);
|
|
4313
4956
|
return {
|
|
4957
|
+
mission: state.mission,
|
|
4958
|
+
pauseMission: () => machine.pauseMission(),
|
|
4959
|
+
resumeMission: () => machine.resumeMission(),
|
|
4960
|
+
cancelMission: () => machine.cancelMission(),
|
|
4961
|
+
hasStartedActing: state.hasStartedActing,
|
|
4314
4962
|
messages: state.messages,
|
|
4963
|
+
replyLocale: state.replyLocale,
|
|
4315
4964
|
isLoading: state.isLoading,
|
|
4316
4965
|
loadingStatusKey: state.loadingStatusKey,
|
|
4317
4966
|
error: state.error,
|
|
@@ -4324,24 +4973,18 @@ function useAppilotsChat(options = {}) {
|
|
|
4324
4973
|
clearError
|
|
4325
4974
|
};
|
|
4326
4975
|
}
|
|
4327
|
-
var DEFAULT_PERMISSIONS2 = {
|
|
4328
|
-
canNavigate: true,
|
|
4329
|
-
canFillForms: true,
|
|
4330
|
-
canInteractUI: true,
|
|
4331
|
-
canSubmitForms: true
|
|
4332
|
-
};
|
|
4333
4976
|
function useAppilotsActions(options = {}) {
|
|
4334
|
-
const { client, subscribe, config, emit } = useAppilotsContext();
|
|
4977
|
+
const { client, subscribe, config, emit: emit2 } = useAppilotsContext();
|
|
4335
4978
|
const { navigationRef, autoExecute = false } = options;
|
|
4336
4979
|
const runnerOptionsRef = useRef({
|
|
4337
|
-
permissions: config.permissions
|
|
4338
|
-
emit,
|
|
4980
|
+
permissions: resolveAgentPermissions(config.permissions),
|
|
4981
|
+
emit: emit2,
|
|
4339
4982
|
navigationRef,
|
|
4340
4983
|
suppressNativeConfirm: config.suppressNativeConfirm
|
|
4341
4984
|
});
|
|
4342
4985
|
runnerOptionsRef.current = {
|
|
4343
|
-
permissions: config.permissions
|
|
4344
|
-
emit,
|
|
4986
|
+
permissions: resolveAgentPermissions(config.permissions),
|
|
4987
|
+
emit: emit2,
|
|
4345
4988
|
navigationRef,
|
|
4346
4989
|
suppressNativeConfirm: config.suppressNativeConfirm
|
|
4347
4990
|
};
|
|
@@ -4350,13 +4993,13 @@ function useAppilotsActions(options = {}) {
|
|
|
4350
4993
|
{
|
|
4351
4994
|
client,
|
|
4352
4995
|
adapter: createReactNativeActionRunner(() => runnerOptionsRef.current),
|
|
4353
|
-
emit,
|
|
4996
|
+
emit: emit2,
|
|
4354
4997
|
warn: appilotsDebugWarn
|
|
4355
4998
|
},
|
|
4356
4999
|
{ autoExecute }
|
|
4357
5000
|
),
|
|
4358
5001
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4359
|
-
[client,
|
|
5002
|
+
[client, emit2]
|
|
4360
5003
|
);
|
|
4361
5004
|
machine.setOptions({ autoExecute });
|
|
4362
5005
|
const state = useSyncExternalStore(machine.subscribeState, machine.getState, machine.getState);
|
|
@@ -4446,9 +5089,9 @@ function useSuggestedPrompts(options) {
|
|
|
4446
5089
|
if (!enabled) return;
|
|
4447
5090
|
let lastScreen = getCurrentScreen();
|
|
4448
5091
|
const interval = setInterval(() => {
|
|
4449
|
-
const
|
|
4450
|
-
if (
|
|
4451
|
-
lastScreen =
|
|
5092
|
+
const current2 = getCurrentScreen();
|
|
5093
|
+
if (current2 !== lastScreen) {
|
|
5094
|
+
lastScreen = current2;
|
|
4452
5095
|
setRefreshTick((t) => t + 1);
|
|
4453
5096
|
}
|
|
4454
5097
|
}, 800);
|
|
@@ -4461,7 +5104,7 @@ function useSuggestedPrompts(options) {
|
|
|
4461
5104
|
};
|
|
4462
5105
|
}
|
|
4463
5106
|
function useAppilotsNavigation() {
|
|
4464
|
-
const { emit } = useAppilotsContext();
|
|
5107
|
+
const { emit: emit2 } = useAppilotsContext();
|
|
4465
5108
|
const [currentScreen, setCurrentScreenState] = useState(null);
|
|
4466
5109
|
const [navigationHistory, setNavigationHistory] = useState([]);
|
|
4467
5110
|
const navigationRef = useRef(null);
|
|
@@ -4470,13 +5113,13 @@ function useAppilotsNavigation() {
|
|
|
4470
5113
|
setCurrentScreenState(screenName);
|
|
4471
5114
|
setCurrentScreen(screenName);
|
|
4472
5115
|
setNavigationHistory((prev) => [...prev, screenName]);
|
|
4473
|
-
|
|
5116
|
+
emit2({
|
|
4474
5117
|
type: "navigation:change",
|
|
4475
5118
|
timestamp: Date.now(),
|
|
4476
5119
|
data: { screenName }
|
|
4477
5120
|
});
|
|
4478
5121
|
},
|
|
4479
|
-
[
|
|
5122
|
+
[emit2]
|
|
4480
5123
|
);
|
|
4481
5124
|
useEffect(() => {
|
|
4482
5125
|
const nav = navigationRef.current;
|
|
@@ -4526,6 +5169,11 @@ function useAppilots() {
|
|
|
4526
5169
|
rejectAction: actions.rejectAction
|
|
4527
5170
|
};
|
|
4528
5171
|
}
|
|
5172
|
+
function useAppilotsElementProps(id) {
|
|
5173
|
+
return useMemo(() => ({ testID: id }), [id]);
|
|
5174
|
+
}
|
|
5175
|
+
|
|
5176
|
+
// src/hooks/useAppilotsField.ts
|
|
4529
5177
|
function useAppilotsField(id, options) {
|
|
4530
5178
|
const { value, onChangeText, ref, fieldType, label, screen } = options;
|
|
4531
5179
|
const registry = useResolvedRegistry();
|
|
@@ -4553,6 +5201,7 @@ function useAppilotsField(id, options) {
|
|
|
4553
5201
|
registry.unregister(id);
|
|
4554
5202
|
};
|
|
4555
5203
|
}, [id, registry, getValue, setValue, focus, ref, fieldType, label, screen]);
|
|
5204
|
+
return useAppilotsElementProps(id);
|
|
4556
5205
|
}
|
|
4557
5206
|
function useAppilotsTarget(id, options) {
|
|
4558
5207
|
const { onPress, onLongPress, onScrollTo, label, screen } = options;
|
|
@@ -4580,6 +5229,7 @@ function useAppilotsTarget(id, options) {
|
|
|
4580
5229
|
registry.unregister(id);
|
|
4581
5230
|
};
|
|
4582
5231
|
}, [id, registry, press, longPress, scrollTo, onLongPress, onScrollTo, label, screen]);
|
|
5232
|
+
return useAppilotsElementProps(id);
|
|
4583
5233
|
}
|
|
4584
5234
|
function useAppilotsToggle(id, options) {
|
|
4585
5235
|
const { value, onValueChange, label, screen } = options;
|
|
@@ -4603,6 +5253,7 @@ function useAppilotsToggle(id, options) {
|
|
|
4603
5253
|
registry.unregister(id);
|
|
4604
5254
|
};
|
|
4605
5255
|
}, [id, registry, getValue, setValue, label, screen]);
|
|
5256
|
+
return useAppilotsElementProps(id);
|
|
4606
5257
|
}
|
|
4607
5258
|
function useAppilotsSlider(id, options) {
|
|
4608
5259
|
const { value, onValueChange, min, max, step, label, screen } = options;
|
|
@@ -4629,6 +5280,7 @@ function useAppilotsSlider(id, options) {
|
|
|
4629
5280
|
registry.unregister(id);
|
|
4630
5281
|
};
|
|
4631
5282
|
}, [id, registry, getValue, setValue, min, max, step, label, screen]);
|
|
5283
|
+
return useAppilotsElementProps(id);
|
|
4632
5284
|
}
|
|
4633
5285
|
|
|
4634
|
-
export { appilotsSelfCheck, captureSnapshot, executeAction, shouldShowSuggestedPrompts, useAppilots, useAppilotsActions, useAppilotsChat, useAppilotsField, useAppilotsNavigation, useAppilotsSlider, useAppilotsTarget, useAppilotsToggle, useSuggestedPrompts };
|
|
5286
|
+
export { AppilotsI18nProvider, REVEAL_MARGIN, SPOTLIGHT_TTL_MS, appilotsSelfCheck, captureSnapshot, detectDeviceLocale, executeAction, getSpotlightMode, getSpotlightTarget, offsetToReveal, resolveLocale, revealTarget, setSpotlightMode, setSpotlightTarget, shouldShowSuggestedPrompts, subscribeSpotlight, subscribeSpotlightAnnouncement, useAppilots, useAppilotsActions, useAppilotsChat, useAppilotsField, useAppilotsI18n, useAppilotsNavigation, useAppilotsSlider, useAppilotsTarget, useAppilotsToggle, useSuggestedPrompts };
|