@modelnex/sdk 0.5.40 → 0.5.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{aom-HDYNCIOY.mjs → aom-LJNCLNXL.mjs} +1 -1
- package/dist/chunk-H4LUY7LI.mjs +243 -0
- package/dist/index.js +266 -106
- package/dist/index.mjs +93 -99
- package/package.json +1 -1
- package/dist/chunk-N65UEB6X.mjs +0 -70
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// src/utils/editable-controls.ts
|
|
2
|
+
var EDITABLE_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox"]);
|
|
3
|
+
var EDITABLE_CONTROL_SELECTOR = [
|
|
4
|
+
'input:not([type="hidden"])',
|
|
5
|
+
"textarea",
|
|
6
|
+
"select",
|
|
7
|
+
'[contenteditable="true"]',
|
|
8
|
+
'[role="textbox"]',
|
|
9
|
+
'[role="combobox"]'
|
|
10
|
+
].join(", ");
|
|
11
|
+
function getTagName(element) {
|
|
12
|
+
return String(element?.tagName || "").toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
function getRole(element) {
|
|
15
|
+
const role = element && typeof element.getAttribute === "function" ? element.getAttribute("role") : null;
|
|
16
|
+
return String(role || "").toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function getLabelControl(element) {
|
|
19
|
+
const labelCtor = typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null;
|
|
20
|
+
if (labelCtor && element instanceof labelCtor && element.control instanceof HTMLElement) {
|
|
21
|
+
return element.control;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function isValueBearingElement(element) {
|
|
26
|
+
if (!element) return false;
|
|
27
|
+
const inputCtor = typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null;
|
|
28
|
+
if (inputCtor && element instanceof inputCtor) return true;
|
|
29
|
+
const textareaCtor = typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null;
|
|
30
|
+
if (textareaCtor && element instanceof textareaCtor) return true;
|
|
31
|
+
const selectCtor = typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null;
|
|
32
|
+
if (selectCtor && element instanceof selectCtor) return true;
|
|
33
|
+
return ["input", "textarea", "select"].includes(getTagName(element));
|
|
34
|
+
}
|
|
35
|
+
function findEditableControlInShadowRoot(root) {
|
|
36
|
+
if (!root) return null;
|
|
37
|
+
if (root.shadowRoot) {
|
|
38
|
+
const found = root.shadowRoot.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
39
|
+
if (found) {
|
|
40
|
+
return resolveEditableControlElement(found);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const child of Array.from(root.children)) {
|
|
44
|
+
if (child instanceof HTMLElement) {
|
|
45
|
+
const found = findEditableControlInShadowRoot(child);
|
|
46
|
+
if (found) return found;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function resolveEditableControlElement(element) {
|
|
52
|
+
const labelControl = getLabelControl(element);
|
|
53
|
+
if (labelControl) {
|
|
54
|
+
return resolveEditableControlElement(labelControl);
|
|
55
|
+
}
|
|
56
|
+
if (isValueBearingElement(element) || element.isContentEditable) {
|
|
57
|
+
return element;
|
|
58
|
+
}
|
|
59
|
+
const role = getRole(element);
|
|
60
|
+
if (EDITABLE_ROLES.has(role)) {
|
|
61
|
+
const nested2 = element.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
62
|
+
if (nested2 && nested2 !== element) {
|
|
63
|
+
return resolveEditableControlElement(nested2);
|
|
64
|
+
}
|
|
65
|
+
const shadowNested2 = findEditableControlInShadowRoot(element);
|
|
66
|
+
return shadowNested2 ?? element;
|
|
67
|
+
}
|
|
68
|
+
const nested = element.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
69
|
+
if (nested) {
|
|
70
|
+
return resolveEditableControlElement(nested);
|
|
71
|
+
}
|
|
72
|
+
const shadowNested = findEditableControlInShadowRoot(element);
|
|
73
|
+
return shadowNested ?? element;
|
|
74
|
+
}
|
|
75
|
+
function readEditableControlValue(element, options = {}) {
|
|
76
|
+
const target = resolveEditableControlElement(element);
|
|
77
|
+
const maskPasswords = options.maskPasswords !== false;
|
|
78
|
+
if (isValueBearingElement(target)) {
|
|
79
|
+
const type = String(target.type || "").toLowerCase();
|
|
80
|
+
if (maskPasswords && type === "password") {
|
|
81
|
+
return "***";
|
|
82
|
+
}
|
|
83
|
+
return String(target.value || "").trim().slice(0, 100);
|
|
84
|
+
}
|
|
85
|
+
const genericValue = target.value;
|
|
86
|
+
if (typeof genericValue === "string") {
|
|
87
|
+
return genericValue.trim().slice(0, 100);
|
|
88
|
+
}
|
|
89
|
+
if (typeof genericValue === "number" && Number.isFinite(genericValue)) {
|
|
90
|
+
return String(genericValue).trim().slice(0, 100);
|
|
91
|
+
}
|
|
92
|
+
if (target.isContentEditable) {
|
|
93
|
+
return String(target.textContent || "").trim().slice(0, 100);
|
|
94
|
+
}
|
|
95
|
+
const ariaValueText = target.getAttribute("aria-valuetext");
|
|
96
|
+
if (ariaValueText?.trim()) {
|
|
97
|
+
return ariaValueText.trim().slice(0, 100);
|
|
98
|
+
}
|
|
99
|
+
const ariaValueNow = target.getAttribute("aria-valuenow");
|
|
100
|
+
if (ariaValueNow?.trim()) {
|
|
101
|
+
return ariaValueNow.trim().slice(0, 100);
|
|
102
|
+
}
|
|
103
|
+
return String(target.textContent || "").trim().slice(0, 100);
|
|
104
|
+
}
|
|
105
|
+
function readEditableControlPlaceholder(element) {
|
|
106
|
+
const target = resolveEditableControlElement(element);
|
|
107
|
+
if (typeof target.placeholder === "string") {
|
|
108
|
+
return String(target.placeholder || "").trim().slice(0, 100);
|
|
109
|
+
}
|
|
110
|
+
return String(target.getAttribute("placeholder") || "").trim().slice(0, 100);
|
|
111
|
+
}
|
|
112
|
+
function readEditableControlName(element) {
|
|
113
|
+
const target = resolveEditableControlElement(element);
|
|
114
|
+
const explicitName = target.name;
|
|
115
|
+
if (typeof explicitName === "string" && explicitName.trim()) {
|
|
116
|
+
return explicitName.trim().slice(0, 100);
|
|
117
|
+
}
|
|
118
|
+
return String(
|
|
119
|
+
target.getAttribute("name") || target.getAttribute("id") || target.getAttribute("aria-label") || ""
|
|
120
|
+
).trim().slice(0, 100);
|
|
121
|
+
}
|
|
122
|
+
function readEditableControlType(element) {
|
|
123
|
+
const target = resolveEditableControlElement(element);
|
|
124
|
+
const role = getRole(target);
|
|
125
|
+
if (role) return role;
|
|
126
|
+
const explicitType = target.type;
|
|
127
|
+
if (typeof explicitType === "string" && explicitType.trim()) {
|
|
128
|
+
return explicitType.trim().toLowerCase().slice(0, 50);
|
|
129
|
+
}
|
|
130
|
+
const attributeType = target.getAttribute("type");
|
|
131
|
+
if (attributeType?.trim()) {
|
|
132
|
+
return attributeType.trim().toLowerCase().slice(0, 50);
|
|
133
|
+
}
|
|
134
|
+
return getTagName(target);
|
|
135
|
+
}
|
|
136
|
+
function isEditableControlDisabled(element) {
|
|
137
|
+
const target = resolveEditableControlElement(element);
|
|
138
|
+
const ariaDisabled = String(target.getAttribute("aria-disabled") || "").toLowerCase();
|
|
139
|
+
if (ariaDisabled === "true") return true;
|
|
140
|
+
return Boolean(target.disabled);
|
|
141
|
+
}
|
|
142
|
+
function collectEditableControls(scope) {
|
|
143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
144
|
+
const controls = [];
|
|
145
|
+
for (const rawElement of Array.from(scope.querySelectorAll(EDITABLE_CONTROL_SELECTOR))) {
|
|
146
|
+
const resolved = resolveEditableControlElement(rawElement);
|
|
147
|
+
if (seen.has(resolved)) continue;
|
|
148
|
+
if (isValueBearingElement(resolved) && String(resolved.type || "").toLowerCase() === "hidden") {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
seen.add(resolved);
|
|
152
|
+
controls.push(resolved);
|
|
153
|
+
}
|
|
154
|
+
return controls;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/utils/aom.ts
|
|
158
|
+
var uidMap = /* @__PURE__ */ new Map();
|
|
159
|
+
var nextUid = 1;
|
|
160
|
+
function generateMinifiedAOM() {
|
|
161
|
+
uidMap.clear();
|
|
162
|
+
nextUid = 1;
|
|
163
|
+
const interactiveSet = /* @__PURE__ */ new Set();
|
|
164
|
+
const interactiveCandidates = [
|
|
165
|
+
...Array.from(document.querySelectorAll(
|
|
166
|
+
'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], [role="textbox"], [role="combobox"], [contenteditable="true"]'
|
|
167
|
+
)),
|
|
168
|
+
...collectEditableControls(document)
|
|
169
|
+
];
|
|
170
|
+
const nodes = [];
|
|
171
|
+
interactiveCandidates.forEach((candidate) => {
|
|
172
|
+
const el = resolveEditableControlElement(candidate);
|
|
173
|
+
if (interactiveSet.has(el)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
interactiveSet.add(el);
|
|
177
|
+
if (!el.offsetParent && (el.offsetWidth === 0 || el.offsetHeight === 0)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (el.closest("#modelnex-studio-root") || el.closest("#modelnex-active-agent-root")) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const uid = `node:${nextUid++}`;
|
|
184
|
+
uidMap.set(uid, el);
|
|
185
|
+
let text = (el.textContent || "").replace(/\s+/g, " ").trim();
|
|
186
|
+
const ariaLabel = el.getAttribute("aria-label");
|
|
187
|
+
const placeholder = el.getAttribute("placeholder");
|
|
188
|
+
const value = readEditableControlValue(el, { maskPasswords: false }) || void 0;
|
|
189
|
+
let role = el.tagName.toLowerCase();
|
|
190
|
+
if (el.hasAttribute("role")) {
|
|
191
|
+
role = el.getAttribute("role");
|
|
192
|
+
} else if (role === "a") {
|
|
193
|
+
role = "link";
|
|
194
|
+
} else if (el.tagName.toLowerCase() === "input") {
|
|
195
|
+
const inputType = el.type;
|
|
196
|
+
role = inputType ? `input[${inputType}]` : "input";
|
|
197
|
+
}
|
|
198
|
+
const node = { uid, role };
|
|
199
|
+
const displayLabel = ariaLabel || text || placeholder;
|
|
200
|
+
if (displayLabel) {
|
|
201
|
+
node.text = displayLabel.substring(0, 100);
|
|
202
|
+
}
|
|
203
|
+
const controlName = readEditableControlName(el);
|
|
204
|
+
if (controlName) {
|
|
205
|
+
node.name = controlName;
|
|
206
|
+
}
|
|
207
|
+
if (value) {
|
|
208
|
+
node.value = value.substring(0, 100);
|
|
209
|
+
}
|
|
210
|
+
if (el instanceof HTMLAnchorElement && el.href) {
|
|
211
|
+
try {
|
|
212
|
+
const url = new URL(el.href);
|
|
213
|
+
node.href = url.pathname + url.search + url.hash;
|
|
214
|
+
} catch {
|
|
215
|
+
node.href = el.getAttribute("href");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
nodes.push(node);
|
|
219
|
+
});
|
|
220
|
+
return { nodes };
|
|
221
|
+
}
|
|
222
|
+
function getElementByUid(uid) {
|
|
223
|
+
return uidMap.get(uid) || null;
|
|
224
|
+
}
|
|
225
|
+
function clearAOMMap() {
|
|
226
|
+
uidMap.clear();
|
|
227
|
+
nextUid = 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export {
|
|
231
|
+
isValueBearingElement,
|
|
232
|
+
findEditableControlInShadowRoot,
|
|
233
|
+
resolveEditableControlElement,
|
|
234
|
+
readEditableControlValue,
|
|
235
|
+
readEditableControlPlaceholder,
|
|
236
|
+
readEditableControlName,
|
|
237
|
+
readEditableControlType,
|
|
238
|
+
isEditableControlDisabled,
|
|
239
|
+
collectEditableControls,
|
|
240
|
+
generateMinifiedAOM,
|
|
241
|
+
getElementByUid,
|
|
242
|
+
clearAOMMap
|
|
243
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,168 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
+
// src/utils/editable-controls.ts
|
|
34
|
+
function getTagName(element) {
|
|
35
|
+
return String(element?.tagName || "").toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
function getRole(element) {
|
|
38
|
+
const role = element && typeof element.getAttribute === "function" ? element.getAttribute("role") : null;
|
|
39
|
+
return String(role || "").toLowerCase();
|
|
40
|
+
}
|
|
41
|
+
function getLabelControl(element) {
|
|
42
|
+
const labelCtor = typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null;
|
|
43
|
+
if (labelCtor && element instanceof labelCtor && element.control instanceof HTMLElement) {
|
|
44
|
+
return element.control;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function isValueBearingElement(element) {
|
|
49
|
+
if (!element) return false;
|
|
50
|
+
const inputCtor = typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null;
|
|
51
|
+
if (inputCtor && element instanceof inputCtor) return true;
|
|
52
|
+
const textareaCtor = typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null;
|
|
53
|
+
if (textareaCtor && element instanceof textareaCtor) return true;
|
|
54
|
+
const selectCtor = typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null;
|
|
55
|
+
if (selectCtor && element instanceof selectCtor) return true;
|
|
56
|
+
return ["input", "textarea", "select"].includes(getTagName(element));
|
|
57
|
+
}
|
|
58
|
+
function findEditableControlInShadowRoot(root) {
|
|
59
|
+
if (!root) return null;
|
|
60
|
+
if (root.shadowRoot) {
|
|
61
|
+
const found = root.shadowRoot.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
62
|
+
if (found) {
|
|
63
|
+
return resolveEditableControlElement(found);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const child of Array.from(root.children)) {
|
|
67
|
+
if (child instanceof HTMLElement) {
|
|
68
|
+
const found = findEditableControlInShadowRoot(child);
|
|
69
|
+
if (found) return found;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function resolveEditableControlElement(element) {
|
|
75
|
+
const labelControl = getLabelControl(element);
|
|
76
|
+
if (labelControl) {
|
|
77
|
+
return resolveEditableControlElement(labelControl);
|
|
78
|
+
}
|
|
79
|
+
if (isValueBearingElement(element) || element.isContentEditable) {
|
|
80
|
+
return element;
|
|
81
|
+
}
|
|
82
|
+
const role = getRole(element);
|
|
83
|
+
if (EDITABLE_ROLES.has(role)) {
|
|
84
|
+
const nested2 = element.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
85
|
+
if (nested2 && nested2 !== element) {
|
|
86
|
+
return resolveEditableControlElement(nested2);
|
|
87
|
+
}
|
|
88
|
+
const shadowNested2 = findEditableControlInShadowRoot(element);
|
|
89
|
+
return shadowNested2 ?? element;
|
|
90
|
+
}
|
|
91
|
+
const nested = element.querySelector(EDITABLE_CONTROL_SELECTOR);
|
|
92
|
+
if (nested) {
|
|
93
|
+
return resolveEditableControlElement(nested);
|
|
94
|
+
}
|
|
95
|
+
const shadowNested = findEditableControlInShadowRoot(element);
|
|
96
|
+
return shadowNested ?? element;
|
|
97
|
+
}
|
|
98
|
+
function readEditableControlValue(element, options = {}) {
|
|
99
|
+
const target = resolveEditableControlElement(element);
|
|
100
|
+
const maskPasswords = options.maskPasswords !== false;
|
|
101
|
+
if (isValueBearingElement(target)) {
|
|
102
|
+
const type = String(target.type || "").toLowerCase();
|
|
103
|
+
if (maskPasswords && type === "password") {
|
|
104
|
+
return "***";
|
|
105
|
+
}
|
|
106
|
+
return String(target.value || "").trim().slice(0, 100);
|
|
107
|
+
}
|
|
108
|
+
const genericValue = target.value;
|
|
109
|
+
if (typeof genericValue === "string") {
|
|
110
|
+
return genericValue.trim().slice(0, 100);
|
|
111
|
+
}
|
|
112
|
+
if (typeof genericValue === "number" && Number.isFinite(genericValue)) {
|
|
113
|
+
return String(genericValue).trim().slice(0, 100);
|
|
114
|
+
}
|
|
115
|
+
if (target.isContentEditable) {
|
|
116
|
+
return String(target.textContent || "").trim().slice(0, 100);
|
|
117
|
+
}
|
|
118
|
+
const ariaValueText = target.getAttribute("aria-valuetext");
|
|
119
|
+
if (ariaValueText?.trim()) {
|
|
120
|
+
return ariaValueText.trim().slice(0, 100);
|
|
121
|
+
}
|
|
122
|
+
const ariaValueNow = target.getAttribute("aria-valuenow");
|
|
123
|
+
if (ariaValueNow?.trim()) {
|
|
124
|
+
return ariaValueNow.trim().slice(0, 100);
|
|
125
|
+
}
|
|
126
|
+
return String(target.textContent || "").trim().slice(0, 100);
|
|
127
|
+
}
|
|
128
|
+
function readEditableControlPlaceholder(element) {
|
|
129
|
+
const target = resolveEditableControlElement(element);
|
|
130
|
+
if (typeof target.placeholder === "string") {
|
|
131
|
+
return String(target.placeholder || "").trim().slice(0, 100);
|
|
132
|
+
}
|
|
133
|
+
return String(target.getAttribute("placeholder") || "").trim().slice(0, 100);
|
|
134
|
+
}
|
|
135
|
+
function readEditableControlName(element) {
|
|
136
|
+
const target = resolveEditableControlElement(element);
|
|
137
|
+
const explicitName = target.name;
|
|
138
|
+
if (typeof explicitName === "string" && explicitName.trim()) {
|
|
139
|
+
return explicitName.trim().slice(0, 100);
|
|
140
|
+
}
|
|
141
|
+
return String(
|
|
142
|
+
target.getAttribute("name") || target.getAttribute("id") || target.getAttribute("aria-label") || ""
|
|
143
|
+
).trim().slice(0, 100);
|
|
144
|
+
}
|
|
145
|
+
function readEditableControlType(element) {
|
|
146
|
+
const target = resolveEditableControlElement(element);
|
|
147
|
+
const role = getRole(target);
|
|
148
|
+
if (role) return role;
|
|
149
|
+
const explicitType = target.type;
|
|
150
|
+
if (typeof explicitType === "string" && explicitType.trim()) {
|
|
151
|
+
return explicitType.trim().toLowerCase().slice(0, 50);
|
|
152
|
+
}
|
|
153
|
+
const attributeType = target.getAttribute("type");
|
|
154
|
+
if (attributeType?.trim()) {
|
|
155
|
+
return attributeType.trim().toLowerCase().slice(0, 50);
|
|
156
|
+
}
|
|
157
|
+
return getTagName(target);
|
|
158
|
+
}
|
|
159
|
+
function isEditableControlDisabled(element) {
|
|
160
|
+
const target = resolveEditableControlElement(element);
|
|
161
|
+
const ariaDisabled = String(target.getAttribute("aria-disabled") || "").toLowerCase();
|
|
162
|
+
if (ariaDisabled === "true") return true;
|
|
163
|
+
return Boolean(target.disabled);
|
|
164
|
+
}
|
|
165
|
+
function collectEditableControls(scope) {
|
|
166
|
+
const seen = /* @__PURE__ */ new Set();
|
|
167
|
+
const controls = [];
|
|
168
|
+
for (const rawElement of Array.from(scope.querySelectorAll(EDITABLE_CONTROL_SELECTOR))) {
|
|
169
|
+
const resolved = resolveEditableControlElement(rawElement);
|
|
170
|
+
if (seen.has(resolved)) continue;
|
|
171
|
+
if (isValueBearingElement(resolved) && String(resolved.type || "").toLowerCase() === "hidden") {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
seen.add(resolved);
|
|
175
|
+
controls.push(resolved);
|
|
176
|
+
}
|
|
177
|
+
return controls;
|
|
178
|
+
}
|
|
179
|
+
var EDITABLE_ROLES, EDITABLE_CONTROL_SELECTOR;
|
|
180
|
+
var init_editable_controls = __esm({
|
|
181
|
+
"src/utils/editable-controls.ts"() {
|
|
182
|
+
"use strict";
|
|
183
|
+
EDITABLE_ROLES = /* @__PURE__ */ new Set(["textbox", "combobox"]);
|
|
184
|
+
EDITABLE_CONTROL_SELECTOR = [
|
|
185
|
+
'input:not([type="hidden"])',
|
|
186
|
+
"textarea",
|
|
187
|
+
"select",
|
|
188
|
+
'[contenteditable="true"]',
|
|
189
|
+
'[role="textbox"]',
|
|
190
|
+
'[role="combobox"]'
|
|
191
|
+
].join(", ");
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
33
195
|
// src/utils/aom.ts
|
|
34
196
|
var aom_exports = {};
|
|
35
197
|
__export(aom_exports, {
|
|
@@ -40,11 +202,20 @@ __export(aom_exports, {
|
|
|
40
202
|
function generateMinifiedAOM() {
|
|
41
203
|
uidMap.clear();
|
|
42
204
|
nextUid = 1;
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
205
|
+
const interactiveSet = /* @__PURE__ */ new Set();
|
|
206
|
+
const interactiveCandidates = [
|
|
207
|
+
...Array.from(document.querySelectorAll(
|
|
208
|
+
'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"], [role="textbox"], [role="combobox"], [contenteditable="true"]'
|
|
209
|
+
)),
|
|
210
|
+
...collectEditableControls(document)
|
|
211
|
+
];
|
|
46
212
|
const nodes = [];
|
|
47
|
-
|
|
213
|
+
interactiveCandidates.forEach((candidate) => {
|
|
214
|
+
const el = resolveEditableControlElement(candidate);
|
|
215
|
+
if (interactiveSet.has(el)) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
interactiveSet.add(el);
|
|
48
219
|
if (!el.offsetParent && (el.offsetWidth === 0 || el.offsetHeight === 0)) {
|
|
49
220
|
return;
|
|
50
221
|
}
|
|
@@ -56,25 +227,24 @@ function generateMinifiedAOM() {
|
|
|
56
227
|
let text = (el.textContent || "").replace(/\s+/g, " ").trim();
|
|
57
228
|
const ariaLabel = el.getAttribute("aria-label");
|
|
58
229
|
const placeholder = el.getAttribute("placeholder");
|
|
59
|
-
|
|
60
|
-
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
|
|
61
|
-
value = el.value;
|
|
62
|
-
}
|
|
230
|
+
const value = readEditableControlValue(el, { maskPasswords: false }) || void 0;
|
|
63
231
|
let role = el.tagName.toLowerCase();
|
|
64
232
|
if (el.hasAttribute("role")) {
|
|
65
233
|
role = el.getAttribute("role");
|
|
66
234
|
} else if (role === "a") {
|
|
67
235
|
role = "link";
|
|
68
|
-
} else if (el
|
|
69
|
-
|
|
236
|
+
} else if (el.tagName.toLowerCase() === "input") {
|
|
237
|
+
const inputType = el.type;
|
|
238
|
+
role = inputType ? `input[${inputType}]` : "input";
|
|
70
239
|
}
|
|
71
240
|
const node = { uid, role };
|
|
72
241
|
const displayLabel = ariaLabel || text || placeholder;
|
|
73
242
|
if (displayLabel) {
|
|
74
243
|
node.text = displayLabel.substring(0, 100);
|
|
75
244
|
}
|
|
76
|
-
|
|
77
|
-
|
|
245
|
+
const controlName = readEditableControlName(el);
|
|
246
|
+
if (controlName) {
|
|
247
|
+
node.name = controlName;
|
|
78
248
|
}
|
|
79
249
|
if (value) {
|
|
80
250
|
node.value = value.substring(0, 100);
|
|
@@ -102,6 +272,7 @@ var uidMap, nextUid;
|
|
|
102
272
|
var init_aom = __esm({
|
|
103
273
|
"src/utils/aom.ts"() {
|
|
104
274
|
"use strict";
|
|
275
|
+
init_editable_controls();
|
|
105
276
|
uidMap = /* @__PURE__ */ new Map();
|
|
106
277
|
nextUid = 1;
|
|
107
278
|
}
|
|
@@ -616,6 +787,7 @@ function useAutoExtract(devMode) {
|
|
|
616
787
|
}
|
|
617
788
|
|
|
618
789
|
// src/utils/dom-summary.ts
|
|
790
|
+
init_editable_controls();
|
|
619
791
|
function textOf(el) {
|
|
620
792
|
return el?.textContent?.trim().slice(0, 200) ?? "";
|
|
621
793
|
}
|
|
@@ -642,8 +814,9 @@ function safeQueryAll(selector) {
|
|
|
642
814
|
}
|
|
643
815
|
}
|
|
644
816
|
function findInputLabel(el) {
|
|
645
|
-
|
|
646
|
-
|
|
817
|
+
const labels = el.labels;
|
|
818
|
+
if (labels && labels.length > 0) {
|
|
819
|
+
const labelText = labels[0].textContent?.trim();
|
|
647
820
|
if (labelText) return labelText.slice(0, 80);
|
|
648
821
|
}
|
|
649
822
|
const ariaLabel = el.getAttribute("aria-label");
|
|
@@ -661,11 +834,12 @@ function findInputLabel(el) {
|
|
|
661
834
|
}
|
|
662
835
|
const closestCell = el.closest('td, th, div[role="cell"], div[role="columnheader"]');
|
|
663
836
|
if (closestCell) {
|
|
837
|
+
const inputType = String(el.type || "").toLowerCase();
|
|
664
838
|
if (closestCell.tagName === "TH" || closestCell.getAttribute("role") === "columnheader") {
|
|
665
|
-
if (
|
|
839
|
+
if (inputType === "checkbox") return "Select All";
|
|
666
840
|
}
|
|
667
841
|
const row = el.closest('tr, div[role="row"]');
|
|
668
|
-
if (row &&
|
|
842
|
+
if (row && inputType === "checkbox") {
|
|
669
843
|
const cells = row.querySelectorAll('td, div[role="cell"]');
|
|
670
844
|
for (const cell of cells) {
|
|
671
845
|
if (cell === closestCell) continue;
|
|
@@ -696,11 +870,11 @@ function captureDomSummary(tags) {
|
|
|
696
870
|
const mapInput = (input) => {
|
|
697
871
|
const el = input;
|
|
698
872
|
return {
|
|
699
|
-
name: el
|
|
700
|
-
type: el
|
|
701
|
-
value:
|
|
702
|
-
disabled: el
|
|
703
|
-
placeholder: el
|
|
873
|
+
name: readEditableControlName(el),
|
|
874
|
+
type: readEditableControlType(el),
|
|
875
|
+
value: readEditableControlValue(el),
|
|
876
|
+
disabled: isEditableControlDisabled(el),
|
|
877
|
+
placeholder: readEditableControlPlaceholder(el),
|
|
704
878
|
fingerprint: generateFingerprint(el),
|
|
705
879
|
label: findInputLabel(el)
|
|
706
880
|
};
|
|
@@ -708,17 +882,14 @@ function captureDomSummary(tags) {
|
|
|
708
882
|
const formElements = safeQueryAll("form").slice(0, 5);
|
|
709
883
|
const formInputSet = /* @__PURE__ */ new Set();
|
|
710
884
|
const forms = formElements.map((form) => {
|
|
711
|
-
const inputs =
|
|
885
|
+
const inputs = collectEditableControls(form).slice(0, 15);
|
|
712
886
|
inputs.forEach((el) => formInputSet.add(el));
|
|
713
887
|
return {
|
|
714
888
|
id: form.id || form.getAttribute("name") || null,
|
|
715
889
|
inputs: inputs.map(mapInput)
|
|
716
890
|
};
|
|
717
891
|
});
|
|
718
|
-
const standaloneInputs =
|
|
719
|
-
const type = el.type;
|
|
720
|
-
return type !== "hidden";
|
|
721
|
-
}).slice(0, 20).map(mapInput);
|
|
892
|
+
const standaloneInputs = collectEditableControls(document).filter((el) => !el.closest("form") && !formInputSet.has(el)).slice(0, 20).map(mapInput);
|
|
722
893
|
if (standaloneInputs.length > 0) {
|
|
723
894
|
forms.push({ id: "__standalone__", inputs: standaloneInputs });
|
|
724
895
|
}
|
|
@@ -3358,6 +3529,9 @@ function compactTourForTransport(tour) {
|
|
|
3358
3529
|
};
|
|
3359
3530
|
}
|
|
3360
3531
|
|
|
3532
|
+
// src/hooks/useTourPlayback.ts
|
|
3533
|
+
init_editable_controls();
|
|
3534
|
+
|
|
3361
3535
|
// src/utils/tour-playback-guards.ts
|
|
3362
3536
|
var playbackOwners = /* @__PURE__ */ new Map();
|
|
3363
3537
|
function shouldExecuteTourCommandBatch(isPlaybackActive) {
|
|
@@ -3699,8 +3873,8 @@ async function performInteractiveFill(target, value) {
|
|
|
3699
3873
|
inputTarget.dispatchEvent(new Event("change", { bubbles: true }));
|
|
3700
3874
|
inputTarget.blur();
|
|
3701
3875
|
} else {
|
|
3702
|
-
const shadowInput =
|
|
3703
|
-
if (shadowInput) {
|
|
3876
|
+
const shadowInput = findEditableControlInShadowRoot(target);
|
|
3877
|
+
if (shadowInput && isValueBearingElement(shadowInput)) {
|
|
3704
3878
|
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
3705
3879
|
if (nativeSetter) nativeSetter.call(shadowInput, value);
|
|
3706
3880
|
else shadowInput.value = value;
|
|
@@ -3714,19 +3888,6 @@ async function performInteractiveFill(target, value) {
|
|
|
3714
3888
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
3715
3889
|
inputTarget.classList.remove("modelnex-agent-type");
|
|
3716
3890
|
}
|
|
3717
|
-
function findInputInShadowRoot(root) {
|
|
3718
|
-
if (root.shadowRoot) {
|
|
3719
|
-
const found = root.shadowRoot.querySelector('input:not([type="hidden"]):not([disabled]), textarea:not([disabled])');
|
|
3720
|
-
if (found) return found;
|
|
3721
|
-
}
|
|
3722
|
-
for (const child of Array.from(root.children)) {
|
|
3723
|
-
if (child instanceof HTMLElement) {
|
|
3724
|
-
const found = findInputInShadowRoot(child);
|
|
3725
|
-
if (found) return found;
|
|
3726
|
-
}
|
|
3727
|
-
}
|
|
3728
|
-
return null;
|
|
3729
|
-
}
|
|
3730
3891
|
function isTerminalAction(action) {
|
|
3731
3892
|
return [
|
|
3732
3893
|
"click_element",
|
|
@@ -3737,11 +3898,6 @@ function isTerminalAction(action) {
|
|
|
3737
3898
|
"end_tour"
|
|
3738
3899
|
].includes(action?.type);
|
|
3739
3900
|
}
|
|
3740
|
-
function isValueBearingElement(element) {
|
|
3741
|
-
return Boolean(
|
|
3742
|
-
element && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)
|
|
3743
|
-
);
|
|
3744
|
-
}
|
|
3745
3901
|
var DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS = 700;
|
|
3746
3902
|
var DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS = 250;
|
|
3747
3903
|
function getManualInputCommitPolicy(target) {
|
|
@@ -3757,59 +3913,10 @@ function getManualInputCommitPolicy(target) {
|
|
|
3757
3913
|
};
|
|
3758
3914
|
}
|
|
3759
3915
|
function resolveWaitTargetElement(element) {
|
|
3760
|
-
|
|
3761
|
-
return element;
|
|
3762
|
-
}
|
|
3763
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3764
|
-
return element;
|
|
3765
|
-
}
|
|
3766
|
-
if (element instanceof HTMLLabelElement && element.control instanceof HTMLElement) {
|
|
3767
|
-
return element.control;
|
|
3768
|
-
}
|
|
3769
|
-
const nestedControl = element.querySelector(
|
|
3770
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3771
|
-
);
|
|
3772
|
-
return nestedControl ?? element;
|
|
3916
|
+
return resolveEditableControlElement(element);
|
|
3773
3917
|
}
|
|
3774
3918
|
function readWaitTargetValue(element) {
|
|
3775
|
-
|
|
3776
|
-
return element.value.trim();
|
|
3777
|
-
}
|
|
3778
|
-
const genericValue = element.value;
|
|
3779
|
-
if (typeof genericValue === "string") {
|
|
3780
|
-
return genericValue.trim();
|
|
3781
|
-
}
|
|
3782
|
-
if (typeof genericValue === "number" && Number.isFinite(genericValue)) {
|
|
3783
|
-
return String(genericValue).trim();
|
|
3784
|
-
}
|
|
3785
|
-
if (element.isContentEditable) {
|
|
3786
|
-
return (element.textContent || "").trim();
|
|
3787
|
-
}
|
|
3788
|
-
const ariaValueText = element.getAttribute("aria-valuetext");
|
|
3789
|
-
if (typeof ariaValueText === "string" && ariaValueText.trim()) {
|
|
3790
|
-
return ariaValueText.trim();
|
|
3791
|
-
}
|
|
3792
|
-
const ariaValueNow = element.getAttribute("aria-valuenow");
|
|
3793
|
-
if (typeof ariaValueNow === "string" && ariaValueNow.trim()) {
|
|
3794
|
-
return ariaValueNow.trim();
|
|
3795
|
-
}
|
|
3796
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3797
|
-
const nestedControl = element.querySelector(
|
|
3798
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3799
|
-
);
|
|
3800
|
-
if (nestedControl && nestedControl !== element) {
|
|
3801
|
-
const nestedValue = readWaitTargetValue(nestedControl);
|
|
3802
|
-
if (nestedValue) {
|
|
3803
|
-
return nestedValue;
|
|
3804
|
-
}
|
|
3805
|
-
}
|
|
3806
|
-
return (element.textContent || "").trim();
|
|
3807
|
-
}
|
|
3808
|
-
const shadowInput = findInputInShadowRoot(element);
|
|
3809
|
-
if (shadowInput) {
|
|
3810
|
-
return shadowInput.value.trim();
|
|
3811
|
-
}
|
|
3812
|
-
return "";
|
|
3919
|
+
return readEditableControlValue(element, { maskPasswords: false });
|
|
3813
3920
|
}
|
|
3814
3921
|
function buildManualCompletionTranscript(step, eventName) {
|
|
3815
3922
|
const targetLabel = step?.element?.textContaining || step?.ask || step?.goal || step?.narration || "the highlighted step";
|
|
@@ -3826,7 +3933,10 @@ function isEditableWaitTarget(element) {
|
|
|
3826
3933
|
const target = resolveWaitTargetElement(element);
|
|
3827
3934
|
return isValueBearingElement(target) || target.isContentEditable || target.getAttribute("role") === "textbox" || target.getAttribute("role") === "combobox";
|
|
3828
3935
|
}
|
|
3829
|
-
function
|
|
3936
|
+
function shouldPreferHighlightedWaitTargetForInputWait(options) {
|
|
3937
|
+
return options.inputLikeWait && options.resolvedTargetExists && !options.resolvedTargetMatchesPreferred && options.preferredTargetEditable;
|
|
3938
|
+
}
|
|
3939
|
+
function createManualWaitForTarget(rawTarget, eventName, step, options = {}) {
|
|
3830
3940
|
const target = resolveWaitTargetElement(rawTarget);
|
|
3831
3941
|
const completionTranscript = buildManualCompletionTranscript(step, eventName);
|
|
3832
3942
|
const isInputLikeEvent = isInputLikeWait(eventName, step);
|
|
@@ -3868,6 +3978,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3868
3978
|
}
|
|
3869
3979
|
};
|
|
3870
3980
|
const scheduleIdleCommit = () => {
|
|
3981
|
+
options.onObservedInput?.();
|
|
3871
3982
|
const currentValue = readWaitTargetValue(target);
|
|
3872
3983
|
if (!currentValue) {
|
|
3873
3984
|
clearIdleCommit();
|
|
@@ -3880,6 +3991,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3880
3991
|
}, commitPolicy.idleCommitMs);
|
|
3881
3992
|
};
|
|
3882
3993
|
const handleImmediateCommitEvent = () => {
|
|
3994
|
+
options.onObservedInput?.();
|
|
3883
3995
|
const currentValue = readWaitTargetValue(target);
|
|
3884
3996
|
if (currentValue) {
|
|
3885
3997
|
clearIdleCommit();
|
|
@@ -3895,6 +4007,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3895
4007
|
}
|
|
3896
4008
|
if (typeof MutationObserver !== "undefined") {
|
|
3897
4009
|
observer = new MutationObserver(() => {
|
|
4010
|
+
options.onObservedInput?.();
|
|
3898
4011
|
scheduleIdleCommit();
|
|
3899
4012
|
});
|
|
3900
4013
|
observer.observe(target, {
|
|
@@ -4018,6 +4131,7 @@ function useTourPlayback({
|
|
|
4018
4131
|
const voiceInputResolveRef = (0, import_react12.useRef)(null);
|
|
4019
4132
|
const askOrFillRef = (0, import_react12.useRef)(null);
|
|
4020
4133
|
const pendingManualWaitCleanupRef = (0, import_react12.useRef)(null);
|
|
4134
|
+
const pendingManualInputSyncRef = (0, import_react12.useRef)(null);
|
|
4021
4135
|
const llmRespondingRef = (0, import_react12.useRef)(false);
|
|
4022
4136
|
const interruptedForQuestionRef = (0, import_react12.useRef)(false);
|
|
4023
4137
|
const pendingInputBufRef = (0, import_react12.useRef)(null);
|
|
@@ -4117,6 +4231,7 @@ function useTourPlayback({
|
|
|
4117
4231
|
}, { devMode: devModeRef.current });
|
|
4118
4232
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4119
4233
|
pendingManualWaitCleanupRef.current = null;
|
|
4234
|
+
clearPendingManualInputSync();
|
|
4120
4235
|
if (voiceInputResolveRef.current) {
|
|
4121
4236
|
const resolvePendingVoiceInput = voiceInputResolveRef.current;
|
|
4122
4237
|
voiceInputResolveRef.current = null;
|
|
@@ -4503,24 +4618,34 @@ function useTourPlayback({
|
|
|
4503
4618
|
const preferredWaitTarget = inputLikeWait ? batchPreferredWaitTarget ?? highlightedWaitTarget : highlightedWaitTarget;
|
|
4504
4619
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4505
4620
|
pendingManualWaitCleanupRef.current = null;
|
|
4621
|
+
clearPendingManualInputSync();
|
|
4506
4622
|
if (waitTargetHints) {
|
|
4507
4623
|
let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
|
|
4508
|
-
if (
|
|
4624
|
+
if (shouldPreferHighlightedWaitTargetForInputWait({
|
|
4625
|
+
inputLikeWait,
|
|
4626
|
+
resolvedTargetExists: Boolean(manualWaitTarget),
|
|
4627
|
+
resolvedTargetMatchesPreferred: manualWaitTarget === preferredWaitTarget,
|
|
4628
|
+
preferredTargetEditable: isEditableWaitTarget(preferredWaitTarget)
|
|
4629
|
+
})) {
|
|
4509
4630
|
manualWaitTarget = preferredWaitTarget;
|
|
4510
|
-
emitSdkDebugLog("[TourClient] wait_for_input
|
|
4631
|
+
emitSdkDebugLog("[TourClient] wait_for_input preferring highlighted editable target over recorded target", {
|
|
4511
4632
|
stepIndex: stepIndexRef.current,
|
|
4512
4633
|
event: waitEvent
|
|
4513
4634
|
}, { devMode: devModeRef.current });
|
|
4514
4635
|
}
|
|
4515
4636
|
if (manualWaitTarget) {
|
|
4516
|
-
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep
|
|
4637
|
+
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep, {
|
|
4638
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4639
|
+
});
|
|
4517
4640
|
manualWaitPromise = manualWait.promise;
|
|
4518
4641
|
manualWaitKind = manualWait.kind;
|
|
4519
4642
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
4520
4643
|
}
|
|
4521
4644
|
}
|
|
4522
4645
|
if (!manualWaitPromise && preferredWaitTarget) {
|
|
4523
|
-
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep
|
|
4646
|
+
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep, {
|
|
4647
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4648
|
+
});
|
|
4524
4649
|
manualWaitPromise = manualWait.promise;
|
|
4525
4650
|
manualWaitKind = manualWait.kind;
|
|
4526
4651
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4534,7 +4659,9 @@ function useTourPlayback({
|
|
|
4534
4659
|
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), [contenteditable="true"], [role="textbox"]'
|
|
4535
4660
|
);
|
|
4536
4661
|
if (firstInput) {
|
|
4537
|
-
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep
|
|
4662
|
+
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep, {
|
|
4663
|
+
onObservedInput: scheduleManualInputSync
|
|
4664
|
+
});
|
|
4538
4665
|
manualWaitPromise = manualWait.promise;
|
|
4539
4666
|
manualWaitKind = manualWait.kind;
|
|
4540
4667
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4580,20 +4707,33 @@ function useTourPlayback({
|
|
|
4580
4707
|
Promise.race([voiceOrTextWaitPromise, manualWaitPromise].filter(Boolean)).then(async (userText) => {
|
|
4581
4708
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4582
4709
|
pendingManualWaitCleanupRef.current = null;
|
|
4710
|
+
clearPendingManualInputSync();
|
|
4583
4711
|
voiceInputResolveRef.current = null;
|
|
4584
4712
|
setPlaybackState("executing");
|
|
4585
4713
|
const transcript = userText.trim();
|
|
4586
4714
|
if (!transcript) {
|
|
4587
4715
|
return;
|
|
4588
4716
|
}
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4717
|
+
try {
|
|
4718
|
+
const { waitForDomSettle: waitForDomSettle2 } = await Promise.resolve().then(() => (init_dom_sync(), dom_sync_exports));
|
|
4719
|
+
await waitForDomSettle2({ timeoutMs: 1500, debounceMs: 200 });
|
|
4720
|
+
await syncAOM();
|
|
4721
|
+
} catch (error) {
|
|
4722
|
+
emitSdkDebugLog("[TourClient] Failed to sync DOM before sending wait_for_input transcript", {
|
|
4723
|
+
stepIndex: stepIndexRef.current,
|
|
4724
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4725
|
+
}, { devMode: devModeRef.current });
|
|
4726
|
+
}
|
|
4592
4727
|
emitIfOpen("tour:user_input", {
|
|
4593
4728
|
transcript,
|
|
4594
4729
|
runId: runIdRef.current,
|
|
4595
4730
|
turnId: turnIdRef.current
|
|
4596
4731
|
});
|
|
4732
|
+
}).catch((error) => {
|
|
4733
|
+
emitSdkDebugLog("[TourClient] wait_for_input listener rejected", {
|
|
4734
|
+
stepIndex: stepIndexRef.current,
|
|
4735
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4736
|
+
}, { devMode: devModeRef.current });
|
|
4597
4737
|
});
|
|
4598
4738
|
return;
|
|
4599
4739
|
}
|
|
@@ -4784,6 +4924,24 @@ function useTourPlayback({
|
|
|
4784
4924
|
domSummary: captureDomSummary()
|
|
4785
4925
|
});
|
|
4786
4926
|
}, []);
|
|
4927
|
+
const scheduleManualInputSync = (0, import_react12.useCallback)(() => {
|
|
4928
|
+
if (pendingManualInputSyncRef.current) {
|
|
4929
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4930
|
+
}
|
|
4931
|
+
pendingManualInputSyncRef.current = setTimeout(async () => {
|
|
4932
|
+
pendingManualInputSyncRef.current = null;
|
|
4933
|
+
if (!isActiveRef.current) return;
|
|
4934
|
+
const { waitForDomSettle: waitForDomSettle2 } = await Promise.resolve().then(() => (init_dom_sync(), dom_sync_exports));
|
|
4935
|
+
await waitForDomSettle2({ timeoutMs: 600, debounceMs: 100 });
|
|
4936
|
+
await syncAOM();
|
|
4937
|
+
}, 150);
|
|
4938
|
+
}, [syncAOM]);
|
|
4939
|
+
const clearPendingManualInputSync = (0, import_react12.useCallback)(() => {
|
|
4940
|
+
if (pendingManualInputSyncRef.current) {
|
|
4941
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4942
|
+
pendingManualInputSyncRef.current = null;
|
|
4943
|
+
}
|
|
4944
|
+
}, []);
|
|
4787
4945
|
const interruptExecution = (0, import_react12.useCallback)((transcript) => {
|
|
4788
4946
|
if (!isSocketWritable(socketRef.current) || !isActiveRef.current) return false;
|
|
4789
4947
|
if (!commandInFlightRef.current && !voice.isSpeaking) return false;
|
|
@@ -4822,6 +4980,7 @@ function useTourPlayback({
|
|
|
4822
4980
|
activeCommandBatchIdRef.current = null;
|
|
4823
4981
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4824
4982
|
pendingManualWaitCleanupRef.current = null;
|
|
4983
|
+
clearPendingManualInputSync();
|
|
4825
4984
|
removeHighlight();
|
|
4826
4985
|
removeCaption();
|
|
4827
4986
|
voice.stopSpeaking();
|
|
@@ -4873,6 +5032,7 @@ function useTourPlayback({
|
|
|
4873
5032
|
removeCaption();
|
|
4874
5033
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4875
5034
|
pendingManualWaitCleanupRef.current = null;
|
|
5035
|
+
clearPendingManualInputSync();
|
|
4876
5036
|
if (!skipRequestedRef.current && userProfile?.userId && tourRef.current) {
|
|
4877
5037
|
markTourComplete(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, experienceType, websiteId);
|
|
4878
5038
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
collectEditableControls,
|
|
3
|
+
findEditableControlInShadowRoot,
|
|
4
|
+
generateMinifiedAOM,
|
|
5
|
+
isEditableControlDisabled,
|
|
6
|
+
isValueBearingElement,
|
|
7
|
+
readEditableControlName,
|
|
8
|
+
readEditableControlPlaceholder,
|
|
9
|
+
readEditableControlType,
|
|
10
|
+
readEditableControlValue,
|
|
11
|
+
resolveEditableControlElement
|
|
12
|
+
} from "./chunk-H4LUY7LI.mjs";
|
|
4
13
|
|
|
5
14
|
// src/index.ts
|
|
6
15
|
import React8, { useState as useState15, useCallback as useCallback14, useEffect as useEffect19, useMemo as useMemo5 } from "react";
|
|
@@ -431,8 +440,9 @@ function safeQueryAll(selector) {
|
|
|
431
440
|
}
|
|
432
441
|
}
|
|
433
442
|
function findInputLabel(el) {
|
|
434
|
-
|
|
435
|
-
|
|
443
|
+
const labels = el.labels;
|
|
444
|
+
if (labels && labels.length > 0) {
|
|
445
|
+
const labelText = labels[0].textContent?.trim();
|
|
436
446
|
if (labelText) return labelText.slice(0, 80);
|
|
437
447
|
}
|
|
438
448
|
const ariaLabel = el.getAttribute("aria-label");
|
|
@@ -450,11 +460,12 @@ function findInputLabel(el) {
|
|
|
450
460
|
}
|
|
451
461
|
const closestCell = el.closest('td, th, div[role="cell"], div[role="columnheader"]');
|
|
452
462
|
if (closestCell) {
|
|
463
|
+
const inputType = String(el.type || "").toLowerCase();
|
|
453
464
|
if (closestCell.tagName === "TH" || closestCell.getAttribute("role") === "columnheader") {
|
|
454
|
-
if (
|
|
465
|
+
if (inputType === "checkbox") return "Select All";
|
|
455
466
|
}
|
|
456
467
|
const row = el.closest('tr, div[role="row"]');
|
|
457
|
-
if (row &&
|
|
468
|
+
if (row && inputType === "checkbox") {
|
|
458
469
|
const cells = row.querySelectorAll('td, div[role="cell"]');
|
|
459
470
|
for (const cell of cells) {
|
|
460
471
|
if (cell === closestCell) continue;
|
|
@@ -485,11 +496,11 @@ function captureDomSummary(tags) {
|
|
|
485
496
|
const mapInput = (input) => {
|
|
486
497
|
const el = input;
|
|
487
498
|
return {
|
|
488
|
-
name: el
|
|
489
|
-
type: el
|
|
490
|
-
value:
|
|
491
|
-
disabled: el
|
|
492
|
-
placeholder: el
|
|
499
|
+
name: readEditableControlName(el),
|
|
500
|
+
type: readEditableControlType(el),
|
|
501
|
+
value: readEditableControlValue(el),
|
|
502
|
+
disabled: isEditableControlDisabled(el),
|
|
503
|
+
placeholder: readEditableControlPlaceholder(el),
|
|
493
504
|
fingerprint: generateFingerprint(el),
|
|
494
505
|
label: findInputLabel(el)
|
|
495
506
|
};
|
|
@@ -497,17 +508,14 @@ function captureDomSummary(tags) {
|
|
|
497
508
|
const formElements = safeQueryAll("form").slice(0, 5);
|
|
498
509
|
const formInputSet = /* @__PURE__ */ new Set();
|
|
499
510
|
const forms = formElements.map((form) => {
|
|
500
|
-
const inputs =
|
|
511
|
+
const inputs = collectEditableControls(form).slice(0, 15);
|
|
501
512
|
inputs.forEach((el) => formInputSet.add(el));
|
|
502
513
|
return {
|
|
503
514
|
id: form.id || form.getAttribute("name") || null,
|
|
504
515
|
inputs: inputs.map(mapInput)
|
|
505
516
|
};
|
|
506
517
|
});
|
|
507
|
-
const standaloneInputs =
|
|
508
|
-
const type = el.type;
|
|
509
|
-
return type !== "hidden";
|
|
510
|
-
}).slice(0, 20).map(mapInput);
|
|
518
|
+
const standaloneInputs = collectEditableControls(document).filter((el) => !el.closest("form") && !formInputSet.has(el)).slice(0, 20).map(mapInput);
|
|
511
519
|
if (standaloneInputs.length > 0) {
|
|
512
520
|
forms.push({ id: "__standalone__", inputs: standaloneInputs });
|
|
513
521
|
}
|
|
@@ -3488,8 +3496,8 @@ async function performInteractiveFill(target, value) {
|
|
|
3488
3496
|
inputTarget.dispatchEvent(new Event("change", { bubbles: true }));
|
|
3489
3497
|
inputTarget.blur();
|
|
3490
3498
|
} else {
|
|
3491
|
-
const shadowInput =
|
|
3492
|
-
if (shadowInput) {
|
|
3499
|
+
const shadowInput = findEditableControlInShadowRoot(target);
|
|
3500
|
+
if (shadowInput && isValueBearingElement(shadowInput)) {
|
|
3493
3501
|
const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
3494
3502
|
if (nativeSetter) nativeSetter.call(shadowInput, value);
|
|
3495
3503
|
else shadowInput.value = value;
|
|
@@ -3503,19 +3511,6 @@ async function performInteractiveFill(target, value) {
|
|
|
3503
3511
|
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
3504
3512
|
inputTarget.classList.remove("modelnex-agent-type");
|
|
3505
3513
|
}
|
|
3506
|
-
function findInputInShadowRoot(root) {
|
|
3507
|
-
if (root.shadowRoot) {
|
|
3508
|
-
const found = root.shadowRoot.querySelector('input:not([type="hidden"]):not([disabled]), textarea:not([disabled])');
|
|
3509
|
-
if (found) return found;
|
|
3510
|
-
}
|
|
3511
|
-
for (const child of Array.from(root.children)) {
|
|
3512
|
-
if (child instanceof HTMLElement) {
|
|
3513
|
-
const found = findInputInShadowRoot(child);
|
|
3514
|
-
if (found) return found;
|
|
3515
|
-
}
|
|
3516
|
-
}
|
|
3517
|
-
return null;
|
|
3518
|
-
}
|
|
3519
3514
|
function isTerminalAction(action) {
|
|
3520
3515
|
return [
|
|
3521
3516
|
"click_element",
|
|
@@ -3526,11 +3521,6 @@ function isTerminalAction(action) {
|
|
|
3526
3521
|
"end_tour"
|
|
3527
3522
|
].includes(action?.type);
|
|
3528
3523
|
}
|
|
3529
|
-
function isValueBearingElement(element) {
|
|
3530
|
-
return Boolean(
|
|
3531
|
-
element && (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)
|
|
3532
|
-
);
|
|
3533
|
-
}
|
|
3534
3524
|
var DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS = 700;
|
|
3535
3525
|
var DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS = 250;
|
|
3536
3526
|
function getManualInputCommitPolicy(target) {
|
|
@@ -3546,59 +3536,10 @@ function getManualInputCommitPolicy(target) {
|
|
|
3546
3536
|
};
|
|
3547
3537
|
}
|
|
3548
3538
|
function resolveWaitTargetElement(element) {
|
|
3549
|
-
|
|
3550
|
-
return element;
|
|
3551
|
-
}
|
|
3552
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3553
|
-
return element;
|
|
3554
|
-
}
|
|
3555
|
-
if (element instanceof HTMLLabelElement && element.control instanceof HTMLElement) {
|
|
3556
|
-
return element.control;
|
|
3557
|
-
}
|
|
3558
|
-
const nestedControl = element.querySelector(
|
|
3559
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3560
|
-
);
|
|
3561
|
-
return nestedControl ?? element;
|
|
3539
|
+
return resolveEditableControlElement(element);
|
|
3562
3540
|
}
|
|
3563
3541
|
function readWaitTargetValue(element) {
|
|
3564
|
-
|
|
3565
|
-
return element.value.trim();
|
|
3566
|
-
}
|
|
3567
|
-
const genericValue = element.value;
|
|
3568
|
-
if (typeof genericValue === "string") {
|
|
3569
|
-
return genericValue.trim();
|
|
3570
|
-
}
|
|
3571
|
-
if (typeof genericValue === "number" && Number.isFinite(genericValue)) {
|
|
3572
|
-
return String(genericValue).trim();
|
|
3573
|
-
}
|
|
3574
|
-
if (element.isContentEditable) {
|
|
3575
|
-
return (element.textContent || "").trim();
|
|
3576
|
-
}
|
|
3577
|
-
const ariaValueText = element.getAttribute("aria-valuetext");
|
|
3578
|
-
if (typeof ariaValueText === "string" && ariaValueText.trim()) {
|
|
3579
|
-
return ariaValueText.trim();
|
|
3580
|
-
}
|
|
3581
|
-
const ariaValueNow = element.getAttribute("aria-valuenow");
|
|
3582
|
-
if (typeof ariaValueNow === "string" && ariaValueNow.trim()) {
|
|
3583
|
-
return ariaValueNow.trim();
|
|
3584
|
-
}
|
|
3585
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3586
|
-
const nestedControl = element.querySelector(
|
|
3587
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3588
|
-
);
|
|
3589
|
-
if (nestedControl && nestedControl !== element) {
|
|
3590
|
-
const nestedValue = readWaitTargetValue(nestedControl);
|
|
3591
|
-
if (nestedValue) {
|
|
3592
|
-
return nestedValue;
|
|
3593
|
-
}
|
|
3594
|
-
}
|
|
3595
|
-
return (element.textContent || "").trim();
|
|
3596
|
-
}
|
|
3597
|
-
const shadowInput = findInputInShadowRoot(element);
|
|
3598
|
-
if (shadowInput) {
|
|
3599
|
-
return shadowInput.value.trim();
|
|
3600
|
-
}
|
|
3601
|
-
return "";
|
|
3542
|
+
return readEditableControlValue(element, { maskPasswords: false });
|
|
3602
3543
|
}
|
|
3603
3544
|
function buildManualCompletionTranscript(step, eventName) {
|
|
3604
3545
|
const targetLabel = step?.element?.textContaining || step?.ask || step?.goal || step?.narration || "the highlighted step";
|
|
@@ -3615,7 +3556,10 @@ function isEditableWaitTarget(element) {
|
|
|
3615
3556
|
const target = resolveWaitTargetElement(element);
|
|
3616
3557
|
return isValueBearingElement(target) || target.isContentEditable || target.getAttribute("role") === "textbox" || target.getAttribute("role") === "combobox";
|
|
3617
3558
|
}
|
|
3618
|
-
function
|
|
3559
|
+
function shouldPreferHighlightedWaitTargetForInputWait(options) {
|
|
3560
|
+
return options.inputLikeWait && options.resolvedTargetExists && !options.resolvedTargetMatchesPreferred && options.preferredTargetEditable;
|
|
3561
|
+
}
|
|
3562
|
+
function createManualWaitForTarget(rawTarget, eventName, step, options = {}) {
|
|
3619
3563
|
const target = resolveWaitTargetElement(rawTarget);
|
|
3620
3564
|
const completionTranscript = buildManualCompletionTranscript(step, eventName);
|
|
3621
3565
|
const isInputLikeEvent = isInputLikeWait(eventName, step);
|
|
@@ -3657,6 +3601,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3657
3601
|
}
|
|
3658
3602
|
};
|
|
3659
3603
|
const scheduleIdleCommit = () => {
|
|
3604
|
+
options.onObservedInput?.();
|
|
3660
3605
|
const currentValue = readWaitTargetValue(target);
|
|
3661
3606
|
if (!currentValue) {
|
|
3662
3607
|
clearIdleCommit();
|
|
@@ -3669,6 +3614,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3669
3614
|
}, commitPolicy.idleCommitMs);
|
|
3670
3615
|
};
|
|
3671
3616
|
const handleImmediateCommitEvent = () => {
|
|
3617
|
+
options.onObservedInput?.();
|
|
3672
3618
|
const currentValue = readWaitTargetValue(target);
|
|
3673
3619
|
if (currentValue) {
|
|
3674
3620
|
clearIdleCommit();
|
|
@@ -3684,6 +3630,7 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3684
3630
|
}
|
|
3685
3631
|
if (typeof MutationObserver !== "undefined") {
|
|
3686
3632
|
observer = new MutationObserver(() => {
|
|
3633
|
+
options.onObservedInput?.();
|
|
3687
3634
|
scheduleIdleCommit();
|
|
3688
3635
|
});
|
|
3689
3636
|
observer.observe(target, {
|
|
@@ -3807,6 +3754,7 @@ function useTourPlayback({
|
|
|
3807
3754
|
const voiceInputResolveRef = useRef8(null);
|
|
3808
3755
|
const askOrFillRef = useRef8(null);
|
|
3809
3756
|
const pendingManualWaitCleanupRef = useRef8(null);
|
|
3757
|
+
const pendingManualInputSyncRef = useRef8(null);
|
|
3810
3758
|
const llmRespondingRef = useRef8(false);
|
|
3811
3759
|
const interruptedForQuestionRef = useRef8(false);
|
|
3812
3760
|
const pendingInputBufRef = useRef8(null);
|
|
@@ -3906,6 +3854,7 @@ function useTourPlayback({
|
|
|
3906
3854
|
}, { devMode: devModeRef.current });
|
|
3907
3855
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
3908
3856
|
pendingManualWaitCleanupRef.current = null;
|
|
3857
|
+
clearPendingManualInputSync();
|
|
3909
3858
|
if (voiceInputResolveRef.current) {
|
|
3910
3859
|
const resolvePendingVoiceInput = voiceInputResolveRef.current;
|
|
3911
3860
|
voiceInputResolveRef.current = null;
|
|
@@ -3991,7 +3940,7 @@ function useTourPlayback({
|
|
|
3991
3940
|
resolve: async () => {
|
|
3992
3941
|
let targetEl = null;
|
|
3993
3942
|
if (params.uid) {
|
|
3994
|
-
const { getElementByUid } = await import("./aom-
|
|
3943
|
+
const { getElementByUid } = await import("./aom-LJNCLNXL.mjs");
|
|
3995
3944
|
targetEl = getElementByUid(params.uid);
|
|
3996
3945
|
}
|
|
3997
3946
|
if (!targetEl) {
|
|
@@ -4292,24 +4241,34 @@ function useTourPlayback({
|
|
|
4292
4241
|
const preferredWaitTarget = inputLikeWait ? batchPreferredWaitTarget ?? highlightedWaitTarget : highlightedWaitTarget;
|
|
4293
4242
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4294
4243
|
pendingManualWaitCleanupRef.current = null;
|
|
4244
|
+
clearPendingManualInputSync();
|
|
4295
4245
|
if (waitTargetHints) {
|
|
4296
4246
|
let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
|
|
4297
|
-
if (
|
|
4247
|
+
if (shouldPreferHighlightedWaitTargetForInputWait({
|
|
4248
|
+
inputLikeWait,
|
|
4249
|
+
resolvedTargetExists: Boolean(manualWaitTarget),
|
|
4250
|
+
resolvedTargetMatchesPreferred: manualWaitTarget === preferredWaitTarget,
|
|
4251
|
+
preferredTargetEditable: isEditableWaitTarget(preferredWaitTarget)
|
|
4252
|
+
})) {
|
|
4298
4253
|
manualWaitTarget = preferredWaitTarget;
|
|
4299
|
-
emitSdkDebugLog("[TourClient] wait_for_input
|
|
4254
|
+
emitSdkDebugLog("[TourClient] wait_for_input preferring highlighted editable target over recorded target", {
|
|
4300
4255
|
stepIndex: stepIndexRef.current,
|
|
4301
4256
|
event: waitEvent
|
|
4302
4257
|
}, { devMode: devModeRef.current });
|
|
4303
4258
|
}
|
|
4304
4259
|
if (manualWaitTarget) {
|
|
4305
|
-
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep
|
|
4260
|
+
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep, {
|
|
4261
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4262
|
+
});
|
|
4306
4263
|
manualWaitPromise = manualWait.promise;
|
|
4307
4264
|
manualWaitKind = manualWait.kind;
|
|
4308
4265
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
4309
4266
|
}
|
|
4310
4267
|
}
|
|
4311
4268
|
if (!manualWaitPromise && preferredWaitTarget) {
|
|
4312
|
-
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep
|
|
4269
|
+
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep, {
|
|
4270
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4271
|
+
});
|
|
4313
4272
|
manualWaitPromise = manualWait.promise;
|
|
4314
4273
|
manualWaitKind = manualWait.kind;
|
|
4315
4274
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4323,7 +4282,9 @@ function useTourPlayback({
|
|
|
4323
4282
|
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), [contenteditable="true"], [role="textbox"]'
|
|
4324
4283
|
);
|
|
4325
4284
|
if (firstInput) {
|
|
4326
|
-
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep
|
|
4285
|
+
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep, {
|
|
4286
|
+
onObservedInput: scheduleManualInputSync
|
|
4287
|
+
});
|
|
4327
4288
|
manualWaitPromise = manualWait.promise;
|
|
4328
4289
|
manualWaitKind = manualWait.kind;
|
|
4329
4290
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4369,20 +4330,33 @@ function useTourPlayback({
|
|
|
4369
4330
|
Promise.race([voiceOrTextWaitPromise, manualWaitPromise].filter(Boolean)).then(async (userText) => {
|
|
4370
4331
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4371
4332
|
pendingManualWaitCleanupRef.current = null;
|
|
4333
|
+
clearPendingManualInputSync();
|
|
4372
4334
|
voiceInputResolveRef.current = null;
|
|
4373
4335
|
setPlaybackState("executing");
|
|
4374
4336
|
const transcript = userText.trim();
|
|
4375
4337
|
if (!transcript) {
|
|
4376
4338
|
return;
|
|
4377
4339
|
}
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4340
|
+
try {
|
|
4341
|
+
const { waitForDomSettle } = await import("./dom-sync-GABDEODR.mjs");
|
|
4342
|
+
await waitForDomSettle({ timeoutMs: 1500, debounceMs: 200 });
|
|
4343
|
+
await syncAOM();
|
|
4344
|
+
} catch (error) {
|
|
4345
|
+
emitSdkDebugLog("[TourClient] Failed to sync DOM before sending wait_for_input transcript", {
|
|
4346
|
+
stepIndex: stepIndexRef.current,
|
|
4347
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4348
|
+
}, { devMode: devModeRef.current });
|
|
4349
|
+
}
|
|
4381
4350
|
emitIfOpen("tour:user_input", {
|
|
4382
4351
|
transcript,
|
|
4383
4352
|
runId: runIdRef.current,
|
|
4384
4353
|
turnId: turnIdRef.current
|
|
4385
4354
|
});
|
|
4355
|
+
}).catch((error) => {
|
|
4356
|
+
emitSdkDebugLog("[TourClient] wait_for_input listener rejected", {
|
|
4357
|
+
stepIndex: stepIndexRef.current,
|
|
4358
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4359
|
+
}, { devMode: devModeRef.current });
|
|
4386
4360
|
});
|
|
4387
4361
|
return;
|
|
4388
4362
|
}
|
|
@@ -4451,7 +4425,7 @@ function useTourPlayback({
|
|
|
4451
4425
|
void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
|
|
4452
4426
|
}
|
|
4453
4427
|
try {
|
|
4454
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4428
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-LJNCLNXL.mjs");
|
|
4455
4429
|
const aom = generateMinifiedAOM2();
|
|
4456
4430
|
if (socketRef.current === socket) {
|
|
4457
4431
|
emitSocketEvent(socket, "tour:sync_dom", {
|
|
@@ -4565,7 +4539,7 @@ function useTourPlayback({
|
|
|
4565
4539
|
}, [isActive, playbackState, voice.isListening, voice.isSpeaking]);
|
|
4566
4540
|
const syncAOM = useCallback7(async () => {
|
|
4567
4541
|
if (!isActiveRef.current) return;
|
|
4568
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4542
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-LJNCLNXL.mjs");
|
|
4569
4543
|
const aom = generateMinifiedAOM2();
|
|
4570
4544
|
emitSocketEvent(socketRef.current, "tour:sync_dom", {
|
|
4571
4545
|
url: window.location.pathname + window.location.search + window.location.hash,
|
|
@@ -4573,6 +4547,24 @@ function useTourPlayback({
|
|
|
4573
4547
|
domSummary: captureDomSummary()
|
|
4574
4548
|
});
|
|
4575
4549
|
}, []);
|
|
4550
|
+
const scheduleManualInputSync = useCallback7(() => {
|
|
4551
|
+
if (pendingManualInputSyncRef.current) {
|
|
4552
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4553
|
+
}
|
|
4554
|
+
pendingManualInputSyncRef.current = setTimeout(async () => {
|
|
4555
|
+
pendingManualInputSyncRef.current = null;
|
|
4556
|
+
if (!isActiveRef.current) return;
|
|
4557
|
+
const { waitForDomSettle } = await import("./dom-sync-GABDEODR.mjs");
|
|
4558
|
+
await waitForDomSettle({ timeoutMs: 600, debounceMs: 100 });
|
|
4559
|
+
await syncAOM();
|
|
4560
|
+
}, 150);
|
|
4561
|
+
}, [syncAOM]);
|
|
4562
|
+
const clearPendingManualInputSync = useCallback7(() => {
|
|
4563
|
+
if (pendingManualInputSyncRef.current) {
|
|
4564
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4565
|
+
pendingManualInputSyncRef.current = null;
|
|
4566
|
+
}
|
|
4567
|
+
}, []);
|
|
4576
4568
|
const interruptExecution = useCallback7((transcript) => {
|
|
4577
4569
|
if (!isSocketWritable(socketRef.current) || !isActiveRef.current) return false;
|
|
4578
4570
|
if (!commandInFlightRef.current && !voice.isSpeaking) return false;
|
|
@@ -4611,6 +4603,7 @@ function useTourPlayback({
|
|
|
4611
4603
|
activeCommandBatchIdRef.current = null;
|
|
4612
4604
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4613
4605
|
pendingManualWaitCleanupRef.current = null;
|
|
4606
|
+
clearPendingManualInputSync();
|
|
4614
4607
|
removeHighlight();
|
|
4615
4608
|
removeCaption();
|
|
4616
4609
|
voice.stopSpeaking();
|
|
@@ -4662,6 +4655,7 @@ function useTourPlayback({
|
|
|
4662
4655
|
removeCaption();
|
|
4663
4656
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4664
4657
|
pendingManualWaitCleanupRef.current = null;
|
|
4658
|
+
clearPendingManualInputSync();
|
|
4665
4659
|
if (!skipRequestedRef.current && userProfile?.userId && tourRef.current) {
|
|
4666
4660
|
markTourComplete(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, experienceType, websiteId);
|
|
4667
4661
|
}
|
package/package.json
CHANGED
package/dist/chunk-N65UEB6X.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// src/utils/aom.ts
|
|
2
|
-
var uidMap = /* @__PURE__ */ new Map();
|
|
3
|
-
var nextUid = 1;
|
|
4
|
-
function generateMinifiedAOM() {
|
|
5
|
-
uidMap.clear();
|
|
6
|
-
nextUid = 1;
|
|
7
|
-
const interactives = document.querySelectorAll(
|
|
8
|
-
'button, a, input, select, textarea, [role="button"], [role="link"], [role="tab"], [role="menuitem"], [role="option"]'
|
|
9
|
-
);
|
|
10
|
-
const nodes = [];
|
|
11
|
-
interactives.forEach((el) => {
|
|
12
|
-
if (!el.offsetParent && (el.offsetWidth === 0 || el.offsetHeight === 0)) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
if (el.closest("#modelnex-studio-root") || el.closest("#modelnex-active-agent-root")) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
const uid = `node:${nextUid++}`;
|
|
19
|
-
uidMap.set(uid, el);
|
|
20
|
-
let text = (el.textContent || "").replace(/\s+/g, " ").trim();
|
|
21
|
-
const ariaLabel = el.getAttribute("aria-label");
|
|
22
|
-
const placeholder = el.getAttribute("placeholder");
|
|
23
|
-
let value = void 0;
|
|
24
|
-
if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement) {
|
|
25
|
-
value = el.value;
|
|
26
|
-
}
|
|
27
|
-
let role = el.tagName.toLowerCase();
|
|
28
|
-
if (el.hasAttribute("role")) {
|
|
29
|
-
role = el.getAttribute("role");
|
|
30
|
-
} else if (role === "a") {
|
|
31
|
-
role = "link";
|
|
32
|
-
} else if (el instanceof HTMLInputElement) {
|
|
33
|
-
role = el.type ? `input[${el.type}]` : "input";
|
|
34
|
-
}
|
|
35
|
-
const node = { uid, role };
|
|
36
|
-
const displayLabel = ariaLabel || text || placeholder;
|
|
37
|
-
if (displayLabel) {
|
|
38
|
-
node.text = displayLabel.substring(0, 100);
|
|
39
|
-
}
|
|
40
|
-
if (el.getAttribute("name")) {
|
|
41
|
-
node.name = el.getAttribute("name");
|
|
42
|
-
}
|
|
43
|
-
if (value) {
|
|
44
|
-
node.value = value.substring(0, 100);
|
|
45
|
-
}
|
|
46
|
-
if (el instanceof HTMLAnchorElement && el.href) {
|
|
47
|
-
try {
|
|
48
|
-
const url = new URL(el.href);
|
|
49
|
-
node.href = url.pathname + url.search + url.hash;
|
|
50
|
-
} catch {
|
|
51
|
-
node.href = el.getAttribute("href");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
nodes.push(node);
|
|
55
|
-
});
|
|
56
|
-
return { nodes };
|
|
57
|
-
}
|
|
58
|
-
function getElementByUid(uid) {
|
|
59
|
-
return uidMap.get(uid) || null;
|
|
60
|
-
}
|
|
61
|
-
function clearAOMMap() {
|
|
62
|
-
uidMap.clear();
|
|
63
|
-
nextUid = 1;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export {
|
|
67
|
-
generateMinifiedAOM,
|
|
68
|
-
getElementByUid,
|
|
69
|
-
clearAOMMap
|
|
70
|
-
};
|