@modelnex/sdk 0.5.53 → 0.5.55
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/index.js +16 -3
- package/dist/index.mjs +16 -3
- package/package.json +11 -12
- package/dist/aom-HDYNCIOY.mjs +0 -10
- package/dist/aom-LJNCLNXL.mjs +0 -10
- package/dist/chunk-H4LUY7LI.mjs +0 -243
- package/dist/chunk-N65UEB6X.mjs +0 -70
- package/dist/dom-sync-L5KIP45X.mjs +0 -55
package/dist/index.js
CHANGED
|
@@ -4643,6 +4643,13 @@ function useTourPlayback({
|
|
|
4643
4643
|
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
4644
4644
|
if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
4645
4645
|
const activeType = experienceTypeRef.current;
|
|
4646
|
+
console.warn("[TourClient] Dropping command batch: playback not active or ownership not held", {
|
|
4647
|
+
isActive: isActiveRef.current,
|
|
4648
|
+
hasOwnership: hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current),
|
|
4649
|
+
experienceType: activeType,
|
|
4650
|
+
stepIndex: payload.stepIndex,
|
|
4651
|
+
commandBatchId: payload.commandBatchId ?? null
|
|
4652
|
+
});
|
|
4646
4653
|
emitSdkDebugLog("[TourClient] Ignoring command batch for inactive playback hook", {
|
|
4647
4654
|
experienceType: activeType,
|
|
4648
4655
|
stepIndex: payload.stepIndex
|
|
@@ -5258,10 +5265,13 @@ function useTourPlayback({
|
|
|
5258
5265
|
}
|
|
5259
5266
|
const tour = tourData.tourContext ?? tourRef.current;
|
|
5260
5267
|
const expType = experienceTypeRef.current;
|
|
5261
|
-
|
|
5268
|
+
const matchesType = !tour?.type || tour.type === expType;
|
|
5269
|
+
const isCompatibleResumption = !!tourData.resumed && (expType === "onboarding" && tour?.type === "tour" || expType === "tour" && tour?.type === "onboarding");
|
|
5270
|
+
if (!matchesType && !isCompatibleResumption) {
|
|
5262
5271
|
emitSdkDebugLog("[TourClient] Ignoring tour start for mismatched experience type", {
|
|
5263
|
-
incomingType: tour
|
|
5264
|
-
hookType: expType
|
|
5272
|
+
incomingType: tour?.type,
|
|
5273
|
+
hookType: expType,
|
|
5274
|
+
resumed: tourData.resumed
|
|
5265
5275
|
}, { devMode: devModeRef.current });
|
|
5266
5276
|
return;
|
|
5267
5277
|
}
|
|
@@ -5373,6 +5383,9 @@ function useTourPlayback({
|
|
|
5373
5383
|
socket.on("tour:end", handleTourEndEvent);
|
|
5374
5384
|
socket.on("tour:debug_log", handleDebugLog);
|
|
5375
5385
|
emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
|
|
5386
|
+
if (socket.connected) {
|
|
5387
|
+
handleConnect();
|
|
5388
|
+
}
|
|
5376
5389
|
return () => {
|
|
5377
5390
|
socket.off("connect", handleConnect);
|
|
5378
5391
|
socket.off("tour:server_state", handleServerState);
|
package/dist/index.mjs
CHANGED
|
@@ -3814,6 +3814,13 @@ function useTourPlayback({
|
|
|
3814
3814
|
const ownerKey = createTourPlaybackOwnerKey(serverUrl, websiteIdRef.current, experienceTypeRef.current);
|
|
3815
3815
|
if (!shouldExecuteTourCommandBatch(isActiveRef.current) || !hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current)) {
|
|
3816
3816
|
const activeType = experienceTypeRef.current;
|
|
3817
|
+
console.warn("[TourClient] Dropping command batch: playback not active or ownership not held", {
|
|
3818
|
+
isActive: isActiveRef.current,
|
|
3819
|
+
hasOwnership: hasTourPlaybackOwnership(ownerKey, playbackOwnerIdRef.current),
|
|
3820
|
+
experienceType: activeType,
|
|
3821
|
+
stepIndex: payload.stepIndex,
|
|
3822
|
+
commandBatchId: payload.commandBatchId ?? null
|
|
3823
|
+
});
|
|
3817
3824
|
emitSdkDebugLog("[TourClient] Ignoring command batch for inactive playback hook", {
|
|
3818
3825
|
experienceType: activeType,
|
|
3819
3826
|
stepIndex: payload.stepIndex
|
|
@@ -4429,10 +4436,13 @@ function useTourPlayback({
|
|
|
4429
4436
|
}
|
|
4430
4437
|
const tour = tourData.tourContext ?? tourRef.current;
|
|
4431
4438
|
const expType = experienceTypeRef.current;
|
|
4432
|
-
|
|
4439
|
+
const matchesType = !tour?.type || tour.type === expType;
|
|
4440
|
+
const isCompatibleResumption = !!tourData.resumed && (expType === "onboarding" && tour?.type === "tour" || expType === "tour" && tour?.type === "onboarding");
|
|
4441
|
+
if (!matchesType && !isCompatibleResumption) {
|
|
4433
4442
|
emitSdkDebugLog("[TourClient] Ignoring tour start for mismatched experience type", {
|
|
4434
|
-
incomingType: tour
|
|
4435
|
-
hookType: expType
|
|
4443
|
+
incomingType: tour?.type,
|
|
4444
|
+
hookType: expType,
|
|
4445
|
+
resumed: tourData.resumed
|
|
4436
4446
|
}, { devMode: devModeRef.current });
|
|
4437
4447
|
return;
|
|
4438
4448
|
}
|
|
@@ -4544,6 +4554,9 @@ function useTourPlayback({
|
|
|
4544
4554
|
socket.on("tour:end", handleTourEndEvent);
|
|
4545
4555
|
socket.on("tour:debug_log", handleDebugLog);
|
|
4546
4556
|
emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
|
|
4557
|
+
if (socket.connected) {
|
|
4558
|
+
handleConnect();
|
|
4559
|
+
}
|
|
4547
4560
|
return () => {
|
|
4548
4561
|
socket.off("connect", handleConnect);
|
|
4549
4562
|
socket.off("tour:server_state", handleServerState);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelnex/sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.55",
|
|
4
4
|
"description": "React SDK for natural language control of web apps via AI agents",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -20,15 +20,6 @@
|
|
|
20
20
|
"dist",
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
|
-
"scripts": {
|
|
24
|
-
"prepublishOnly": "npm run build",
|
|
25
|
-
"build": "npm exec -- tsup src/index.ts --format cjs,esm --dts",
|
|
26
|
-
"dev": "npm exec -- tsup src/index.ts --format cjs,esm --watch --dts",
|
|
27
|
-
"lint": "eslint src/",
|
|
28
|
-
"test": "npm run build && node --test tests/*.test.js",
|
|
29
|
-
"test:ci": "npm run test && npm run test:unit",
|
|
30
|
-
"test:unit": "node --import tsx --test tests/*.test.ts"
|
|
31
|
-
},
|
|
32
23
|
"peerDependencies": {
|
|
33
24
|
"react": ">=17.0.0",
|
|
34
25
|
"react-dom": ">=17.0.0",
|
|
@@ -67,5 +58,13 @@
|
|
|
67
58
|
"bugs": {
|
|
68
59
|
"url": "https://github.com/sharunaraksha/modelnex-sdk/issues"
|
|
69
60
|
},
|
|
70
|
-
"homepage": "https://github.com/sharunaraksha/modelnex-sdk#readme"
|
|
71
|
-
|
|
61
|
+
"homepage": "https://github.com/sharunaraksha/modelnex-sdk#readme",
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "npm exec -- tsup src/index.ts --format cjs,esm --dts",
|
|
64
|
+
"dev": "npm exec -- tsup src/index.ts --format cjs,esm --watch --dts",
|
|
65
|
+
"lint": "eslint src/",
|
|
66
|
+
"test": "npm run build && node --test tests/*.test.js",
|
|
67
|
+
"test:ci": "npm run test && npm run test:unit",
|
|
68
|
+
"test:unit": "node --import tsx --test tests/*.test.ts"
|
|
69
|
+
}
|
|
70
|
+
}
|
package/dist/aom-HDYNCIOY.mjs
DELETED
package/dist/aom-LJNCLNXL.mjs
DELETED
package/dist/chunk-H4LUY7LI.mjs
DELETED
|
@@ -1,243 +0,0 @@
|
|
|
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/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
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// src/utils/dom-sync.ts
|
|
2
|
-
function waitForDomSettle(options = {}) {
|
|
3
|
-
const { timeoutMs = 5e3, debounceMs = 400, minWaitMs = 100 } = options;
|
|
4
|
-
return new Promise((resolve) => {
|
|
5
|
-
let debounceTimer = null;
|
|
6
|
-
let resolved = false;
|
|
7
|
-
const maxTimer = setTimeout(() => {
|
|
8
|
-
if (!resolved) {
|
|
9
|
-
resolved = true;
|
|
10
|
-
cleanup();
|
|
11
|
-
console.log("[DOM Sync] Forced resolution by max timeout");
|
|
12
|
-
resolve();
|
|
13
|
-
}
|
|
14
|
-
}, Math.max(timeoutMs, minWaitMs));
|
|
15
|
-
const finish = () => {
|
|
16
|
-
if (!resolved) {
|
|
17
|
-
resolved = true;
|
|
18
|
-
cleanup();
|
|
19
|
-
resolve();
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
const observer = new MutationObserver((mutations) => {
|
|
23
|
-
const hasSignificantMutations = mutations.some((m) => {
|
|
24
|
-
if (m.target instanceof HTMLElement) {
|
|
25
|
-
if (m.target.hasAttribute("data-modelnex-tour-highlight")) return false;
|
|
26
|
-
if (m.target.hasAttribute("data-modelnex-caption")) return false;
|
|
27
|
-
if (m.target.closest("#modelnex-studio-root")) return false;
|
|
28
|
-
if (m.target.closest("#modelnex-active-agent-root")) return false;
|
|
29
|
-
}
|
|
30
|
-
return true;
|
|
31
|
-
});
|
|
32
|
-
if (!hasSignificantMutations) return;
|
|
33
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
34
|
-
debounceTimer = setTimeout(finish, debounceMs);
|
|
35
|
-
});
|
|
36
|
-
const cleanup = () => {
|
|
37
|
-
observer.disconnect();
|
|
38
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
39
|
-
clearTimeout(maxTimer);
|
|
40
|
-
};
|
|
41
|
-
setTimeout(() => {
|
|
42
|
-
if (resolved) return;
|
|
43
|
-
observer.observe(document.body, {
|
|
44
|
-
childList: true,
|
|
45
|
-
subtree: true,
|
|
46
|
-
attributes: true,
|
|
47
|
-
characterData: true
|
|
48
|
-
});
|
|
49
|
-
debounceTimer = setTimeout(finish, debounceMs);
|
|
50
|
-
}, minWaitMs);
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
export {
|
|
54
|
-
waitForDomSettle
|
|
55
|
-
};
|