@modelnex/sdk 0.5.53 → 0.5.54
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 +10 -0
- package/dist/index.mjs +10 -0
- 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
|
|
@@ -5373,6 +5380,9 @@ function useTourPlayback({
|
|
|
5373
5380
|
socket.on("tour:end", handleTourEndEvent);
|
|
5374
5381
|
socket.on("tour:debug_log", handleDebugLog);
|
|
5375
5382
|
emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
|
|
5383
|
+
if (socket.connected) {
|
|
5384
|
+
handleConnect();
|
|
5385
|
+
}
|
|
5376
5386
|
return () => {
|
|
5377
5387
|
socket.off("connect", handleConnect);
|
|
5378
5388
|
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
|
|
@@ -4544,6 +4551,9 @@ function useTourPlayback({
|
|
|
4544
4551
|
socket.on("tour:end", handleTourEndEvent);
|
|
4545
4552
|
socket.on("tour:debug_log", handleDebugLog);
|
|
4546
4553
|
emitSdkDebugLog("[ModelNex SDK] Tour playback initialized", void 0, { devMode: devModeRef.current });
|
|
4554
|
+
if (socket.connected) {
|
|
4555
|
+
handleConnect();
|
|
4556
|
+
}
|
|
4547
4557
|
return () => {
|
|
4548
4558
|
socket.off("connect", handleConnect);
|
|
4549
4559
|
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.54",
|
|
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
|
-
};
|