@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 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.53",
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
+ }
@@ -1,10 +0,0 @@
1
- import {
2
- clearAOMMap,
3
- generateMinifiedAOM,
4
- getElementByUid
5
- } from "./chunk-N65UEB6X.mjs";
6
- export {
7
- clearAOMMap,
8
- generateMinifiedAOM,
9
- getElementByUid
10
- };
@@ -1,10 +0,0 @@
1
- import {
2
- clearAOMMap,
3
- generateMinifiedAOM,
4
- getElementByUid
5
- } from "./chunk-H4LUY7LI.mjs";
6
- export {
7
- clearAOMMap,
8
- generateMinifiedAOM,
9
- getElementByUid
10
- };
@@ -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
- };
@@ -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
- };