@appilots/web-sdk 0.2.0 → 0.3.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/README.md +19 -0
- package/dist/{chunk-T5TUFZ6T.js → chunk-H4ITA2QP.js} +255 -16
- package/dist/{chunk-VHRE4RUO.mjs → chunk-RR3T5CPP.mjs} +250 -17
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +70 -58
- package/dist/index.mjs +1 -1
- package/dist/react/index.d.mts +19 -5
- package/dist/react/index.d.ts +19 -5
- package/dist/react/index.js +516 -101
- package/dist/react/index.mjs +505 -98
- package/dist/{AppilotsWebRuntime-DZF26UyU.d.mts → webSpeechHost-CrTKADeb.d.mts} +127 -1
- package/dist/{AppilotsWebRuntime-DZF26UyU.d.ts → webSpeechHost-CrTKADeb.d.ts} +127 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,6 +115,25 @@ messages when reading older replies.
|
|
|
115
115
|
they never send automatically. Markdown supports headings, lists, bold, italic,
|
|
116
116
|
inline/fenced code and HTTP(S) links. HTML is rendered as text.
|
|
117
117
|
|
|
118
|
+
### Voice dictation (opt-in)
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<AppilotsChatFab locale="pt-BR" speech="browser" />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`speech="browser"` adds a mic button that uses the Web Speech API. The button
|
|
125
|
+
is hidden where the browser has no speech recognition (Firefox, many WebViews).
|
|
126
|
+
It is a toggle (`aria-pressed`), so it works with a keyboard or a switch device
|
|
127
|
+
and nobody has to hold it down. The recognised text fills the composer as the
|
|
128
|
+
user speaks, can be edited, and is never sent automatically. Escape discards the
|
|
129
|
+
dictation. A status region announces when listening starts and stops.
|
|
130
|
+
|
|
131
|
+
**In Chrome and Edge the audio is sent to the browser vendor's speech service**,
|
|
132
|
+
so it leaves the device. To use an engine you control, pass your own `SpeechHost`
|
|
133
|
+
instead (`speech={host}`). See the
|
|
134
|
+
[Voice Input guide](https://docs.appilots.com/docs/guides/voice-input) for the
|
|
135
|
+
contract and the LGPD notes.
|
|
136
|
+
|
|
118
137
|
“Talk to a person” uses the existing escalation API: waiting/connected status,
|
|
119
138
|
operator replies, and user messages all stay in the same transcript. The API
|
|
120
139
|
and an operator in the dashboard must be available for a person to answer.
|
|
@@ -4511,18 +4511,27 @@ var WRITE_SUCCESS = {
|
|
|
4511
4511
|
"a\\s+[e\xE9]t[e\xE9]\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]e?"
|
|
4512
4512
|
]
|
|
4513
4513
|
};
|
|
4514
|
+
var SENTENCE_START = "(?:^|[.!?:;*\\n]\\s*)";
|
|
4514
4515
|
var COMPLETION_CLAIM = {
|
|
4515
4516
|
pt: [
|
|
4516
4517
|
"j[a\xE1]\\s+(?:cadastrei|registrei|salvei|criei)",
|
|
4517
4518
|
"conclu[i\xED]\\s+o\\s+cadastro",
|
|
4518
|
-
"cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)"
|
|
4519
|
+
"cadastro\\s+(?:foi\\s+)?(?:conclu[i\xED]do|realizado|feito)",
|
|
4520
|
+
`${SENTENCE_START}(?:eu\\s+)?(?:cadastrei|registrei|salvei|criei|adicionei|enviei)\\b`
|
|
4519
4521
|
],
|
|
4520
4522
|
en: [
|
|
4521
4523
|
"i(?:'|\u2019)?ve\\s+(?:registered|saved|created|added)",
|
|
4522
|
-
"i\\s+have\\s+(?:registered|saved|created|added)"
|
|
4524
|
+
"i\\s+have\\s+(?:registered|saved|created|added)",
|
|
4525
|
+
`${SENTENCE_START}i\\s+(?:registered|saved|created|added|submitted)\\b`
|
|
4523
4526
|
],
|
|
4524
|
-
es: [
|
|
4525
|
-
|
|
4527
|
+
es: [
|
|
4528
|
+
"ya\\s+(?:lo\\s+|la\\s+)?(?:registr[e\xE9]|guard[e\xE9]|cre[e\xE9])",
|
|
4529
|
+
`${SENTENCE_START}(?:ya\\s+)?he\\s+(?:registrado|guardado|creado|enviado|a[\xF1n]adido)\\b`
|
|
4530
|
+
],
|
|
4531
|
+
fr: [
|
|
4532
|
+
"j(?:e\\s+l(?:'|\u2019)ai|(?:'|\u2019)ai)\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]",
|
|
4533
|
+
`${SENTENCE_START}c(?:'|\u2019)est\\s+(?:enregistr|cr[e\xE9]|ajout|sauvegard)[e\xE9]`
|
|
4534
|
+
]
|
|
4526
4535
|
};
|
|
4527
4536
|
var SUBMIT_CONTROL = {
|
|
4528
4537
|
pt: ["cadastrar", "registrar", "salvar", "enviar", "confirmar"],
|
|
@@ -4640,6 +4649,7 @@ var AppilotsClient = class {
|
|
|
4640
4649
|
mcpVersion;
|
|
4641
4650
|
introspectionReporter;
|
|
4642
4651
|
user;
|
|
4652
|
+
plainLanguage;
|
|
4643
4653
|
sessionId = null;
|
|
4644
4654
|
mission = null;
|
|
4645
4655
|
missionDispatchPaused = false;
|
|
@@ -4658,6 +4668,7 @@ var AppilotsClient = class {
|
|
|
4658
4668
|
this.mcpVersion = options.mcpVersion;
|
|
4659
4669
|
this.introspectionReporter = options.introspectionReporter;
|
|
4660
4670
|
this.user = options.user;
|
|
4671
|
+
this.plainLanguage = options.plainLanguage;
|
|
4661
4672
|
this.headers = {
|
|
4662
4673
|
"Content-Type": "application/json",
|
|
4663
4674
|
// Version telemetry: lets the server correlate behavior per SDK
|
|
@@ -4807,6 +4818,19 @@ var AppilotsClient = class {
|
|
|
4807
4818
|
return {};
|
|
4808
4819
|
}
|
|
4809
4820
|
}
|
|
4821
|
+
/**
|
|
4822
|
+
* Per-session plain-language override — e.g. from the signed-in user's
|
|
4823
|
+
* accessibility profile. `undefined` hands the decision back to the
|
|
4824
|
+
* project setting. Applies from the next request on.
|
|
4825
|
+
*/
|
|
4826
|
+
setPlainLanguage(value) {
|
|
4827
|
+
this.plainLanguage = value;
|
|
4828
|
+
}
|
|
4829
|
+
/** Every relay-bound context carries the reply-style preference. */
|
|
4830
|
+
withPreferences(context) {
|
|
4831
|
+
if (this.plainLanguage === void 0) return context;
|
|
4832
|
+
return { ...context ?? {}, plainLanguage: this.plainLanguage };
|
|
4833
|
+
}
|
|
4810
4834
|
// ── Messages ──────────────────────────────────────────────────
|
|
4811
4835
|
async sendMessage(content, context) {
|
|
4812
4836
|
if (this.sessionStorage && !this.sessionId) await this.loadStoredSession();
|
|
@@ -4820,7 +4844,7 @@ var AppilotsClient = class {
|
|
|
4820
4844
|
body: JSON.stringify({
|
|
4821
4845
|
content,
|
|
4822
4846
|
sessionId: this.sessionId,
|
|
4823
|
-
context,
|
|
4847
|
+
context: this.withPreferences(context),
|
|
4824
4848
|
// Optional MCP version bundled with this app build. Server
|
|
4825
4849
|
// compares vs the active MCP doc to detect stale uploads.
|
|
4826
4850
|
...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
|
|
@@ -5085,7 +5109,7 @@ var AppilotsClient = class {
|
|
|
5085
5109
|
JSON.stringify({
|
|
5086
5110
|
content,
|
|
5087
5111
|
sessionId: this.sessionId,
|
|
5088
|
-
context,
|
|
5112
|
+
context: this.withPreferences(context),
|
|
5089
5113
|
...this.mcpVersion ? { mcpVersion: this.mcpVersion } : {},
|
|
5090
5114
|
...this.introspectionFragment()
|
|
5091
5115
|
})
|
|
@@ -5104,7 +5128,9 @@ var AppilotsClient = class {
|
|
|
5104
5128
|
body: JSON.stringify({
|
|
5105
5129
|
sessionId: this.sessionId,
|
|
5106
5130
|
results,
|
|
5107
|
-
context: this.
|
|
5131
|
+
context: this.withPreferences(
|
|
5132
|
+
this.mission ? { ...context, missionProtocol: 1, missionId: this.mission.scope.id } : context
|
|
5133
|
+
),
|
|
5108
5134
|
hop
|
|
5109
5135
|
})
|
|
5110
5136
|
},
|
|
@@ -5192,7 +5218,7 @@ var AppilotsClient = class {
|
|
|
5192
5218
|
body: JSON.stringify({
|
|
5193
5219
|
command,
|
|
5194
5220
|
missionId: this.mission.scope.id,
|
|
5195
|
-
context
|
|
5221
|
+
context: this.withPreferences(context)
|
|
5196
5222
|
})
|
|
5197
5223
|
});
|
|
5198
5224
|
if (command === "resume") this.missionDispatchPaused = false;
|
|
@@ -5579,6 +5605,11 @@ var AppilotsClient = class {
|
|
|
5579
5605
|
};
|
|
5580
5606
|
}
|
|
5581
5607
|
};
|
|
5608
|
+
function isSpeechHost(value) {
|
|
5609
|
+
if (!value || typeof value !== "object") return false;
|
|
5610
|
+
const candidate = value;
|
|
5611
|
+
return typeof candidate.start === "function" && typeof candidate.stop === "function" && typeof candidate.cancel === "function";
|
|
5612
|
+
}
|
|
5582
5613
|
var DESTRUCTIVE_LABEL_RE = /\b(cancelar?\w*|cancel(?:ar|led|ation|amento)?|delete\w*|exclu\w*|delet\w*|remov\w*|remove|apag\w*|destroy\w*|unsubscribe\w*)\b/i;
|
|
5583
5614
|
function looksDestructiveActionLabel(value) {
|
|
5584
5615
|
if (typeof value !== "string" || !value.trim()) return false;
|
|
@@ -6720,6 +6751,10 @@ function actionHasCompletedConfirmGate(action, actions) {
|
|
|
6720
6751
|
return payload.destructive === true || payload.severity === "danger";
|
|
6721
6752
|
});
|
|
6722
6753
|
}
|
|
6754
|
+
function confirmExpiresAt(confirm) {
|
|
6755
|
+
const scope = readPayload(confirm).missionApproval;
|
|
6756
|
+
return typeof scope?.expiresAt === "number" ? scope.expiresAt : void 0;
|
|
6757
|
+
}
|
|
6723
6758
|
var ActionQueueMachine = class {
|
|
6724
6759
|
deps;
|
|
6725
6760
|
options;
|
|
@@ -7225,6 +7260,20 @@ var ActionQueueMachine = class {
|
|
|
7225
7260
|
return this.state.actions.filter((a) => a.status === "completed" || a.status === "failed");
|
|
7226
7261
|
}
|
|
7227
7262
|
};
|
|
7263
|
+
var STILL_WORKING_FIRST_MS = 1e4;
|
|
7264
|
+
var STILL_WORKING_EVERY_MS = 2e4;
|
|
7265
|
+
function startStillWorkingTimer(onTick) {
|
|
7266
|
+
let elapsedMs = STILL_WORKING_FIRST_MS;
|
|
7267
|
+
let timer = setTimeout(function tick() {
|
|
7268
|
+
onTick(Math.round(elapsedMs / 1e3));
|
|
7269
|
+
elapsedMs += STILL_WORKING_EVERY_MS;
|
|
7270
|
+
timer = setTimeout(tick, STILL_WORKING_EVERY_MS);
|
|
7271
|
+
}, STILL_WORKING_FIRST_MS);
|
|
7272
|
+
return () => clearTimeout(timer);
|
|
7273
|
+
}
|
|
7274
|
+
function normalize3(s) {
|
|
7275
|
+
return s.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
7276
|
+
}
|
|
7228
7277
|
function matchScore(needle, hay) {
|
|
7229
7278
|
if (!needle || !hay) return 0;
|
|
7230
7279
|
if (needle === hay) return 100;
|
|
@@ -7925,7 +7974,7 @@ function parseInlineMarkdown(text) {
|
|
|
7925
7974
|
}
|
|
7926
7975
|
|
|
7927
7976
|
// src/version.ts
|
|
7928
|
-
var SDK_VERSION2 = "0.
|
|
7977
|
+
var SDK_VERSION2 = "0.3.0";
|
|
7929
7978
|
|
|
7930
7979
|
// src/introspection/locators.ts
|
|
7931
7980
|
var APPILOTS_ID_ATTR = "data-appilots-id";
|
|
@@ -9320,7 +9369,9 @@ function stripComponentPrefix(componentId) {
|
|
|
9320
9369
|
/^(?:button|btn|switch|toggle|slider|input|field|link)[-_](.+)$/i
|
|
9321
9370
|
);
|
|
9322
9371
|
if (kebab?.[1]) return kebab[1];
|
|
9323
|
-
const camel = componentId.match(
|
|
9372
|
+
const camel = componentId.match(
|
|
9373
|
+
/^(?:button|btn|switch|toggle|slider|input|field|link)([A-Z].*)$/
|
|
9374
|
+
);
|
|
9324
9375
|
if (camel?.[1]) return camel[1].charAt(0).toLowerCase() + camel[1].slice(1);
|
|
9325
9376
|
return null;
|
|
9326
9377
|
}
|
|
@@ -9348,7 +9399,8 @@ function matchesKind(el, kind) {
|
|
|
9348
9399
|
return !["checkbox", "radio", "button", "submit", "reset", "image"].includes(type);
|
|
9349
9400
|
}
|
|
9350
9401
|
if (kind === "toggle") {
|
|
9351
|
-
if (tag === "input" && (el.getAttribute("type") ?? "").toLowerCase() === "checkbox")
|
|
9402
|
+
if (tag === "input" && (el.getAttribute("type") ?? "").toLowerCase() === "checkbox")
|
|
9403
|
+
return true;
|
|
9352
9404
|
const role2 = (el.getAttribute("role") ?? "").toLowerCase();
|
|
9353
9405
|
return role2 === "switch" || role2 === "checkbox";
|
|
9354
9406
|
}
|
|
@@ -9367,7 +9419,9 @@ function findListItemElements(listIndex, options = {}) {
|
|
|
9367
9419
|
const root = rootOf(options);
|
|
9368
9420
|
if (typeof root.querySelectorAll !== "function") return void 0;
|
|
9369
9421
|
const containers = Array.from(
|
|
9370
|
-
root.querySelectorAll(
|
|
9422
|
+
root.querySelectorAll(
|
|
9423
|
+
`[${APPILOTS_LIST_ID_ATTR}], ul, ol, table, [role="list"], [role="listbox"], [role="grid"]`
|
|
9424
|
+
)
|
|
9371
9425
|
).filter((el) => !hidesSubtree(el));
|
|
9372
9426
|
const container = containers[listIndex];
|
|
9373
9427
|
if (!container) return void 0;
|
|
@@ -9462,6 +9516,14 @@ function resolveTargetElement(rawId, options = {}) {
|
|
|
9462
9516
|
return { element: el, via: "exact-id" };
|
|
9463
9517
|
}
|
|
9464
9518
|
}
|
|
9519
|
+
const folded = normalize3(id);
|
|
9520
|
+
if (folded) {
|
|
9521
|
+
for (const el of candidates) {
|
|
9522
|
+
if (idCandidatesFor(el).some((candidate) => normalize3(candidate) === folded)) {
|
|
9523
|
+
return { element: el, via: "exact-id" };
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9526
|
+
}
|
|
9465
9527
|
const stripped = stripComponentPrefix(id);
|
|
9466
9528
|
if (stripped) {
|
|
9467
9529
|
const strippedLower = stripped.toLowerCase();
|
|
@@ -9476,9 +9538,10 @@ function resolveTargetElement(rawId, options = {}) {
|
|
|
9476
9538
|
let best;
|
|
9477
9539
|
let tied = false;
|
|
9478
9540
|
for (const el of candidates) {
|
|
9541
|
+
if (isSensitiveField(el, bestId(el), accessibleName(el))) continue;
|
|
9479
9542
|
let score = 0;
|
|
9480
9543
|
for (const candidate of idCandidatesFor(el)) {
|
|
9481
|
-
score = Math.max(score, matchScore(
|
|
9544
|
+
score = Math.max(score, matchScore(folded, normalize3(candidate)));
|
|
9482
9545
|
}
|
|
9483
9546
|
if (score < MIN_FUZZY_SCORE) continue;
|
|
9484
9547
|
if (!best || score > best.score) {
|
|
@@ -9508,7 +9571,9 @@ function isOccludedByModal(el, options = {}) {
|
|
|
9508
9571
|
const root = rootOf(options);
|
|
9509
9572
|
if (typeof root.querySelectorAll !== "function") return false;
|
|
9510
9573
|
const modals = Array.from(
|
|
9511
|
-
root.querySelectorAll(
|
|
9574
|
+
root.querySelectorAll(
|
|
9575
|
+
'[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]'
|
|
9576
|
+
)
|
|
9512
9577
|
).filter((modal) => !hidesSubtree(modal) && isVisible(modal));
|
|
9513
9578
|
if (modals.length === 0) return false;
|
|
9514
9579
|
return !modals.some((modal) => modal === el || modal.contains(el));
|
|
@@ -9517,7 +9582,9 @@ function modalCandidateLabels(options = {}, limit = 8) {
|
|
|
9517
9582
|
const root = rootOf(options);
|
|
9518
9583
|
if (typeof root.querySelectorAll !== "function") return [];
|
|
9519
9584
|
const modal = Array.from(
|
|
9520
|
-
root.querySelectorAll(
|
|
9585
|
+
root.querySelectorAll(
|
|
9586
|
+
'[aria-modal="true"], [role="dialog"], [role="alertdialog"], dialog[open]'
|
|
9587
|
+
)
|
|
9521
9588
|
).find((candidate) => !hidesSubtree(candidate));
|
|
9522
9589
|
if (!modal) return [];
|
|
9523
9590
|
const out = [];
|
|
@@ -9552,6 +9619,21 @@ function readVisibleValidation(context) {
|
|
|
9552
9619
|
const snapshot = captureSnapshot({ root: rootElement(context) });
|
|
9553
9620
|
return snapshot.texts.find((text) => FORM_VALIDATION_TEXT_RE2.test(text));
|
|
9554
9621
|
}
|
|
9622
|
+
var SUBMIT_SETTLE_MAX_MS = 6e3;
|
|
9623
|
+
function readAlertTexts(context) {
|
|
9624
|
+
const root = rootElement(context) ?? (typeof document !== "undefined" ? document.body : void 0);
|
|
9625
|
+
if (!root) return [];
|
|
9626
|
+
return Array.from(root.querySelectorAll('[role="alert"], [aria-live="assertive"]')).filter((el) => !hidesSubtree(el)).map((el) => (el.textContent ?? "").trim()).filter(Boolean);
|
|
9627
|
+
}
|
|
9628
|
+
async function waitForSubmitSettle(submit, context) {
|
|
9629
|
+
const busy = () => submit.isConnected && (submit.disabled || submit.getAttribute("aria-disabled") === "true") || captureSnapshot({ root: rootElement(context) }).loading;
|
|
9630
|
+
const started = Date.now();
|
|
9631
|
+
while (busy()) {
|
|
9632
|
+
if (context.signal?.aborted || Date.now() - started >= SUBMIT_SETTLE_MAX_MS) return !busy();
|
|
9633
|
+
await sleep2(100);
|
|
9634
|
+
}
|
|
9635
|
+
return true;
|
|
9636
|
+
}
|
|
9555
9637
|
function rootElement(context) {
|
|
9556
9638
|
const root = context.root;
|
|
9557
9639
|
if (!root) return void 0;
|
|
@@ -9757,6 +9839,18 @@ async function formFillHandler(payload, context, permissions) {
|
|
|
9757
9839
|
setToggleValue(el, value2);
|
|
9758
9840
|
continue;
|
|
9759
9841
|
}
|
|
9842
|
+
if (el.tagName.toLowerCase() === "input" && el.type === "file") {
|
|
9843
|
+
failures.push({
|
|
9844
|
+
fieldId: field.fieldId,
|
|
9845
|
+
error: `Field "${field.fieldId}" is a file picker; only the user can attach a file`,
|
|
9846
|
+
diagnose: diagnose(context, "validation", {
|
|
9847
|
+
fieldId: field.fieldId,
|
|
9848
|
+
recoverable: false,
|
|
9849
|
+
requiresUserInput: true
|
|
9850
|
+
})
|
|
9851
|
+
});
|
|
9852
|
+
continue;
|
|
9853
|
+
}
|
|
9760
9854
|
const value = field.value == null ? "" : String(field.value);
|
|
9761
9855
|
const written = setControlValue(el, value);
|
|
9762
9856
|
if (!written) {
|
|
@@ -9824,6 +9918,7 @@ async function formFillHandler(payload, context, permissions) {
|
|
|
9824
9918
|
context
|
|
9825
9919
|
);
|
|
9826
9920
|
if (blocked) return { success: false, ...blocked };
|
|
9921
|
+
const alertsBefore = new Set(readAlertTexts(context));
|
|
9827
9922
|
clickElement(submit);
|
|
9828
9923
|
await sleep2(120);
|
|
9829
9924
|
if (context.signal?.aborted) return { success: false, error: "Session ended" };
|
|
@@ -9836,6 +9931,30 @@ async function formFillHandler(payload, context, permissions) {
|
|
|
9836
9931
|
effect: "partial"
|
|
9837
9932
|
};
|
|
9838
9933
|
}
|
|
9934
|
+
const settled = await waitForSubmitSettle(submit, context);
|
|
9935
|
+
if (context.signal?.aborted) return { success: false, error: "Session ended" };
|
|
9936
|
+
if (!settled) {
|
|
9937
|
+
return {
|
|
9938
|
+
success: false,
|
|
9939
|
+
error: `Submit still in progress after ${SUBMIT_SETTLE_MAX_MS / 1e3}s \u2014 outcome unknown, do not report it as saved`,
|
|
9940
|
+
diagnose: diagnose(context, "screen-timeout", { recoverable: false }),
|
|
9941
|
+
effect: "unknown"
|
|
9942
|
+
};
|
|
9943
|
+
}
|
|
9944
|
+
const lateValidation = readVisibleValidation(context);
|
|
9945
|
+
const rejection = lateValidation ?? readAlertTexts(context).find((text) => !alertsBefore.has(text));
|
|
9946
|
+
if (rejection) {
|
|
9947
|
+
return {
|
|
9948
|
+
success: false,
|
|
9949
|
+
error: `Submit was rejected: ${rejection}`,
|
|
9950
|
+
diagnose: lateValidation ? diagnose(context, "validation", { visibleMessage: rejection, recoverable: true }) : (
|
|
9951
|
+
// Server-side failure (5xx, expired session, timeout): retrying
|
|
9952
|
+
// blindly can double-submit or loop, so hand it to the user.
|
|
9953
|
+
diagnose(context, "unknown", { visibleMessage: rejection, recoverable: false })
|
|
9954
|
+
),
|
|
9955
|
+
effect: "partial"
|
|
9956
|
+
};
|
|
9957
|
+
}
|
|
9839
9958
|
return { success: true, effect: "completed" };
|
|
9840
9959
|
}
|
|
9841
9960
|
return { success: true, effect: "changed" };
|
|
@@ -10154,6 +10273,14 @@ async function withSuppressedConfirm(context, fn) {
|
|
|
10154
10273
|
}
|
|
10155
10274
|
|
|
10156
10275
|
// src/runtime/AppilotsWebRuntime.ts
|
|
10276
|
+
function browserLocale() {
|
|
10277
|
+
if (typeof navigator === "undefined") return void 0;
|
|
10278
|
+
for (const language of navigator.languages ?? [navigator.language]) {
|
|
10279
|
+
const locale = normalizeAppilotsLocale(language);
|
|
10280
|
+
if (locale) return locale;
|
|
10281
|
+
}
|
|
10282
|
+
return void 0;
|
|
10283
|
+
}
|
|
10157
10284
|
var POST_ACTION_LOADING_MAX_MS = 6e3;
|
|
10158
10285
|
var AppilotsWebRuntime = class {
|
|
10159
10286
|
client;
|
|
@@ -10179,13 +10306,15 @@ var AppilotsWebRuntime = class {
|
|
|
10179
10306
|
apiKey: options.apiKey,
|
|
10180
10307
|
apiBaseUrl: options.apiBaseUrl,
|
|
10181
10308
|
debug: options.debug,
|
|
10309
|
+
timeout: options.timeout,
|
|
10182
10310
|
appVersion: options.appVersion,
|
|
10183
10311
|
mcpVersion: options.mcpVersion,
|
|
10184
10312
|
// This package's own version, so the server can tell a web
|
|
10185
10313
|
// client apart from an RN one by more than the platform field
|
|
10186
10314
|
// (issue #312).
|
|
10187
10315
|
sdkVersion: SDK_VERSION2,
|
|
10188
|
-
user: options.user
|
|
10316
|
+
user: options.user,
|
|
10317
|
+
plainLanguage: options.plainLanguage
|
|
10189
10318
|
});
|
|
10190
10319
|
this.navigation = new WebNavigationAdapter({
|
|
10191
10320
|
routes: options.routes,
|
|
@@ -10251,6 +10380,8 @@ var AppilotsWebRuntime = class {
|
|
|
10251
10380
|
buildContext(extras = {}) {
|
|
10252
10381
|
if (extras.newMission === true) this.deltaTracker.reset();
|
|
10253
10382
|
const context = { platform: "web" };
|
|
10383
|
+
const deviceLocale = browserLocale();
|
|
10384
|
+
if (deviceLocale) context.deviceLocale = deviceLocale;
|
|
10254
10385
|
const screen = this.navigation.getCurrentScreen();
|
|
10255
10386
|
if (screen) context.currentScreen = screen;
|
|
10256
10387
|
const navigationState = this.navigation.getNavigationState();
|
|
@@ -10381,6 +10512,108 @@ var AppilotsWebRuntime = class {
|
|
|
10381
10512
|
}
|
|
10382
10513
|
};
|
|
10383
10514
|
|
|
10515
|
+
// src/speech/webSpeechHost.ts
|
|
10516
|
+
var MAX_SILENT_RESTARTS = 5;
|
|
10517
|
+
function recognitionCtor() {
|
|
10518
|
+
const g = globalThis;
|
|
10519
|
+
return g.SpeechRecognition ?? g.webkitSpeechRecognition ?? null;
|
|
10520
|
+
}
|
|
10521
|
+
function isWebSpeechSupported() {
|
|
10522
|
+
return recognitionCtor() !== null;
|
|
10523
|
+
}
|
|
10524
|
+
function createWebSpeechHost() {
|
|
10525
|
+
const Ctor = recognitionCtor();
|
|
10526
|
+
if (!Ctor) return null;
|
|
10527
|
+
let live = null;
|
|
10528
|
+
let committed = "";
|
|
10529
|
+
let current = "";
|
|
10530
|
+
let silentRestarts = 0;
|
|
10531
|
+
let pendingStop = null;
|
|
10532
|
+
const partialListeners = /* @__PURE__ */ new Set();
|
|
10533
|
+
const endListeners = /* @__PURE__ */ new Set();
|
|
10534
|
+
const text = () => `${committed} ${current}`.replace(/\s+/g, " ").trim();
|
|
10535
|
+
const ended = (error) => endListeners.forEach((l) => l(error));
|
|
10536
|
+
const open = (locale) => new Promise((resolve, reject) => {
|
|
10537
|
+
const r = new Ctor();
|
|
10538
|
+
live = r;
|
|
10539
|
+
r.lang = locale;
|
|
10540
|
+
r.continuous = true;
|
|
10541
|
+
r.interimResults = true;
|
|
10542
|
+
let started = false;
|
|
10543
|
+
let failure;
|
|
10544
|
+
r.onstart = () => {
|
|
10545
|
+
started = true;
|
|
10546
|
+
resolve();
|
|
10547
|
+
};
|
|
10548
|
+
r.onresult = ({ results }) => {
|
|
10549
|
+
if (live !== r) return;
|
|
10550
|
+
let next = "";
|
|
10551
|
+
for (let i = 0; i < results.length; i++) next += results[i]?.[0]?.transcript ?? "";
|
|
10552
|
+
current = next;
|
|
10553
|
+
silentRestarts = 0;
|
|
10554
|
+
const t = text();
|
|
10555
|
+
partialListeners.forEach((l) => l(t));
|
|
10556
|
+
};
|
|
10557
|
+
r.onerror = ({ error }) => {
|
|
10558
|
+
if (error !== "no-speech") failure = error;
|
|
10559
|
+
};
|
|
10560
|
+
r.onend = () => {
|
|
10561
|
+
if (live !== r) return;
|
|
10562
|
+
if (pendingStop) {
|
|
10563
|
+
const done = pendingStop;
|
|
10564
|
+
pendingStop = null;
|
|
10565
|
+
live = null;
|
|
10566
|
+
done(text());
|
|
10567
|
+
return;
|
|
10568
|
+
}
|
|
10569
|
+
live = null;
|
|
10570
|
+
if (!started) {
|
|
10571
|
+
reject(new Error(failure ?? "speech recognition did not start"));
|
|
10572
|
+
return;
|
|
10573
|
+
}
|
|
10574
|
+
if (failure) return ended(new Error(failure));
|
|
10575
|
+
if (silentRestarts >= MAX_SILENT_RESTARTS) return ended();
|
|
10576
|
+
silentRestarts++;
|
|
10577
|
+
committed = text();
|
|
10578
|
+
current = "";
|
|
10579
|
+
open(locale).catch(ended);
|
|
10580
|
+
};
|
|
10581
|
+
r.start();
|
|
10582
|
+
});
|
|
10583
|
+
return {
|
|
10584
|
+
start({ locale }) {
|
|
10585
|
+
live?.abort();
|
|
10586
|
+
committed = "";
|
|
10587
|
+
current = "";
|
|
10588
|
+
silentRestarts = 0;
|
|
10589
|
+
pendingStop = null;
|
|
10590
|
+
return open(locale);
|
|
10591
|
+
},
|
|
10592
|
+
stop() {
|
|
10593
|
+
const r = live;
|
|
10594
|
+
if (!r) return Promise.resolve(text());
|
|
10595
|
+
return new Promise((resolve) => {
|
|
10596
|
+
pendingStop = resolve;
|
|
10597
|
+
r.stop();
|
|
10598
|
+
});
|
|
10599
|
+
},
|
|
10600
|
+
async cancel() {
|
|
10601
|
+
const r = live;
|
|
10602
|
+
live = null;
|
|
10603
|
+
pendingStop = null;
|
|
10604
|
+
r?.abort();
|
|
10605
|
+
},
|
|
10606
|
+
onPartial(listener) {
|
|
10607
|
+
partialListeners.add(listener);
|
|
10608
|
+
return () => partialListeners.delete(listener);
|
|
10609
|
+
},
|
|
10610
|
+
onEnd(listener) {
|
|
10611
|
+
endListeners.add(listener);
|
|
10612
|
+
return () => endListeners.delete(listener);
|
|
10613
|
+
}
|
|
10614
|
+
};
|
|
10615
|
+
}
|
|
10616
|
+
|
|
10384
10617
|
exports.APPILOTS_CHAT_ATTR = APPILOTS_CHAT_ATTR;
|
|
10385
10618
|
exports.APPILOTS_ID_ATTR = APPILOTS_ID_ATTR;
|
|
10386
10619
|
exports.APPILOTS_ITEM_COUNT_ATTR = APPILOTS_ITEM_COUNT_ATTR;
|
|
@@ -10398,11 +10631,14 @@ exports.RateLimitedError = RateLimitedError;
|
|
|
10398
10631
|
exports.SDK_VERSION = SDK_VERSION2;
|
|
10399
10632
|
exports.WebNavigationAdapter = WebNavigationAdapter;
|
|
10400
10633
|
exports.accessibleName = accessibleName;
|
|
10634
|
+
exports.appilotsDebugWarn = appilotsDebugWarn;
|
|
10401
10635
|
exports.bestId = bestId;
|
|
10402
10636
|
exports.buildPath = buildPath;
|
|
10403
10637
|
exports.candidateIds = candidateIds;
|
|
10404
10638
|
exports.captureSnapshot = captureSnapshot;
|
|
10405
10639
|
exports.clickElement = clickElement;
|
|
10640
|
+
exports.confirmExpiresAt = confirmExpiresAt;
|
|
10641
|
+
exports.createWebSpeechHost = createWebSpeechHost;
|
|
10406
10642
|
exports.describeAction = describeAction;
|
|
10407
10643
|
exports.elementRole = elementRole;
|
|
10408
10644
|
exports.executeAction = executeAction;
|
|
@@ -10413,7 +10649,9 @@ exports.getRegisteredElement = getRegisteredElement;
|
|
|
10413
10649
|
exports.hidesSubtree = hidesSubtree;
|
|
10414
10650
|
exports.humanizeError = humanizeError;
|
|
10415
10651
|
exports.isOccludedByModal = isOccludedByModal;
|
|
10652
|
+
exports.isSpeechHost = isSpeechHost;
|
|
10416
10653
|
exports.isVisible = isVisible;
|
|
10654
|
+
exports.isWebSpeechSupported = isWebSpeechSupported;
|
|
10417
10655
|
exports.longPressElement = longPressElement;
|
|
10418
10656
|
exports.matchRoute = matchRoute;
|
|
10419
10657
|
exports.modalCandidateLabels = modalCandidateLabels;
|
|
@@ -10436,6 +10674,7 @@ exports.scrollIntoView = scrollIntoView;
|
|
|
10436
10674
|
exports.scrollToOffset = scrollToOffset;
|
|
10437
10675
|
exports.setControlValue = setControlValue;
|
|
10438
10676
|
exports.setToggleValue = setToggleValue;
|
|
10677
|
+
exports.startStillWorkingTimer = startStillWorkingTimer;
|
|
10439
10678
|
exports.stripComponentPrefix = stripComponentPrefix;
|
|
10440
10679
|
exports.waitForLoadingSettle = waitForLoadingSettle;
|
|
10441
10680
|
exports.waitForScreenSettle = waitForScreenSettle;
|