@modelnex/sdk 0.5.39 → 0.5.41
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 +307 -73
- package/dist/index.mjs +134 -66
- 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,34 +3898,25 @@ function isTerminalAction(action) {
|
|
|
3737
3898
|
"end_tour"
|
|
3738
3899
|
].includes(action?.type);
|
|
3739
3900
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
);
|
|
3901
|
+
var DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS = 700;
|
|
3902
|
+
var DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS = 250;
|
|
3903
|
+
function getManualInputCommitPolicy(target) {
|
|
3904
|
+
const tagName = String(target.tagName || "").toLowerCase();
|
|
3905
|
+
const role = String(target.role || "").toLowerCase();
|
|
3906
|
+
const inputType = String(target.type || "").toLowerCase();
|
|
3907
|
+
const isContentEditable = Boolean(target.isContentEditable);
|
|
3908
|
+
const isSelectionLike = tagName === "select" || role === "combobox" || ["checkbox", "radio", "range", "color", "date", "datetime-local", "month", "time", "week"].includes(inputType);
|
|
3909
|
+
return {
|
|
3910
|
+
idleCommitMs: isSelectionLike ? DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS : DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS,
|
|
3911
|
+
commitOnBlur: !isSelectionLike || tagName === "select" || role === "combobox" || isContentEditable,
|
|
3912
|
+
commitOnChange: true
|
|
3913
|
+
};
|
|
3744
3914
|
}
|
|
3745
3915
|
function resolveWaitTargetElement(element) {
|
|
3746
|
-
|
|
3747
|
-
return element;
|
|
3748
|
-
}
|
|
3749
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3750
|
-
return element;
|
|
3751
|
-
}
|
|
3752
|
-
if (element instanceof HTMLLabelElement && element.control instanceof HTMLElement) {
|
|
3753
|
-
return element.control;
|
|
3754
|
-
}
|
|
3755
|
-
const nestedControl = element.querySelector(
|
|
3756
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3757
|
-
);
|
|
3758
|
-
return nestedControl ?? element;
|
|
3916
|
+
return resolveEditableControlElement(element);
|
|
3759
3917
|
}
|
|
3760
3918
|
function readWaitTargetValue(element) {
|
|
3761
|
-
|
|
3762
|
-
return element.value.trim();
|
|
3763
|
-
}
|
|
3764
|
-
if (element.isContentEditable) {
|
|
3765
|
-
return (element.textContent || "").trim();
|
|
3766
|
-
}
|
|
3767
|
-
return "";
|
|
3919
|
+
return readEditableControlValue(element, { maskPasswords: false });
|
|
3768
3920
|
}
|
|
3769
3921
|
function buildManualCompletionTranscript(step, eventName) {
|
|
3770
3922
|
const targetLabel = step?.element?.textContaining || step?.ask || step?.goal || step?.narration || "the highlighted step";
|
|
@@ -3781,10 +3933,16 @@ function isEditableWaitTarget(element) {
|
|
|
3781
3933
|
const target = resolveWaitTargetElement(element);
|
|
3782
3934
|
return isValueBearingElement(target) || target.isContentEditable || target.getAttribute("role") === "textbox" || target.getAttribute("role") === "combobox";
|
|
3783
3935
|
}
|
|
3784
|
-
function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
3936
|
+
function createManualWaitForTarget(rawTarget, eventName, step, options = {}) {
|
|
3785
3937
|
const target = resolveWaitTargetElement(rawTarget);
|
|
3786
3938
|
const completionTranscript = buildManualCompletionTranscript(step, eventName);
|
|
3787
3939
|
const isInputLikeEvent = isInputLikeWait(eventName, step);
|
|
3940
|
+
const commitPolicy = getManualInputCommitPolicy({
|
|
3941
|
+
tagName: target.tagName,
|
|
3942
|
+
role: target.getAttribute("role"),
|
|
3943
|
+
type: target.type ?? null,
|
|
3944
|
+
isContentEditable: target.isContentEditable
|
|
3945
|
+
});
|
|
3788
3946
|
if (isInputLikeEvent && readWaitTargetValue(target)) {
|
|
3789
3947
|
const initialValue = readWaitTargetValue(target);
|
|
3790
3948
|
return {
|
|
@@ -3796,6 +3954,8 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3796
3954
|
let cleanup = () => void 0;
|
|
3797
3955
|
const promise = new Promise((resolve) => {
|
|
3798
3956
|
const listeners2 = [];
|
|
3957
|
+
let observer = null;
|
|
3958
|
+
let idleTimer = null;
|
|
3799
3959
|
let settled = false;
|
|
3800
3960
|
const finish = () => {
|
|
3801
3961
|
if (settled) return;
|
|
@@ -3808,20 +3968,64 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3808
3968
|
listeners2.push({ node, type, handler });
|
|
3809
3969
|
};
|
|
3810
3970
|
if (isInputLikeEvent) {
|
|
3811
|
-
const
|
|
3971
|
+
const clearIdleCommit = () => {
|
|
3972
|
+
if (idleTimer) {
|
|
3973
|
+
clearTimeout(idleTimer);
|
|
3974
|
+
idleTimer = null;
|
|
3975
|
+
}
|
|
3976
|
+
};
|
|
3977
|
+
const scheduleIdleCommit = () => {
|
|
3978
|
+
options.onObservedInput?.();
|
|
3979
|
+
const currentValue = readWaitTargetValue(target);
|
|
3980
|
+
if (!currentValue) {
|
|
3981
|
+
clearIdleCommit();
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
clearIdleCommit();
|
|
3985
|
+
idleTimer = setTimeout(() => {
|
|
3986
|
+
idleTimer = null;
|
|
3987
|
+
finish();
|
|
3988
|
+
}, commitPolicy.idleCommitMs);
|
|
3989
|
+
};
|
|
3990
|
+
const handleImmediateCommitEvent = () => {
|
|
3991
|
+
options.onObservedInput?.();
|
|
3812
3992
|
const currentValue = readWaitTargetValue(target);
|
|
3813
3993
|
if (currentValue) {
|
|
3994
|
+
clearIdleCommit();
|
|
3814
3995
|
finish();
|
|
3815
3996
|
}
|
|
3816
3997
|
};
|
|
3817
|
-
addListener(target, "input",
|
|
3818
|
-
|
|
3819
|
-
|
|
3998
|
+
addListener(target, "input", scheduleIdleCommit);
|
|
3999
|
+
if (commitPolicy.commitOnChange) {
|
|
4000
|
+
addListener(target, "change", handleImmediateCommitEvent);
|
|
4001
|
+
}
|
|
4002
|
+
if (commitPolicy.commitOnBlur) {
|
|
4003
|
+
addListener(target, "blur", handleImmediateCommitEvent);
|
|
4004
|
+
}
|
|
4005
|
+
if (typeof MutationObserver !== "undefined") {
|
|
4006
|
+
observer = new MutationObserver(() => {
|
|
4007
|
+
options.onObservedInput?.();
|
|
4008
|
+
scheduleIdleCommit();
|
|
4009
|
+
});
|
|
4010
|
+
observer.observe(target, {
|
|
4011
|
+
subtree: true,
|
|
4012
|
+
childList: true,
|
|
4013
|
+
characterData: true,
|
|
4014
|
+
attributes: true,
|
|
4015
|
+
attributeFilter: ["value", "aria-valuetext", "aria-valuenow", "aria-activedescendant"]
|
|
4016
|
+
});
|
|
4017
|
+
}
|
|
3820
4018
|
} else {
|
|
3821
4019
|
const handleActionEvent = () => finish();
|
|
3822
4020
|
addListener(target, eventName, handleActionEvent);
|
|
3823
4021
|
}
|
|
3824
4022
|
cleanup = () => {
|
|
4023
|
+
if (idleTimer) {
|
|
4024
|
+
clearTimeout(idleTimer);
|
|
4025
|
+
idleTimer = null;
|
|
4026
|
+
}
|
|
4027
|
+
observer?.disconnect();
|
|
4028
|
+
observer = null;
|
|
3825
4029
|
listeners2.forEach(({ node, type, handler }) => node.removeEventListener(type, handler));
|
|
3826
4030
|
listeners2.length = 0;
|
|
3827
4031
|
};
|
|
@@ -3924,6 +4128,7 @@ function useTourPlayback({
|
|
|
3924
4128
|
const voiceInputResolveRef = (0, import_react12.useRef)(null);
|
|
3925
4129
|
const askOrFillRef = (0, import_react12.useRef)(null);
|
|
3926
4130
|
const pendingManualWaitCleanupRef = (0, import_react12.useRef)(null);
|
|
4131
|
+
const pendingManualInputSyncRef = (0, import_react12.useRef)(null);
|
|
3927
4132
|
const llmRespondingRef = (0, import_react12.useRef)(false);
|
|
3928
4133
|
const interruptedForQuestionRef = (0, import_react12.useRef)(false);
|
|
3929
4134
|
const pendingInputBufRef = (0, import_react12.useRef)(null);
|
|
@@ -4023,6 +4228,7 @@ function useTourPlayback({
|
|
|
4023
4228
|
}, { devMode: devModeRef.current });
|
|
4024
4229
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4025
4230
|
pendingManualWaitCleanupRef.current = null;
|
|
4231
|
+
clearPendingManualInputSync();
|
|
4026
4232
|
if (voiceInputResolveRef.current) {
|
|
4027
4233
|
const resolvePendingVoiceInput = voiceInputResolveRef.current;
|
|
4028
4234
|
voiceInputResolveRef.current = null;
|
|
@@ -4409,6 +4615,7 @@ function useTourPlayback({
|
|
|
4409
4615
|
const preferredWaitTarget = inputLikeWait ? batchPreferredWaitTarget ?? highlightedWaitTarget : highlightedWaitTarget;
|
|
4410
4616
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4411
4617
|
pendingManualWaitCleanupRef.current = null;
|
|
4618
|
+
clearPendingManualInputSync();
|
|
4412
4619
|
if (waitTargetHints) {
|
|
4413
4620
|
let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
|
|
4414
4621
|
if (inputLikeWait && preferredWaitTarget && manualWaitTarget && manualWaitTarget !== preferredWaitTarget && !isEditableWaitTarget(manualWaitTarget) && isEditableWaitTarget(preferredWaitTarget)) {
|
|
@@ -4419,14 +4626,18 @@ function useTourPlayback({
|
|
|
4419
4626
|
}, { devMode: devModeRef.current });
|
|
4420
4627
|
}
|
|
4421
4628
|
if (manualWaitTarget) {
|
|
4422
|
-
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep
|
|
4629
|
+
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep, {
|
|
4630
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4631
|
+
});
|
|
4423
4632
|
manualWaitPromise = manualWait.promise;
|
|
4424
4633
|
manualWaitKind = manualWait.kind;
|
|
4425
4634
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
4426
4635
|
}
|
|
4427
4636
|
}
|
|
4428
4637
|
if (!manualWaitPromise && preferredWaitTarget) {
|
|
4429
|
-
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep
|
|
4638
|
+
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep, {
|
|
4639
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4640
|
+
});
|
|
4430
4641
|
manualWaitPromise = manualWait.promise;
|
|
4431
4642
|
manualWaitKind = manualWait.kind;
|
|
4432
4643
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4440,7 +4651,9 @@ function useTourPlayback({
|
|
|
4440
4651
|
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), [contenteditable="true"], [role="textbox"]'
|
|
4441
4652
|
);
|
|
4442
4653
|
if (firstInput) {
|
|
4443
|
-
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep
|
|
4654
|
+
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep, {
|
|
4655
|
+
onObservedInput: scheduleManualInputSync
|
|
4656
|
+
});
|
|
4444
4657
|
manualWaitPromise = manualWait.promise;
|
|
4445
4658
|
manualWaitKind = manualWait.kind;
|
|
4446
4659
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4486,6 +4699,7 @@ function useTourPlayback({
|
|
|
4486
4699
|
Promise.race([voiceOrTextWaitPromise, manualWaitPromise].filter(Boolean)).then(async (userText) => {
|
|
4487
4700
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4488
4701
|
pendingManualWaitCleanupRef.current = null;
|
|
4702
|
+
clearPendingManualInputSync();
|
|
4489
4703
|
voiceInputResolveRef.current = null;
|
|
4490
4704
|
setPlaybackState("executing");
|
|
4491
4705
|
const transcript = userText.trim();
|
|
@@ -4690,6 +4904,24 @@ function useTourPlayback({
|
|
|
4690
4904
|
domSummary: captureDomSummary()
|
|
4691
4905
|
});
|
|
4692
4906
|
}, []);
|
|
4907
|
+
const scheduleManualInputSync = (0, import_react12.useCallback)(() => {
|
|
4908
|
+
if (pendingManualInputSyncRef.current) {
|
|
4909
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4910
|
+
}
|
|
4911
|
+
pendingManualInputSyncRef.current = setTimeout(async () => {
|
|
4912
|
+
pendingManualInputSyncRef.current = null;
|
|
4913
|
+
if (!isActiveRef.current) return;
|
|
4914
|
+
const { waitForDomSettle: waitForDomSettle2 } = await Promise.resolve().then(() => (init_dom_sync(), dom_sync_exports));
|
|
4915
|
+
await waitForDomSettle2({ timeoutMs: 600, debounceMs: 100 });
|
|
4916
|
+
await syncAOM();
|
|
4917
|
+
}, 150);
|
|
4918
|
+
}, [syncAOM]);
|
|
4919
|
+
const clearPendingManualInputSync = (0, import_react12.useCallback)(() => {
|
|
4920
|
+
if (pendingManualInputSyncRef.current) {
|
|
4921
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4922
|
+
pendingManualInputSyncRef.current = null;
|
|
4923
|
+
}
|
|
4924
|
+
}, []);
|
|
4693
4925
|
const interruptExecution = (0, import_react12.useCallback)((transcript) => {
|
|
4694
4926
|
if (!isSocketWritable(socketRef.current) || !isActiveRef.current) return false;
|
|
4695
4927
|
if (!commandInFlightRef.current && !voice.isSpeaking) return false;
|
|
@@ -4728,6 +4960,7 @@ function useTourPlayback({
|
|
|
4728
4960
|
activeCommandBatchIdRef.current = null;
|
|
4729
4961
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4730
4962
|
pendingManualWaitCleanupRef.current = null;
|
|
4963
|
+
clearPendingManualInputSync();
|
|
4731
4964
|
removeHighlight();
|
|
4732
4965
|
removeCaption();
|
|
4733
4966
|
voice.stopSpeaking();
|
|
@@ -4779,6 +5012,7 @@ function useTourPlayback({
|
|
|
4779
5012
|
removeCaption();
|
|
4780
5013
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4781
5014
|
pendingManualWaitCleanupRef.current = null;
|
|
5015
|
+
clearPendingManualInputSync();
|
|
4782
5016
|
if (!skipRequestedRef.current && userProfile?.userId && tourRef.current) {
|
|
4783
5017
|
markTourComplete(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, experienceType, websiteId);
|
|
4784
5018
|
}
|
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,34 +3521,25 @@ function isTerminalAction(action) {
|
|
|
3526
3521
|
"end_tour"
|
|
3527
3522
|
].includes(action?.type);
|
|
3528
3523
|
}
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
);
|
|
3524
|
+
var DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS = 700;
|
|
3525
|
+
var DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS = 250;
|
|
3526
|
+
function getManualInputCommitPolicy(target) {
|
|
3527
|
+
const tagName = String(target.tagName || "").toLowerCase();
|
|
3528
|
+
const role = String(target.role || "").toLowerCase();
|
|
3529
|
+
const inputType = String(target.type || "").toLowerCase();
|
|
3530
|
+
const isContentEditable = Boolean(target.isContentEditable);
|
|
3531
|
+
const isSelectionLike = tagName === "select" || role === "combobox" || ["checkbox", "radio", "range", "color", "date", "datetime-local", "month", "time", "week"].includes(inputType);
|
|
3532
|
+
return {
|
|
3533
|
+
idleCommitMs: isSelectionLike ? DEFAULT_SELECTION_INPUT_IDLE_COMMIT_MS : DEFAULT_TEXT_INPUT_IDLE_COMMIT_MS,
|
|
3534
|
+
commitOnBlur: !isSelectionLike || tagName === "select" || role === "combobox" || isContentEditable,
|
|
3535
|
+
commitOnChange: true
|
|
3536
|
+
};
|
|
3533
3537
|
}
|
|
3534
3538
|
function resolveWaitTargetElement(element) {
|
|
3535
|
-
|
|
3536
|
-
return element;
|
|
3537
|
-
}
|
|
3538
|
-
if (element.getAttribute("role") === "textbox" || element.getAttribute("role") === "combobox") {
|
|
3539
|
-
return element;
|
|
3540
|
-
}
|
|
3541
|
-
if (element instanceof HTMLLabelElement && element.control instanceof HTMLElement) {
|
|
3542
|
-
return element.control;
|
|
3543
|
-
}
|
|
3544
|
-
const nestedControl = element.querySelector(
|
|
3545
|
-
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), [contenteditable="true"], [role="textbox"], [role="combobox"]'
|
|
3546
|
-
);
|
|
3547
|
-
return nestedControl ?? element;
|
|
3539
|
+
return resolveEditableControlElement(element);
|
|
3548
3540
|
}
|
|
3549
3541
|
function readWaitTargetValue(element) {
|
|
3550
|
-
|
|
3551
|
-
return element.value.trim();
|
|
3552
|
-
}
|
|
3553
|
-
if (element.isContentEditable) {
|
|
3554
|
-
return (element.textContent || "").trim();
|
|
3555
|
-
}
|
|
3556
|
-
return "";
|
|
3542
|
+
return readEditableControlValue(element, { maskPasswords: false });
|
|
3557
3543
|
}
|
|
3558
3544
|
function buildManualCompletionTranscript(step, eventName) {
|
|
3559
3545
|
const targetLabel = step?.element?.textContaining || step?.ask || step?.goal || step?.narration || "the highlighted step";
|
|
@@ -3570,10 +3556,16 @@ function isEditableWaitTarget(element) {
|
|
|
3570
3556
|
const target = resolveWaitTargetElement(element);
|
|
3571
3557
|
return isValueBearingElement(target) || target.isContentEditable || target.getAttribute("role") === "textbox" || target.getAttribute("role") === "combobox";
|
|
3572
3558
|
}
|
|
3573
|
-
function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
3559
|
+
function createManualWaitForTarget(rawTarget, eventName, step, options = {}) {
|
|
3574
3560
|
const target = resolveWaitTargetElement(rawTarget);
|
|
3575
3561
|
const completionTranscript = buildManualCompletionTranscript(step, eventName);
|
|
3576
3562
|
const isInputLikeEvent = isInputLikeWait(eventName, step);
|
|
3563
|
+
const commitPolicy = getManualInputCommitPolicy({
|
|
3564
|
+
tagName: target.tagName,
|
|
3565
|
+
role: target.getAttribute("role"),
|
|
3566
|
+
type: target.type ?? null,
|
|
3567
|
+
isContentEditable: target.isContentEditable
|
|
3568
|
+
});
|
|
3577
3569
|
if (isInputLikeEvent && readWaitTargetValue(target)) {
|
|
3578
3570
|
const initialValue = readWaitTargetValue(target);
|
|
3579
3571
|
return {
|
|
@@ -3585,6 +3577,8 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3585
3577
|
let cleanup = () => void 0;
|
|
3586
3578
|
const promise = new Promise((resolve) => {
|
|
3587
3579
|
const listeners2 = [];
|
|
3580
|
+
let observer = null;
|
|
3581
|
+
let idleTimer = null;
|
|
3588
3582
|
let settled = false;
|
|
3589
3583
|
const finish = () => {
|
|
3590
3584
|
if (settled) return;
|
|
@@ -3597,20 +3591,64 @@ function createManualWaitForTarget(rawTarget, eventName, step) {
|
|
|
3597
3591
|
listeners2.push({ node, type, handler });
|
|
3598
3592
|
};
|
|
3599
3593
|
if (isInputLikeEvent) {
|
|
3600
|
-
const
|
|
3594
|
+
const clearIdleCommit = () => {
|
|
3595
|
+
if (idleTimer) {
|
|
3596
|
+
clearTimeout(idleTimer);
|
|
3597
|
+
idleTimer = null;
|
|
3598
|
+
}
|
|
3599
|
+
};
|
|
3600
|
+
const scheduleIdleCommit = () => {
|
|
3601
|
+
options.onObservedInput?.();
|
|
3602
|
+
const currentValue = readWaitTargetValue(target);
|
|
3603
|
+
if (!currentValue) {
|
|
3604
|
+
clearIdleCommit();
|
|
3605
|
+
return;
|
|
3606
|
+
}
|
|
3607
|
+
clearIdleCommit();
|
|
3608
|
+
idleTimer = setTimeout(() => {
|
|
3609
|
+
idleTimer = null;
|
|
3610
|
+
finish();
|
|
3611
|
+
}, commitPolicy.idleCommitMs);
|
|
3612
|
+
};
|
|
3613
|
+
const handleImmediateCommitEvent = () => {
|
|
3614
|
+
options.onObservedInput?.();
|
|
3601
3615
|
const currentValue = readWaitTargetValue(target);
|
|
3602
3616
|
if (currentValue) {
|
|
3617
|
+
clearIdleCommit();
|
|
3603
3618
|
finish();
|
|
3604
3619
|
}
|
|
3605
3620
|
};
|
|
3606
|
-
addListener(target, "input",
|
|
3607
|
-
|
|
3608
|
-
|
|
3621
|
+
addListener(target, "input", scheduleIdleCommit);
|
|
3622
|
+
if (commitPolicy.commitOnChange) {
|
|
3623
|
+
addListener(target, "change", handleImmediateCommitEvent);
|
|
3624
|
+
}
|
|
3625
|
+
if (commitPolicy.commitOnBlur) {
|
|
3626
|
+
addListener(target, "blur", handleImmediateCommitEvent);
|
|
3627
|
+
}
|
|
3628
|
+
if (typeof MutationObserver !== "undefined") {
|
|
3629
|
+
observer = new MutationObserver(() => {
|
|
3630
|
+
options.onObservedInput?.();
|
|
3631
|
+
scheduleIdleCommit();
|
|
3632
|
+
});
|
|
3633
|
+
observer.observe(target, {
|
|
3634
|
+
subtree: true,
|
|
3635
|
+
childList: true,
|
|
3636
|
+
characterData: true,
|
|
3637
|
+
attributes: true,
|
|
3638
|
+
attributeFilter: ["value", "aria-valuetext", "aria-valuenow", "aria-activedescendant"]
|
|
3639
|
+
});
|
|
3640
|
+
}
|
|
3609
3641
|
} else {
|
|
3610
3642
|
const handleActionEvent = () => finish();
|
|
3611
3643
|
addListener(target, eventName, handleActionEvent);
|
|
3612
3644
|
}
|
|
3613
3645
|
cleanup = () => {
|
|
3646
|
+
if (idleTimer) {
|
|
3647
|
+
clearTimeout(idleTimer);
|
|
3648
|
+
idleTimer = null;
|
|
3649
|
+
}
|
|
3650
|
+
observer?.disconnect();
|
|
3651
|
+
observer = null;
|
|
3614
3652
|
listeners2.forEach(({ node, type, handler }) => node.removeEventListener(type, handler));
|
|
3615
3653
|
listeners2.length = 0;
|
|
3616
3654
|
};
|
|
@@ -3713,6 +3751,7 @@ function useTourPlayback({
|
|
|
3713
3751
|
const voiceInputResolveRef = useRef8(null);
|
|
3714
3752
|
const askOrFillRef = useRef8(null);
|
|
3715
3753
|
const pendingManualWaitCleanupRef = useRef8(null);
|
|
3754
|
+
const pendingManualInputSyncRef = useRef8(null);
|
|
3716
3755
|
const llmRespondingRef = useRef8(false);
|
|
3717
3756
|
const interruptedForQuestionRef = useRef8(false);
|
|
3718
3757
|
const pendingInputBufRef = useRef8(null);
|
|
@@ -3812,6 +3851,7 @@ function useTourPlayback({
|
|
|
3812
3851
|
}, { devMode: devModeRef.current });
|
|
3813
3852
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
3814
3853
|
pendingManualWaitCleanupRef.current = null;
|
|
3854
|
+
clearPendingManualInputSync();
|
|
3815
3855
|
if (voiceInputResolveRef.current) {
|
|
3816
3856
|
const resolvePendingVoiceInput = voiceInputResolveRef.current;
|
|
3817
3857
|
voiceInputResolveRef.current = null;
|
|
@@ -3897,7 +3937,7 @@ function useTourPlayback({
|
|
|
3897
3937
|
resolve: async () => {
|
|
3898
3938
|
let targetEl = null;
|
|
3899
3939
|
if (params.uid) {
|
|
3900
|
-
const { getElementByUid } = await import("./aom-
|
|
3940
|
+
const { getElementByUid } = await import("./aom-LJNCLNXL.mjs");
|
|
3901
3941
|
targetEl = getElementByUid(params.uid);
|
|
3902
3942
|
}
|
|
3903
3943
|
if (!targetEl) {
|
|
@@ -4198,6 +4238,7 @@ function useTourPlayback({
|
|
|
4198
4238
|
const preferredWaitTarget = inputLikeWait ? batchPreferredWaitTarget ?? highlightedWaitTarget : highlightedWaitTarget;
|
|
4199
4239
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4200
4240
|
pendingManualWaitCleanupRef.current = null;
|
|
4241
|
+
clearPendingManualInputSync();
|
|
4201
4242
|
if (waitTargetHints) {
|
|
4202
4243
|
let manualWaitTarget = await resolveTargetElement2(waitTargetHints, currentStep);
|
|
4203
4244
|
if (inputLikeWait && preferredWaitTarget && manualWaitTarget && manualWaitTarget !== preferredWaitTarget && !isEditableWaitTarget(manualWaitTarget) && isEditableWaitTarget(preferredWaitTarget)) {
|
|
@@ -4208,14 +4249,18 @@ function useTourPlayback({
|
|
|
4208
4249
|
}, { devMode: devModeRef.current });
|
|
4209
4250
|
}
|
|
4210
4251
|
if (manualWaitTarget) {
|
|
4211
|
-
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep
|
|
4252
|
+
const manualWait = createManualWaitForTarget(manualWaitTarget, waitEvent, currentStep, {
|
|
4253
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4254
|
+
});
|
|
4212
4255
|
manualWaitPromise = manualWait.promise;
|
|
4213
4256
|
manualWaitKind = manualWait.kind;
|
|
4214
4257
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
4215
4258
|
}
|
|
4216
4259
|
}
|
|
4217
4260
|
if (!manualWaitPromise && preferredWaitTarget) {
|
|
4218
|
-
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep
|
|
4261
|
+
const manualWait = createManualWaitForTarget(preferredWaitTarget, waitEvent, currentStep, {
|
|
4262
|
+
onObservedInput: inputLikeWait ? scheduleManualInputSync : null
|
|
4263
|
+
});
|
|
4219
4264
|
manualWaitPromise = manualWait.promise;
|
|
4220
4265
|
manualWaitKind = manualWait.kind;
|
|
4221
4266
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4229,7 +4274,9 @@ function useTourPlayback({
|
|
|
4229
4274
|
'input:not([type="hidden"]):not([disabled]), textarea:not([disabled]), [contenteditable="true"], [role="textbox"]'
|
|
4230
4275
|
);
|
|
4231
4276
|
if (firstInput) {
|
|
4232
|
-
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep
|
|
4277
|
+
const manualWait = createManualWaitForTarget(firstInput, waitEvent, currentStep, {
|
|
4278
|
+
onObservedInput: scheduleManualInputSync
|
|
4279
|
+
});
|
|
4233
4280
|
manualWaitPromise = manualWait.promise;
|
|
4234
4281
|
manualWaitKind = manualWait.kind;
|
|
4235
4282
|
pendingManualWaitCleanupRef.current = manualWait.cleanup;
|
|
@@ -4275,6 +4322,7 @@ function useTourPlayback({
|
|
|
4275
4322
|
Promise.race([voiceOrTextWaitPromise, manualWaitPromise].filter(Boolean)).then(async (userText) => {
|
|
4276
4323
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4277
4324
|
pendingManualWaitCleanupRef.current = null;
|
|
4325
|
+
clearPendingManualInputSync();
|
|
4278
4326
|
voiceInputResolveRef.current = null;
|
|
4279
4327
|
setPlaybackState("executing");
|
|
4280
4328
|
const transcript = userText.trim();
|
|
@@ -4357,7 +4405,7 @@ function useTourPlayback({
|
|
|
4357
4405
|
void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
|
|
4358
4406
|
}
|
|
4359
4407
|
try {
|
|
4360
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4408
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-LJNCLNXL.mjs");
|
|
4361
4409
|
const aom = generateMinifiedAOM2();
|
|
4362
4410
|
if (socketRef.current === socket) {
|
|
4363
4411
|
emitSocketEvent(socket, "tour:sync_dom", {
|
|
@@ -4471,7 +4519,7 @@ function useTourPlayback({
|
|
|
4471
4519
|
}, [isActive, playbackState, voice.isListening, voice.isSpeaking]);
|
|
4472
4520
|
const syncAOM = useCallback7(async () => {
|
|
4473
4521
|
if (!isActiveRef.current) return;
|
|
4474
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4522
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-LJNCLNXL.mjs");
|
|
4475
4523
|
const aom = generateMinifiedAOM2();
|
|
4476
4524
|
emitSocketEvent(socketRef.current, "tour:sync_dom", {
|
|
4477
4525
|
url: window.location.pathname + window.location.search + window.location.hash,
|
|
@@ -4479,6 +4527,24 @@ function useTourPlayback({
|
|
|
4479
4527
|
domSummary: captureDomSummary()
|
|
4480
4528
|
});
|
|
4481
4529
|
}, []);
|
|
4530
|
+
const scheduleManualInputSync = useCallback7(() => {
|
|
4531
|
+
if (pendingManualInputSyncRef.current) {
|
|
4532
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4533
|
+
}
|
|
4534
|
+
pendingManualInputSyncRef.current = setTimeout(async () => {
|
|
4535
|
+
pendingManualInputSyncRef.current = null;
|
|
4536
|
+
if (!isActiveRef.current) return;
|
|
4537
|
+
const { waitForDomSettle } = await import("./dom-sync-GABDEODR.mjs");
|
|
4538
|
+
await waitForDomSettle({ timeoutMs: 600, debounceMs: 100 });
|
|
4539
|
+
await syncAOM();
|
|
4540
|
+
}, 150);
|
|
4541
|
+
}, [syncAOM]);
|
|
4542
|
+
const clearPendingManualInputSync = useCallback7(() => {
|
|
4543
|
+
if (pendingManualInputSyncRef.current) {
|
|
4544
|
+
clearTimeout(pendingManualInputSyncRef.current);
|
|
4545
|
+
pendingManualInputSyncRef.current = null;
|
|
4546
|
+
}
|
|
4547
|
+
}, []);
|
|
4482
4548
|
const interruptExecution = useCallback7((transcript) => {
|
|
4483
4549
|
if (!isSocketWritable(socketRef.current) || !isActiveRef.current) return false;
|
|
4484
4550
|
if (!commandInFlightRef.current && !voice.isSpeaking) return false;
|
|
@@ -4517,6 +4583,7 @@ function useTourPlayback({
|
|
|
4517
4583
|
activeCommandBatchIdRef.current = null;
|
|
4518
4584
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4519
4585
|
pendingManualWaitCleanupRef.current = null;
|
|
4586
|
+
clearPendingManualInputSync();
|
|
4520
4587
|
removeHighlight();
|
|
4521
4588
|
removeCaption();
|
|
4522
4589
|
voice.stopSpeaking();
|
|
@@ -4568,6 +4635,7 @@ function useTourPlayback({
|
|
|
4568
4635
|
removeCaption();
|
|
4569
4636
|
runCleanup(pendingManualWaitCleanupRef.current);
|
|
4570
4637
|
pendingManualWaitCleanupRef.current = null;
|
|
4638
|
+
clearPendingManualInputSync();
|
|
4571
4639
|
if (!skipRequestedRef.current && userProfile?.userId && tourRef.current) {
|
|
4572
4640
|
markTourComplete(serverUrl, toursApiBaseRef.current, tourRef.current.id, userProfile.userId, experienceType, websiteId);
|
|
4573
4641
|
}
|
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
|
-
};
|