@modelnex/sdk 0.5.44 → 0.5.46
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-LJNCLNXL.mjs → aom-SP2LMWQI.mjs} +1 -1
- package/dist/chunk-SXGINP3O.mjs +683 -0
- package/dist/index.js +1023 -680
- package/dist/index.mjs +663 -776
- package/package.json +1 -1
- package/dist/chunk-H4LUY7LI.mjs +0 -243
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectEditableControls,
|
|
3
|
+
emitSdkDebugLog,
|
|
4
|
+
extractInteractiveElements,
|
|
3
5
|
findEditableControlInShadowRoot,
|
|
6
|
+
generateFingerprint,
|
|
4
7
|
generateMinifiedAOM,
|
|
5
8
|
isEditableControlDisabled,
|
|
9
|
+
isSdkDebugEnabled,
|
|
6
10
|
isValueBearingElement,
|
|
7
11
|
readEditableControlName,
|
|
8
12
|
readEditableControlPlaceholder,
|
|
9
13
|
readEditableControlType,
|
|
10
14
|
readEditableControlValue,
|
|
11
|
-
resolveEditableControlElement
|
|
12
|
-
|
|
15
|
+
resolveEditableControlElement,
|
|
16
|
+
sanitizeAgentDebug,
|
|
17
|
+
sanitizeChatMessages,
|
|
18
|
+
useAutoExtract
|
|
19
|
+
} from "./chunk-SXGINP3O.mjs";
|
|
13
20
|
|
|
14
21
|
// src/index.ts
|
|
15
|
-
import React8, { useState as
|
|
22
|
+
import React8, { useState as useState14, useCallback as useCallback13, useEffect as useEffect18, useMemo as useMemo5 } from "react";
|
|
16
23
|
|
|
17
24
|
// src/context.ts
|
|
18
25
|
import { createContext } from "react";
|
|
19
26
|
var ModelNexContext = createContext(null);
|
|
20
27
|
|
|
21
28
|
// src/hooks/useModelNexSocket.ts
|
|
22
|
-
import { useEffect as
|
|
29
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
23
30
|
import { io } from "socket.io-client";
|
|
24
31
|
|
|
25
32
|
// src/utils/socket-io-transports.ts
|
|
@@ -34,390 +41,11 @@ function resolveSocketIoTransports(serverUrl, order) {
|
|
|
34
41
|
return ["websocket", "polling"];
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
// src/auto-extract.ts
|
|
38
|
-
import { useState, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2 } from "react";
|
|
39
|
-
|
|
40
|
-
// src/utils/dev-logging.ts
|
|
41
|
-
function isSdkDebugEnabled(devMode) {
|
|
42
|
-
if (devMode) return true;
|
|
43
|
-
if (typeof window !== "undefined" && Boolean(window.MODELNEX_DEBUG)) {
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
return process.env.NODE_ENV === "development";
|
|
47
|
-
}
|
|
48
|
-
function emitSdkDebugLog(message, payload, options) {
|
|
49
|
-
if (!isSdkDebugEnabled(options?.devMode)) return;
|
|
50
|
-
if (payload && Object.keys(payload).length > 0) {
|
|
51
|
-
console.log(message, payload);
|
|
52
|
-
} else {
|
|
53
|
-
console.log(message);
|
|
54
|
-
}
|
|
55
|
-
if (options?.dispatchEvent && typeof window !== "undefined") {
|
|
56
|
-
window.dispatchEvent(new CustomEvent("modelnex-debug", { detail: { msg: message, data: payload } }));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function sanitizeActionList(actions) {
|
|
60
|
-
if (!Array.isArray(actions) || actions.length === 0) return void 0;
|
|
61
|
-
return actions.map(({ actionId }) => ({ actionId }));
|
|
62
|
-
}
|
|
63
|
-
function sanitizeAgentDebug(debug) {
|
|
64
|
-
if (!debug) return void 0;
|
|
65
|
-
const actions = sanitizeActionList(debug.actions);
|
|
66
|
-
const traces = Array.isArray(debug.traces) && debug.traces.length > 0 ? debug.traces.map((trace) => ({
|
|
67
|
-
step: trace.step,
|
|
68
|
-
actions: sanitizeActionList(trace.actions) ?? [],
|
|
69
|
-
results: Array.isArray(trace.results) && trace.results.length > 0 ? trace.results.map(({ actionId, success }) => ({ actionId, success })) : void 0
|
|
70
|
-
})) : void 0;
|
|
71
|
-
if ((!actions || actions.length === 0) && (!traces || traces.length === 0)) {
|
|
72
|
-
return void 0;
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
...actions ? { actions } : {},
|
|
76
|
-
...traces ? { traces } : {}
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
function sanitizeChatMessages(messages, devMode) {
|
|
80
|
-
const includeDebug = isSdkDebugEnabled(devMode);
|
|
81
|
-
return messages.map((message) => {
|
|
82
|
-
if (message.role !== "assistant") {
|
|
83
|
-
return message;
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
...message,
|
|
87
|
-
debug: includeDebug ? sanitizeAgentDebug(message.debug) : void 0
|
|
88
|
-
};
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// src/auto-extract.ts
|
|
93
|
-
function simpleHash(str) {
|
|
94
|
-
let hash = 0;
|
|
95
|
-
for (let i = 0; i < str.length; i++) {
|
|
96
|
-
const char = str.charCodeAt(i);
|
|
97
|
-
hash = (hash << 5) - hash + char;
|
|
98
|
-
hash |= 0;
|
|
99
|
-
}
|
|
100
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
101
|
-
}
|
|
102
|
-
function isStableDomId(id) {
|
|
103
|
-
if (!id) return false;
|
|
104
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
105
|
-
const REACT_USE_ID_RE = /^(?:radix-)?:[rR][^\s]*$/;
|
|
106
|
-
if (REACT_USE_ID_RE.test(id)) return false;
|
|
107
|
-
if (/^\d+$/.test(id)) return false;
|
|
108
|
-
if (UUID_RE.test(id)) return false;
|
|
109
|
-
return true;
|
|
110
|
-
}
|
|
111
|
-
function getNearestHeading(el) {
|
|
112
|
-
const shellContainer = el.closest('nav, aside, header, [role="navigation"], [role="complementary"], [role="banner"]');
|
|
113
|
-
const boundary = shellContainer || document.body;
|
|
114
|
-
let current = el;
|
|
115
|
-
while (current && current !== boundary) {
|
|
116
|
-
let sibling = current.previousElementSibling;
|
|
117
|
-
while (sibling) {
|
|
118
|
-
if (/^H[1-6]$/.test(sibling.tagName)) {
|
|
119
|
-
return sibling.textContent?.trim().slice(0, 100) || null;
|
|
120
|
-
}
|
|
121
|
-
const innerHeading = sibling.querySelector("h1, h2, h3, h4, h5, h6");
|
|
122
|
-
if (innerHeading) {
|
|
123
|
-
return innerHeading.textContent?.trim().slice(0, 100) || null;
|
|
124
|
-
}
|
|
125
|
-
sibling = sibling.previousElementSibling;
|
|
126
|
-
}
|
|
127
|
-
const parentHeading = current.parentElement?.querySelector("h1, h2, h3, h4, h5, h6");
|
|
128
|
-
if (parentHeading && !current.contains(parentHeading)) {
|
|
129
|
-
return parentHeading.textContent?.trim().slice(0, 100) || null;
|
|
130
|
-
}
|
|
131
|
-
current = current.parentElement;
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
function getParentContainer(el) {
|
|
136
|
-
let current = el.parentElement;
|
|
137
|
-
while (current && current !== document.body) {
|
|
138
|
-
const role = current.getAttribute("role");
|
|
139
|
-
const tag = current.tagName.toLowerCase();
|
|
140
|
-
const state = current.getAttribute("data-state");
|
|
141
|
-
if (role === "dialog" || role === "alertdialog" || tag === "dialog") {
|
|
142
|
-
const title = current.querySelector('h2, h3, [class*="title"]');
|
|
143
|
-
const titleText = title?.textContent?.trim().slice(0, 60) || "";
|
|
144
|
-
return titleText ? `dialog:${titleText}` : "dialog";
|
|
145
|
-
}
|
|
146
|
-
if (role === "menu" || role === "listbox") {
|
|
147
|
-
return role;
|
|
148
|
-
}
|
|
149
|
-
if (current.hasAttribute("popover") || state === "open" && current.getAttribute("data-radix-popper-content-wrapper") !== null) {
|
|
150
|
-
return "popover";
|
|
151
|
-
}
|
|
152
|
-
if (current.getAttribute("data-radix-menu-content") !== null || role === "menubar") {
|
|
153
|
-
return "dropdown-menu";
|
|
154
|
-
}
|
|
155
|
-
if (role === "tabpanel") {
|
|
156
|
-
const label = current.getAttribute("aria-label") || current.getAttribute("aria-labelledby");
|
|
157
|
-
return label ? `tabpanel:${label.slice(0, 40)}` : "tabpanel";
|
|
158
|
-
}
|
|
159
|
-
if (tag === "nav" || role === "navigation") {
|
|
160
|
-
const label = current.getAttribute("aria-label");
|
|
161
|
-
return label ? `navigation:${label.slice(0, 40)}` : "navigation";
|
|
162
|
-
}
|
|
163
|
-
if (tag === "aside") {
|
|
164
|
-
return "sidebar";
|
|
165
|
-
}
|
|
166
|
-
if (tag === "header") {
|
|
167
|
-
return "header";
|
|
168
|
-
}
|
|
169
|
-
current = current.parentElement;
|
|
170
|
-
}
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
function getRowContext(el) {
|
|
174
|
-
let current = el.parentElement;
|
|
175
|
-
while (current && current !== document.body) {
|
|
176
|
-
const tag = current.tagName.toLowerCase();
|
|
177
|
-
if (tag === "tr" || tag === "li" || current.getAttribute("role") === "row" || current.getAttribute("role") === "listitem") {
|
|
178
|
-
const rowText = current.textContent?.trim().replace(/\s+/g, " ").slice(0, 120) || "";
|
|
179
|
-
return rowText;
|
|
180
|
-
}
|
|
181
|
-
current = current.parentElement;
|
|
182
|
-
}
|
|
183
|
-
return "";
|
|
184
|
-
}
|
|
185
|
-
function getNearestAncestorId(el) {
|
|
186
|
-
let current = el.parentElement;
|
|
187
|
-
while (current && current !== document.body) {
|
|
188
|
-
const aid = current.id;
|
|
189
|
-
if (isStableDomId(aid)) {
|
|
190
|
-
return aid;
|
|
191
|
-
}
|
|
192
|
-
const ancestorTestId = current.getAttribute("data-testid");
|
|
193
|
-
if (ancestorTestId) return `[data-testid="${ancestorTestId}"]`;
|
|
194
|
-
current = current.parentElement;
|
|
195
|
-
}
|
|
196
|
-
return null;
|
|
197
|
-
}
|
|
198
|
-
function generateFingerprint(el) {
|
|
199
|
-
const tag = el.tagName.toLowerCase();
|
|
200
|
-
const testId = el.getAttribute("data-testid");
|
|
201
|
-
const id = el.id;
|
|
202
|
-
const name = el.getAttribute("name");
|
|
203
|
-
const ariaLabel = el.getAttribute("aria-label");
|
|
204
|
-
const type = el.type || "";
|
|
205
|
-
const text = el.textContent?.trim().slice(0, 80) || "";
|
|
206
|
-
if (testId) return `tid:${testId}`;
|
|
207
|
-
if (isStableDomId(id)) return `id:${id}`;
|
|
208
|
-
if (name) return `name:${tag}:${name}`;
|
|
209
|
-
if (ariaLabel) {
|
|
210
|
-
const rowCtx2 = getRowContext(el);
|
|
211
|
-
if (rowCtx2) {
|
|
212
|
-
return `aria:${simpleHash(ariaLabel + ":" + rowCtx2)}:${ariaLabel.slice(0, 40)}`;
|
|
213
|
-
}
|
|
214
|
-
return `aria:${simpleHash(ariaLabel)}:${ariaLabel.slice(0, 40)}`;
|
|
215
|
-
}
|
|
216
|
-
if (tag === "a") {
|
|
217
|
-
const href = el.getAttribute("href");
|
|
218
|
-
if (href && href !== "#" && !href.startsWith("javascript:")) {
|
|
219
|
-
return `href:${simpleHash(href + ":" + text)}:${text.slice(0, 30) || href.slice(0, 30)}`;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
const ancestorId = getNearestAncestorId(el);
|
|
223
|
-
if (ancestorId) {
|
|
224
|
-
return `anc:${simpleHash(ancestorId + ":" + tag + ":" + text)}:${text.slice(0, 30) || tag}`;
|
|
225
|
-
}
|
|
226
|
-
const heading = getNearestHeading(el) || "";
|
|
227
|
-
const rowCtx = getRowContext(el);
|
|
228
|
-
const raw = `${tag}:${type}:${text}:${heading}:${rowCtx}`;
|
|
229
|
-
return `hash:${simpleHash(raw)}:${text.slice(0, 30) || tag}`;
|
|
230
|
-
}
|
|
231
|
-
function classifyRole(el) {
|
|
232
|
-
const tag = el.tagName.toLowerCase();
|
|
233
|
-
const role = el.getAttribute("role");
|
|
234
|
-
if (tag === "button" || role === "button") return "button";
|
|
235
|
-
if (tag === "a") return "link";
|
|
236
|
-
if (tag === "input") return "input";
|
|
237
|
-
if (tag === "textarea") return "textarea";
|
|
238
|
-
if (tag === "select") return "select";
|
|
239
|
-
if (tag === "form") return "form";
|
|
240
|
-
if (tag === "label") return "label";
|
|
241
|
-
if (role === "link") return "link";
|
|
242
|
-
if (role === "menuitem") return "menuitem";
|
|
243
|
-
if (role === "menuitemcheckbox") return "menuitem";
|
|
244
|
-
if (role === "menuitemradio") return "menuitem";
|
|
245
|
-
if (role === "tab") return "tab";
|
|
246
|
-
if (role === "checkbox") return "checkbox";
|
|
247
|
-
if (role === "radio") return "radio";
|
|
248
|
-
if (role === "switch") return "switch";
|
|
249
|
-
if (role === "slider") return "slider";
|
|
250
|
-
if (role === "combobox") return "combobox";
|
|
251
|
-
if (role === "option") return "option";
|
|
252
|
-
if (role === "treeitem") return "treeitem";
|
|
253
|
-
if (role === "gridcell") return "gridcell";
|
|
254
|
-
if (el.hasAttribute("tabindex") && el.getAttribute("tabindex") !== "-1") return "interactive";
|
|
255
|
-
if (el.hasAttribute("cmdk-item")) return "menuitem";
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
var INTERACTIVE_SELECTOR = [
|
|
259
|
-
"button",
|
|
260
|
-
"a",
|
|
261
|
-
// All anchor tags (SPA links may not have href)
|
|
262
|
-
'input:not([type="hidden"])',
|
|
263
|
-
"textarea",
|
|
264
|
-
"select",
|
|
265
|
-
"label[for]",
|
|
266
|
-
'[role="button"]',
|
|
267
|
-
'[role="link"]',
|
|
268
|
-
'[role="menuitem"]',
|
|
269
|
-
'[role="menuitemcheckbox"]',
|
|
270
|
-
'[role="menuitemradio"]',
|
|
271
|
-
'[role="tab"]',
|
|
272
|
-
'[role="checkbox"]',
|
|
273
|
-
'[role="radio"]',
|
|
274
|
-
'[role="switch"]',
|
|
275
|
-
'[role="slider"]',
|
|
276
|
-
'[role="combobox"]',
|
|
277
|
-
'[role="option"]',
|
|
278
|
-
'[role="treeitem"]',
|
|
279
|
-
'[role="gridcell"]',
|
|
280
|
-
'[tabindex]:not([tabindex="-1"])',
|
|
281
|
-
"[data-discover]",
|
|
282
|
-
// React Router links
|
|
283
|
-
"[cmdk-item]",
|
|
284
|
-
// cmdk menu items
|
|
285
|
-
"form"
|
|
286
|
-
].join(", ");
|
|
287
|
-
function isVisible(el) {
|
|
288
|
-
if (el.offsetParent === null) {
|
|
289
|
-
const computed = window.getComputedStyle(el);
|
|
290
|
-
if (computed.position !== "fixed" && computed.position !== "sticky") {
|
|
291
|
-
if (computed.display === "none" || computed.visibility === "hidden") {
|
|
292
|
-
return false;
|
|
293
|
-
}
|
|
294
|
-
const rect2 = el.getBoundingClientRect();
|
|
295
|
-
if (rect2.width === 0 && rect2.height === 0) return false;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
const style = window.getComputedStyle(el);
|
|
299
|
-
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
const rect = el.getBoundingClientRect();
|
|
303
|
-
return rect.width > 0 && rect.height > 0;
|
|
304
|
-
}
|
|
305
|
-
function extractInteractiveElements() {
|
|
306
|
-
const elements = [];
|
|
307
|
-
const seen = /* @__PURE__ */ new Set();
|
|
308
|
-
try {
|
|
309
|
-
const nodes = document.querySelectorAll(INTERACTIVE_SELECTOR);
|
|
310
|
-
nodes.forEach((node) => {
|
|
311
|
-
const el = node;
|
|
312
|
-
if (!isVisible(el)) return;
|
|
313
|
-
if (el.closest(".modelnex-chat-panel, .modelnex-studio-overlay, [data-modelnex-internal]")) return;
|
|
314
|
-
const role = classifyRole(el);
|
|
315
|
-
if (!role) return;
|
|
316
|
-
let fingerprint = generateFingerprint(el);
|
|
317
|
-
if (seen.has(fingerprint)) {
|
|
318
|
-
let idx = 2;
|
|
319
|
-
while (seen.has(`${fingerprint}#${idx}`)) idx++;
|
|
320
|
-
fingerprint = `${fingerprint}#${idx}`;
|
|
321
|
-
}
|
|
322
|
-
seen.add(fingerprint);
|
|
323
|
-
const rect = el.getBoundingClientRect();
|
|
324
|
-
const text = el.textContent?.trim().slice(0, 200) || "";
|
|
325
|
-
elements.push({
|
|
326
|
-
fingerprint,
|
|
327
|
-
tagName: el.tagName.toLowerCase(),
|
|
328
|
-
text,
|
|
329
|
-
role,
|
|
330
|
-
rect: {
|
|
331
|
-
top: rect.top,
|
|
332
|
-
left: rect.left,
|
|
333
|
-
width: rect.width,
|
|
334
|
-
height: rect.height
|
|
335
|
-
},
|
|
336
|
-
attributes: {
|
|
337
|
-
id: el.id || void 0,
|
|
338
|
-
name: el.getAttribute("name") || void 0,
|
|
339
|
-
type: el.type || void 0,
|
|
340
|
-
ariaLabel: el.getAttribute("aria-label") || void 0,
|
|
341
|
-
dataTestId: el.getAttribute("data-testid") || void 0,
|
|
342
|
-
href: el.href || void 0,
|
|
343
|
-
placeholder: el.placeholder || void 0
|
|
344
|
-
},
|
|
345
|
-
nearestHeading: getNearestHeading(el),
|
|
346
|
-
parentContainer: getParentContainer(el),
|
|
347
|
-
disabled: el.disabled || el.getAttribute("aria-disabled") === "true",
|
|
348
|
-
element: el
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
} catch (err) {
|
|
352
|
-
console.warn("[ModelNex] Auto-extraction error:", err);
|
|
353
|
-
}
|
|
354
|
-
return elements;
|
|
355
|
-
}
|
|
356
|
-
function useAutoExtract(devMode) {
|
|
357
|
-
const [elements, setElements] = useState([]);
|
|
358
|
-
const timerRef = useRef2(null);
|
|
359
|
-
const lastSnapshotRef = useRef2("");
|
|
360
|
-
const scan = useCallback2(() => {
|
|
361
|
-
if (typeof document === "undefined") return;
|
|
362
|
-
const extracted = extractInteractiveElements();
|
|
363
|
-
const snapshot = JSON.stringify(extracted.map((e) => ({
|
|
364
|
-
fingerprint: e.fingerprint,
|
|
365
|
-
role: e.role,
|
|
366
|
-
text: e.text,
|
|
367
|
-
rect: e.rect,
|
|
368
|
-
disabled: e.disabled,
|
|
369
|
-
nearestHeading: e.nearestHeading,
|
|
370
|
-
parentContainer: e.parentContainer
|
|
371
|
-
})));
|
|
372
|
-
if (snapshot === lastSnapshotRef.current) return;
|
|
373
|
-
lastSnapshotRef.current = snapshot;
|
|
374
|
-
emitSdkDebugLog("[ModelNex AutoExtract] Scan complete", {
|
|
375
|
-
elementCount: extracted.length
|
|
376
|
-
}, { devMode });
|
|
377
|
-
setElements(extracted);
|
|
378
|
-
}, [devMode]);
|
|
379
|
-
useEffect2(() => {
|
|
380
|
-
const initialTimer = setTimeout(scan, 300);
|
|
381
|
-
const observer = new MutationObserver((mutations) => {
|
|
382
|
-
const hasRelevantMutation = mutations.some((mutation) => {
|
|
383
|
-
const target = mutation.target;
|
|
384
|
-
if (target?.closest?.(".modelnex-chat-panel, .modelnex-studio-overlay, [data-modelnex-internal]")) {
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
return true;
|
|
388
|
-
});
|
|
389
|
-
if (!hasRelevantMutation) return;
|
|
390
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
391
|
-
timerRef.current = setTimeout(scan, 500);
|
|
392
|
-
});
|
|
393
|
-
observer.observe(document.body, {
|
|
394
|
-
childList: true,
|
|
395
|
-
subtree: true,
|
|
396
|
-
attributes: true,
|
|
397
|
-
attributeFilter: ["disabled", "aria-disabled", "hidden", "style", "class"]
|
|
398
|
-
});
|
|
399
|
-
const handlePositionChange = () => {
|
|
400
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
401
|
-
timerRef.current = setTimeout(scan, 200);
|
|
402
|
-
};
|
|
403
|
-
window.addEventListener("scroll", handlePositionChange, { passive: true });
|
|
404
|
-
window.addEventListener("resize", handlePositionChange, { passive: true });
|
|
405
|
-
return () => {
|
|
406
|
-
clearTimeout(initialTimer);
|
|
407
|
-
if (timerRef.current) clearTimeout(timerRef.current);
|
|
408
|
-
observer.disconnect();
|
|
409
|
-
window.removeEventListener("scroll", handlePositionChange);
|
|
410
|
-
window.removeEventListener("resize", handlePositionChange);
|
|
411
|
-
};
|
|
412
|
-
}, [scan]);
|
|
413
|
-
return elements;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
44
|
// src/utils/dom-summary.ts
|
|
417
45
|
function textOf(el) {
|
|
418
46
|
return el?.textContent?.trim().slice(0, 200) ?? "";
|
|
419
47
|
}
|
|
420
|
-
function
|
|
48
|
+
function isVisible(el) {
|
|
421
49
|
const htmlEl = el;
|
|
422
50
|
const style = window.getComputedStyle(htmlEl);
|
|
423
51
|
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
@@ -488,7 +116,7 @@ function captureDomSummary(tags) {
|
|
|
488
116
|
const alerts = safeQueryAll('[role="alert"], [role="status"]').map(textOf).filter(Boolean);
|
|
489
117
|
const allButtons = safeQueryAll('button, [role="button"], a[class*="btn"], a[class*="Button"], a');
|
|
490
118
|
const disabledButtons = allButtons.filter((b) => b.disabled || b.getAttribute("aria-disabled") === "true").map((b) => ({ text: textOf(b), fingerprint: generateFingerprint(b) })).filter((o) => !!o.text);
|
|
491
|
-
const enabledButtons = allButtons.filter((b) => !b.disabled && b.getAttribute("aria-disabled") !== "true").filter((b) =>
|
|
119
|
+
const enabledButtons = allButtons.filter((b) => !b.disabled && b.getAttribute("aria-disabled") !== "true").filter((b) => isVisible(b)).map((b) => ({ text: textOf(b), fingerprint: generateFingerprint(b) })).filter((o) => !!o.text).slice(0, 40);
|
|
492
120
|
const visibleDialogs = safeQueryAll('[role="dialog"], [data-state="open"], dialog[open]').map((d) => {
|
|
493
121
|
const title = d.querySelector('h2, h3, [class*="title"]');
|
|
494
122
|
return title ? textOf(title) : textOf(d).slice(0, 100);
|
|
@@ -677,6 +305,157 @@ function serializeContexts(contexts) {
|
|
|
677
305
|
return Array.from(contexts.values());
|
|
678
306
|
}
|
|
679
307
|
|
|
308
|
+
// src/utils/debug-tools.ts
|
|
309
|
+
var lastActionEffect = null;
|
|
310
|
+
function normalizeText(value) {
|
|
311
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
312
|
+
}
|
|
313
|
+
function isElementVisible(el) {
|
|
314
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
315
|
+
const style = window.getComputedStyle(el);
|
|
316
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
const rect = el.getBoundingClientRect();
|
|
320
|
+
return rect.width > 0 && rect.height > 0;
|
|
321
|
+
}
|
|
322
|
+
function safeQueryAll2(selector) {
|
|
323
|
+
try {
|
|
324
|
+
return Array.from(document.querySelectorAll(selector)).filter(
|
|
325
|
+
(el) => el instanceof HTMLElement
|
|
326
|
+
);
|
|
327
|
+
} catch {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
function classifyLevel(el, text) {
|
|
332
|
+
const levelHint = `${el.getAttribute("data-variant") || ""} ${el.className || ""} ${text}`.toLowerCase();
|
|
333
|
+
if (/\berror|danger|invalid|failed|required\b/.test(levelHint)) return "error";
|
|
334
|
+
if (/\bwarn|warning|caution\b/.test(levelHint)) return "warning";
|
|
335
|
+
return "info";
|
|
336
|
+
}
|
|
337
|
+
function classifySource(el) {
|
|
338
|
+
const role = (el.getAttribute("role") || "").toLowerCase();
|
|
339
|
+
if (role === "alert") return "alert";
|
|
340
|
+
if (role === "status") return "status";
|
|
341
|
+
if (el.hasAttribute("aria-live")) return "aria-live";
|
|
342
|
+
const classHint = String(el.className || "").toLowerCase();
|
|
343
|
+
if (/\bbanner\b/.test(classHint)) return "banner";
|
|
344
|
+
return "inline";
|
|
345
|
+
}
|
|
346
|
+
function selectorHintFor(el) {
|
|
347
|
+
if (el.id) return `#${el.id}`;
|
|
348
|
+
if (el.getAttribute("data-testid")) return `[data-testid="${el.getAttribute("data-testid")}"]`;
|
|
349
|
+
const role = el.getAttribute("role");
|
|
350
|
+
return role ? `${el.tagName.toLowerCase()}[role="${role}"]` : el.tagName.toLowerCase();
|
|
351
|
+
}
|
|
352
|
+
function collectUiMessages(options) {
|
|
353
|
+
const includeHidden = Boolean(options?.includeHidden);
|
|
354
|
+
const maxItems = Math.max(1, Math.min(Number(options?.maxItems || 10), 20));
|
|
355
|
+
const selectors = [
|
|
356
|
+
'[role="alert"]',
|
|
357
|
+
'[role="status"]',
|
|
358
|
+
"[aria-live]",
|
|
359
|
+
'[aria-invalid="true"]',
|
|
360
|
+
"[data-error]",
|
|
361
|
+
"[data-warning]",
|
|
362
|
+
".error",
|
|
363
|
+
".warning",
|
|
364
|
+
".alert",
|
|
365
|
+
".toast",
|
|
366
|
+
".banner"
|
|
367
|
+
];
|
|
368
|
+
const seen = /* @__PURE__ */ new Set();
|
|
369
|
+
const results = [];
|
|
370
|
+
for (const selector of selectors) {
|
|
371
|
+
for (const el of safeQueryAll2(selector)) {
|
|
372
|
+
const visible = isElementVisible(el);
|
|
373
|
+
if (!includeHidden && !visible) continue;
|
|
374
|
+
const text = normalizeText(el.textContent);
|
|
375
|
+
if (!text || text.length < 2) continue;
|
|
376
|
+
const key = `${selector}:${text.toLowerCase()}`;
|
|
377
|
+
if (seen.has(key)) continue;
|
|
378
|
+
seen.add(key);
|
|
379
|
+
results.push({
|
|
380
|
+
id: `ui_${results.length + 1}`,
|
|
381
|
+
level: classifyLevel(el, text),
|
|
382
|
+
text,
|
|
383
|
+
source: classifySource(el),
|
|
384
|
+
visible,
|
|
385
|
+
selectorHint: selectorHintFor(el)
|
|
386
|
+
});
|
|
387
|
+
if (results.length >= maxItems) return results;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return results;
|
|
391
|
+
}
|
|
392
|
+
function captureDebugSnapshot() {
|
|
393
|
+
return {
|
|
394
|
+
route: `${window.location.pathname}${window.location.search}${window.location.hash}`,
|
|
395
|
+
title: document.title || "",
|
|
396
|
+
uiMessages: collectUiMessages({ maxItems: 6 }),
|
|
397
|
+
timestamp: Date.now()
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
function diffUiMessages(before, after) {
|
|
401
|
+
const beforeKeys = new Set(before.map((entry) => `${entry.level}:${entry.text}`));
|
|
402
|
+
return after.filter((entry) => !beforeKeys.has(`${entry.level}:${entry.text}`));
|
|
403
|
+
}
|
|
404
|
+
function summarizeEffect(record) {
|
|
405
|
+
const routeChanged = record.before.route !== record.after.route;
|
|
406
|
+
const titleChanged = record.before.title !== record.after.title;
|
|
407
|
+
const newMessages = diffUiMessages(record.before.uiMessages, record.after.uiMessages);
|
|
408
|
+
const changed = routeChanged || titleChanged || newMessages.length > 0 || !record.success || record.before.timestamp !== record.after.timestamp;
|
|
409
|
+
const parts = [];
|
|
410
|
+
if (routeChanged) parts.push(`route changed from ${record.before.route} to ${record.after.route}`);
|
|
411
|
+
if (titleChanged) parts.push("page title changed");
|
|
412
|
+
if (newMessages.length > 0) parts.push(`new UI messages: ${newMessages.map((entry) => entry.text).join(" | ")}`);
|
|
413
|
+
if (!record.success && record.error) parts.push(`action failed: ${record.error}`);
|
|
414
|
+
if (parts.length === 0) parts.push("no obvious route/title/message change detected");
|
|
415
|
+
return {
|
|
416
|
+
summary: parts.join("; "),
|
|
417
|
+
changed
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function recordLastActionEffect(record) {
|
|
421
|
+
const summary = summarizeEffect(record);
|
|
422
|
+
lastActionEffect = {
|
|
423
|
+
...record,
|
|
424
|
+
...summary
|
|
425
|
+
};
|
|
426
|
+
return lastActionEffect;
|
|
427
|
+
}
|
|
428
|
+
function getLastActionEffectRecord() {
|
|
429
|
+
return lastActionEffect;
|
|
430
|
+
}
|
|
431
|
+
function inspectDomElementState(el) {
|
|
432
|
+
const rect = el.getBoundingClientRect();
|
|
433
|
+
const describedByIds = normalizeText(el.getAttribute("aria-describedby")).split(/\s+/).filter(Boolean);
|
|
434
|
+
const describedByText = describedByIds.map((id) => normalizeText(document.getElementById(id)?.textContent)).filter(Boolean).join(" | ");
|
|
435
|
+
const labelText = normalizeText(
|
|
436
|
+
el.labels?.[0]?.textContent || el.getAttribute("aria-label") || el.getAttribute("name") || el.getAttribute("id") || el.textContent
|
|
437
|
+
);
|
|
438
|
+
return {
|
|
439
|
+
tag: el.tagName.toLowerCase(),
|
|
440
|
+
label: labelText || null,
|
|
441
|
+
text: normalizeText(el.textContent).slice(0, 300) || null,
|
|
442
|
+
value: "value" in el ? normalizeText(String(el.value || "")) || null : null,
|
|
443
|
+
visible: isElementVisible(el),
|
|
444
|
+
enabled: !el.hasAttribute("disabled") && el.getAttribute("aria-disabled") !== "true",
|
|
445
|
+
focused: document.activeElement === el,
|
|
446
|
+
ariaInvalid: el.getAttribute("aria-invalid") === "true",
|
|
447
|
+
describedByText: describedByText || null,
|
|
448
|
+
role: el.getAttribute("role") || null,
|
|
449
|
+
selectorHint: selectorHintFor(el),
|
|
450
|
+
rect: {
|
|
451
|
+
x: Math.round(rect.x),
|
|
452
|
+
y: Math.round(rect.y),
|
|
453
|
+
width: Math.round(rect.width),
|
|
454
|
+
height: Math.round(rect.height)
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
680
459
|
// src/hooks/useModelNexSocket.ts
|
|
681
460
|
function useModelNexSocket({
|
|
682
461
|
serverUrl,
|
|
@@ -692,16 +471,16 @@ function useModelNexSocket({
|
|
|
692
471
|
websiteId,
|
|
693
472
|
devMode
|
|
694
473
|
}) {
|
|
695
|
-
const socketRef =
|
|
696
|
-
const actionsRef =
|
|
697
|
-
const contextsRef =
|
|
698
|
-
const documentationRef =
|
|
699
|
-
const tagsRef =
|
|
474
|
+
const socketRef = useRef2(null);
|
|
475
|
+
const actionsRef = useRef2(actions);
|
|
476
|
+
const contextsRef = useRef2(contexts);
|
|
477
|
+
const documentationRef = useRef2(documentation);
|
|
478
|
+
const tagsRef = useRef2(tags);
|
|
700
479
|
actionsRef.current = actions;
|
|
701
480
|
contextsRef.current = contexts;
|
|
702
481
|
documentationRef.current = documentation;
|
|
703
482
|
tagsRef.current = tags;
|
|
704
|
-
|
|
483
|
+
useEffect2(() => {
|
|
705
484
|
if (disabled) {
|
|
706
485
|
socketRef.current = null;
|
|
707
486
|
onSocketId?.(null);
|
|
@@ -775,11 +554,22 @@ function useModelNexSocket({
|
|
|
775
554
|
const NAV_ACTION_IDS = ["navigate_to_documents", "navigate_to_templates", "navigate_to_inbox", "navigate_to_settings", "navigate_to_template", "navigate_to_document", "navigate_to_folder", "navigate_editor_step"];
|
|
776
555
|
const action = currentActions.get(actionId);
|
|
777
556
|
if (action) {
|
|
557
|
+
const beforeSnapshot = captureDebugSnapshot();
|
|
558
|
+
const startedAt = Date.now();
|
|
778
559
|
try {
|
|
779
560
|
const validatedParams = action.schema.parse(params);
|
|
780
561
|
log("[SDK] Executing action", { actionId });
|
|
781
562
|
const execResult = await action.execute(validatedParams);
|
|
782
563
|
log("[SDK] Action completed", { actionId });
|
|
564
|
+
recordLastActionEffect({
|
|
565
|
+
actionId,
|
|
566
|
+
success: true,
|
|
567
|
+
error: null,
|
|
568
|
+
startedAt,
|
|
569
|
+
finishedAt: Date.now(),
|
|
570
|
+
before: beforeSnapshot,
|
|
571
|
+
after: captureDebugSnapshot()
|
|
572
|
+
});
|
|
783
573
|
sendResult(true, execResult);
|
|
784
574
|
if (NAV_ACTION_IDS.includes(actionId)) {
|
|
785
575
|
requestAnimationFrame(() => {
|
|
@@ -804,6 +594,15 @@ function useModelNexSocket({
|
|
|
804
594
|
} catch (err) {
|
|
805
595
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
806
596
|
console.error(`[ModelNex SDK] Execution failed for ${actionId}: ${errMsg}`);
|
|
597
|
+
recordLastActionEffect({
|
|
598
|
+
actionId,
|
|
599
|
+
success: false,
|
|
600
|
+
error: errMsg,
|
|
601
|
+
startedAt,
|
|
602
|
+
finishedAt: Date.now(),
|
|
603
|
+
before: beforeSnapshot,
|
|
604
|
+
after: captureDebugSnapshot()
|
|
605
|
+
});
|
|
807
606
|
if (isSdkDebugEnabled(devMode)) {
|
|
808
607
|
window.dispatchEvent(
|
|
809
608
|
new CustomEvent("modelnex-debug", {
|
|
@@ -834,7 +633,7 @@ function useModelNexSocket({
|
|
|
834
633
|
socket.disconnect();
|
|
835
634
|
};
|
|
836
635
|
}, [disabled, onSocketId, serverUrl]);
|
|
837
|
-
|
|
636
|
+
useEffect2(() => {
|
|
838
637
|
if (disabled) {
|
|
839
638
|
return;
|
|
840
639
|
}
|
|
@@ -852,10 +651,10 @@ function useModelNexSocket({
|
|
|
852
651
|
}
|
|
853
652
|
|
|
854
653
|
// src/hooks/useFieldHighlight.ts
|
|
855
|
-
import { useEffect as
|
|
654
|
+
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
856
655
|
function useFieldHighlight(stagingFields, executedFields, setExecutedFields) {
|
|
857
|
-
const highlightedRef =
|
|
858
|
-
|
|
656
|
+
const highlightedRef = useRef3(/* @__PURE__ */ new Map());
|
|
657
|
+
useEffect3(() => {
|
|
859
658
|
const prev = highlightedRef.current;
|
|
860
659
|
prev.forEach((_, fieldId) => {
|
|
861
660
|
if (!stagingFields.has(fieldId)) {
|
|
@@ -874,7 +673,7 @@ function useFieldHighlight(stagingFields, executedFields, setExecutedFields) {
|
|
|
874
673
|
prev.clear();
|
|
875
674
|
};
|
|
876
675
|
}, [stagingFields]);
|
|
877
|
-
|
|
676
|
+
useEffect3(() => {
|
|
878
677
|
const timeouts = [];
|
|
879
678
|
executedFields.forEach((fieldId) => {
|
|
880
679
|
const els = document.querySelectorAll(`[data-modelnex-field-id="${fieldId}"]`);
|
|
@@ -895,7 +694,7 @@ function useFieldHighlight(stagingFields, executedFields, setExecutedFields) {
|
|
|
895
694
|
}
|
|
896
695
|
|
|
897
696
|
// src/overlay.tsx
|
|
898
|
-
import React2, { useState
|
|
697
|
+
import React2, { useState, useEffect as useEffect4 } from "react";
|
|
899
698
|
var AIActivityOverlay = ({ activeActions }) => {
|
|
900
699
|
if (activeActions.size === 0) return null;
|
|
901
700
|
return React2.createElement(
|
|
@@ -1092,7 +891,7 @@ body.modelnex-highlight-actions [data-modelnex-action-id]::before {
|
|
|
1092
891
|
`;
|
|
1093
892
|
|
|
1094
893
|
// src/tag-store.ts
|
|
1095
|
-
import { useState as
|
|
894
|
+
import { useState as useState2, useCallback as useCallback2, useEffect as useEffect5 } from "react";
|
|
1096
895
|
var STORAGE_KEY = "modelnex-tags";
|
|
1097
896
|
function loadTags() {
|
|
1098
897
|
try {
|
|
@@ -1116,10 +915,10 @@ function saveTags(tags) {
|
|
|
1116
915
|
}
|
|
1117
916
|
}
|
|
1118
917
|
function useTagStore(options) {
|
|
1119
|
-
const [tags, setTags] =
|
|
918
|
+
const [tags, setTags] = useState2(/* @__PURE__ */ new Map());
|
|
1120
919
|
const apiUrl = options?.serverUrl ? `${options.serverUrl.replace(/\/$/, "")}/api/tags` : null;
|
|
1121
920
|
const websiteId = options?.websiteId;
|
|
1122
|
-
|
|
921
|
+
useEffect5(() => {
|
|
1123
922
|
const local = loadTags();
|
|
1124
923
|
setTags(local);
|
|
1125
924
|
if (apiUrl) {
|
|
@@ -1139,14 +938,14 @@ function useTagStore(options) {
|
|
|
1139
938
|
}).catch((err) => console.warn("[ModelNex] Failed to fetch remote tags:", err));
|
|
1140
939
|
}
|
|
1141
940
|
}, [apiUrl]);
|
|
1142
|
-
|
|
941
|
+
useEffect5(() => {
|
|
1143
942
|
if (typeof window === "undefined") return;
|
|
1144
943
|
saveTags(tags);
|
|
1145
944
|
}, [tags]);
|
|
1146
|
-
const getTag =
|
|
945
|
+
const getTag = useCallback2((fingerprint) => {
|
|
1147
946
|
return tags.get(fingerprint);
|
|
1148
947
|
}, [tags]);
|
|
1149
|
-
const setTag =
|
|
948
|
+
const setTag = useCallback2((fingerprint, description, category, metadata, selector, patternId, behavior, sourcePage, displayContext, skipRemoteSync) => {
|
|
1150
949
|
setTags((prev) => {
|
|
1151
950
|
const next = new Map(prev);
|
|
1152
951
|
const key = selector ? `selector:${selector}` : fingerprint;
|
|
@@ -1178,7 +977,7 @@ function useTagStore(options) {
|
|
|
1178
977
|
return next;
|
|
1179
978
|
});
|
|
1180
979
|
}, [apiUrl, websiteId]);
|
|
1181
|
-
const setTagsBatch =
|
|
980
|
+
const setTagsBatch = useCallback2((newTags, skipRemoteSync) => {
|
|
1182
981
|
setTags((prev) => {
|
|
1183
982
|
const next = new Map(prev);
|
|
1184
983
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1206,24 +1005,24 @@ function useTagStore(options) {
|
|
|
1206
1005
|
return next;
|
|
1207
1006
|
});
|
|
1208
1007
|
}, [apiUrl, websiteId]);
|
|
1209
|
-
const deleteTag =
|
|
1008
|
+
const deleteTag = useCallback2((fingerprint) => {
|
|
1210
1009
|
setTags((prev) => {
|
|
1211
1010
|
const next = new Map(prev);
|
|
1212
1011
|
next.delete(fingerprint);
|
|
1213
1012
|
return next;
|
|
1214
1013
|
});
|
|
1215
1014
|
}, []);
|
|
1216
|
-
const getAllTags =
|
|
1015
|
+
const getAllTags = useCallback2(() => {
|
|
1217
1016
|
return Array.from(tags.values());
|
|
1218
1017
|
}, [tags]);
|
|
1219
|
-
const exportTags =
|
|
1018
|
+
const exportTags = useCallback2(() => {
|
|
1220
1019
|
const obj = {};
|
|
1221
1020
|
tags.forEach((v, k) => {
|
|
1222
1021
|
obj[k] = v;
|
|
1223
1022
|
});
|
|
1224
1023
|
return JSON.stringify(obj, null, 2);
|
|
1225
1024
|
}, [tags]);
|
|
1226
|
-
const importTags =
|
|
1025
|
+
const importTags = useCallback2((json) => {
|
|
1227
1026
|
try {
|
|
1228
1027
|
const parsed = JSON.parse(json);
|
|
1229
1028
|
setTags((prev) => {
|
|
@@ -1239,7 +1038,7 @@ function useTagStore(options) {
|
|
|
1239
1038
|
}
|
|
1240
1039
|
|
|
1241
1040
|
// src/studio-mode.tsx
|
|
1242
|
-
import { useState as
|
|
1041
|
+
import { useState as useState3, useEffect as useEffect6, useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
1243
1042
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1244
1043
|
var CATEGORIES = [
|
|
1245
1044
|
{ value: "button", label: "Button" },
|
|
@@ -1250,10 +1049,10 @@ var CATEGORIES = [
|
|
|
1250
1049
|
{ value: "other", label: "Other" }
|
|
1251
1050
|
];
|
|
1252
1051
|
function TaggingPanel({ element, existingTag, onSave, onDelete, onClose }) {
|
|
1253
|
-
const [description, setDescription] =
|
|
1254
|
-
const [category, setCategory] =
|
|
1255
|
-
const inputRef =
|
|
1256
|
-
|
|
1052
|
+
const [description, setDescription] = useState3(existingTag?.description ?? "");
|
|
1053
|
+
const [category, setCategory] = useState3(existingTag?.category ?? "other");
|
|
1054
|
+
const inputRef = useRef4(null);
|
|
1055
|
+
useEffect6(() => {
|
|
1257
1056
|
inputRef.current?.focus();
|
|
1258
1057
|
}, []);
|
|
1259
1058
|
const handleSave = () => {
|
|
@@ -1480,10 +1279,10 @@ function findTagForElement(el, tagStore) {
|
|
|
1480
1279
|
return void 0;
|
|
1481
1280
|
}
|
|
1482
1281
|
function StudioOverlay({ elements, tagStore }) {
|
|
1483
|
-
const [selectedFingerprint, setSelectedFingerprint] =
|
|
1484
|
-
const [positions, setPositions] =
|
|
1485
|
-
const liveElementsRef =
|
|
1486
|
-
const updatePositions =
|
|
1282
|
+
const [selectedFingerprint, setSelectedFingerprint] = useState3(null);
|
|
1283
|
+
const [positions, setPositions] = useState3(/* @__PURE__ */ new Map());
|
|
1284
|
+
const liveElementsRef = useRef4(/* @__PURE__ */ new Map());
|
|
1285
|
+
const updatePositions = useCallback3(() => {
|
|
1487
1286
|
const newPositions = /* @__PURE__ */ new Map();
|
|
1488
1287
|
const newLiveElements = /* @__PURE__ */ new Map();
|
|
1489
1288
|
const knownFingerprints = new Set(elements.map((e) => e.fingerprint));
|
|
@@ -1518,7 +1317,7 @@ function StudioOverlay({ elements, tagStore }) {
|
|
|
1518
1317
|
liveElementsRef.current = newLiveElements;
|
|
1519
1318
|
setPositions(newPositions);
|
|
1520
1319
|
}, [elements]);
|
|
1521
|
-
|
|
1320
|
+
useEffect6(() => {
|
|
1522
1321
|
updatePositions();
|
|
1523
1322
|
const interval = setInterval(updatePositions, 1e3);
|
|
1524
1323
|
window.addEventListener("scroll", updatePositions, { passive: true });
|
|
@@ -1662,7 +1461,7 @@ function StudioOverlay({ elements, tagStore }) {
|
|
|
1662
1461
|
}
|
|
1663
1462
|
|
|
1664
1463
|
// src/hooks/useBuiltinActions.ts
|
|
1665
|
-
import { useEffect as
|
|
1464
|
+
import { useEffect as useEffect7, useRef as useRef5 } from "react";
|
|
1666
1465
|
import { z as z2 } from "zod";
|
|
1667
1466
|
|
|
1668
1467
|
// src/utils/screenshot.ts
|
|
@@ -1843,7 +1642,7 @@ function isTourEligible(tour, userProfile) {
|
|
|
1843
1642
|
var resolutionCache = /* @__PURE__ */ new Map();
|
|
1844
1643
|
var DEFAULT_WORKFLOW_SEARCH_LIMIT = 5;
|
|
1845
1644
|
var WORKFLOW_TYPES = ["onboarding", "tour"];
|
|
1846
|
-
function
|
|
1645
|
+
function safeQueryAll3(selector) {
|
|
1847
1646
|
try {
|
|
1848
1647
|
return Array.from(document.querySelectorAll(selector)).filter(
|
|
1849
1648
|
(el) => el instanceof HTMLElement
|
|
@@ -1852,6 +1651,9 @@ function safeQueryAll2(selector) {
|
|
|
1852
1651
|
return [];
|
|
1853
1652
|
}
|
|
1854
1653
|
}
|
|
1654
|
+
function safeQueryOne(selector) {
|
|
1655
|
+
return safeQueryAll3(selector)[0] || null;
|
|
1656
|
+
}
|
|
1855
1657
|
var ROW_SELECTORS = 'tr, [role="row"], [role="listitem"], li, [data-row], [class*="row"], [class*="card"], article, section';
|
|
1856
1658
|
function getRowText(el) {
|
|
1857
1659
|
const row = el.closest(ROW_SELECTORS);
|
|
@@ -1883,6 +1685,20 @@ function makeTarget(el, via) {
|
|
|
1883
1685
|
resolvedVia: via
|
|
1884
1686
|
};
|
|
1885
1687
|
}
|
|
1688
|
+
function resolveInspectableElement(params, getTagStore) {
|
|
1689
|
+
if (params.selector) {
|
|
1690
|
+
const bySelector = safeQueryOne(params.selector);
|
|
1691
|
+
if (bySelector) return makeTarget(bySelector, "single-selector");
|
|
1692
|
+
}
|
|
1693
|
+
if (params.fingerprint) {
|
|
1694
|
+
return resolveTargetElement(
|
|
1695
|
+
params.fingerprint,
|
|
1696
|
+
{ patternId: params.patternId, textContaining: params.textContaining },
|
|
1697
|
+
getTagStore()
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1886
1702
|
function resolveTargetElement(fingerprint, options, tagStore) {
|
|
1887
1703
|
const elements = extractInteractiveElements();
|
|
1888
1704
|
const byFp = elements.find((e) => e.fingerprint === fingerprint);
|
|
@@ -1891,7 +1707,7 @@ function resolveTargetElement(fingerprint, options, tagStore) {
|
|
|
1891
1707
|
if (cached) {
|
|
1892
1708
|
const cachedEl = elements.find((e) => e.fingerprint === cached.resolvedFingerprint);
|
|
1893
1709
|
if (cachedEl?.element) return makeTarget(cachedEl.element, "cache");
|
|
1894
|
-
const selectorMatches =
|
|
1710
|
+
const selectorMatches = safeQueryAll3(cached.selector);
|
|
1895
1711
|
if (selectorMatches.length === 1) {
|
|
1896
1712
|
return makeTarget(selectorMatches[0], "cache");
|
|
1897
1713
|
}
|
|
@@ -1910,7 +1726,7 @@ function resolveTargetElement(fingerprint, options, tagStore) {
|
|
|
1910
1726
|
const fallbackTags = patternId ? selectorTags.filter((t) => t.patternId !== patternId) : [];
|
|
1911
1727
|
for (const tagSet of [candidateTags, fallbackTags]) {
|
|
1912
1728
|
for (const tag of tagSet) {
|
|
1913
|
-
const matched =
|
|
1729
|
+
const matched = safeQueryAll3(tag.selector);
|
|
1914
1730
|
if (matched.length === 0) continue;
|
|
1915
1731
|
for (const el of matched) {
|
|
1916
1732
|
const fp = generateFingerprint(el);
|
|
@@ -1996,7 +1812,7 @@ function lastResortScan(fingerprint, options, elements) {
|
|
|
1996
1812
|
}
|
|
1997
1813
|
if (bestEl) return makeTarget(bestEl, "fuzzy");
|
|
1998
1814
|
if (fpTextHint) {
|
|
1999
|
-
const ariaMatches =
|
|
1815
|
+
const ariaMatches = safeQueryAll3(`[aria-label*="${CSS.escape(fpTextHint)}"], [data-testid*="${CSS.escape(fpTextHint)}"]`);
|
|
2000
1816
|
if (textContaining && ariaMatches.length > 1) {
|
|
2001
1817
|
let best = null;
|
|
2002
1818
|
let bestS = 0;
|
|
@@ -2338,6 +2154,68 @@ function createWaitAction(getTagStore) {
|
|
|
2338
2154
|
}
|
|
2339
2155
|
};
|
|
2340
2156
|
}
|
|
2157
|
+
var getUiMessagesSchema = z2.object({
|
|
2158
|
+
includeHidden: z2.boolean().optional().describe("Include currently hidden UI messages when true. Defaults to false."),
|
|
2159
|
+
maxItems: z2.number().int().min(1).max(20).optional().describe("Maximum number of messages to return. Defaults to 10.")
|
|
2160
|
+
});
|
|
2161
|
+
function createGetUiMessagesAction() {
|
|
2162
|
+
return {
|
|
2163
|
+
id: "get_ui_messages",
|
|
2164
|
+
description: "Return current UI-facing messages such as alerts, warnings, toasts, banners, and inline validation text.",
|
|
2165
|
+
schema: getUiMessagesSchema,
|
|
2166
|
+
execute: async (params) => {
|
|
2167
|
+
const messages = collectUiMessages(params);
|
|
2168
|
+
return {
|
|
2169
|
+
messages,
|
|
2170
|
+
total: messages.length,
|
|
2171
|
+
capturedAt: Date.now()
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
var getLastActionEffectSchema = z2.object({});
|
|
2177
|
+
function createGetLastActionEffectAction() {
|
|
2178
|
+
return {
|
|
2179
|
+
id: "get_last_action_effect",
|
|
2180
|
+
description: "Return a compact summary of what changed after the most recent agent action.",
|
|
2181
|
+
schema: getLastActionEffectSchema,
|
|
2182
|
+
execute: async () => {
|
|
2183
|
+
return {
|
|
2184
|
+
effect: getLastActionEffectRecord()
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
var inspectElementStateSchema = z2.object({
|
|
2190
|
+
fingerprint: z2.string().optional().describe("Fingerprint of the target element to inspect."),
|
|
2191
|
+
selector: z2.string().optional().describe("Optional CSS selector for direct inspection when a fingerprint is unavailable."),
|
|
2192
|
+
patternId: z2.string().optional().describe("Optional patternId from tagged elements for disambiguation."),
|
|
2193
|
+
textContaining: z2.string().optional().describe("Optional row or label text used to disambiguate similar elements.")
|
|
2194
|
+
});
|
|
2195
|
+
function createInspectElementStateAction(getTagStore) {
|
|
2196
|
+
return {
|
|
2197
|
+
id: "inspect_element_state",
|
|
2198
|
+
description: "Inspect one specific element and return its current visibility, enabled state, value/text, aria state, and associated error/help text.",
|
|
2199
|
+
schema: inspectElementStateSchema,
|
|
2200
|
+
execute: async (params) => {
|
|
2201
|
+
const target = resolveInspectableElement(params, getTagStore);
|
|
2202
|
+
if (!target) {
|
|
2203
|
+
return {
|
|
2204
|
+
found: false,
|
|
2205
|
+
reason: "Element not found from the provided fingerprint or selector."
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
return {
|
|
2209
|
+
found: true,
|
|
2210
|
+
target: {
|
|
2211
|
+
fingerprint: target.fingerprint,
|
|
2212
|
+
resolvedVia: target.resolvedVia
|
|
2213
|
+
},
|
|
2214
|
+
element: inspectDomElementState(target.element)
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2341
2219
|
var searchDocsSchema = z2.object({
|
|
2342
2220
|
query: z2.string().describe("The search query to find relevant documentation."),
|
|
2343
2221
|
limit: z2.number().optional().describe("Maximum number of results to return (default: 5).")
|
|
@@ -2507,6 +2385,9 @@ var BUILTIN_ACTION_IDS = {
|
|
|
2507
2385
|
click: "click_element",
|
|
2508
2386
|
fill: "fill_input",
|
|
2509
2387
|
wait: "request_user_action",
|
|
2388
|
+
getUiMessages: "get_ui_messages",
|
|
2389
|
+
getLastActionEffect: "get_last_action_effect",
|
|
2390
|
+
inspectElementState: "inspect_element_state",
|
|
2510
2391
|
searchDocs: "search_docs",
|
|
2511
2392
|
searchTaggedElements: "search_tagged_elements",
|
|
2512
2393
|
searchWorkflows: "search_workflows",
|
|
@@ -2514,18 +2395,18 @@ var BUILTIN_ACTION_IDS = {
|
|
|
2514
2395
|
startWorkflow: "start_workflow"
|
|
2515
2396
|
};
|
|
2516
2397
|
function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile) {
|
|
2517
|
-
const registeredRef =
|
|
2518
|
-
const tagStoreRef =
|
|
2398
|
+
const registeredRef = useRef5(false);
|
|
2399
|
+
const tagStoreRef = useRef5(tagStore);
|
|
2519
2400
|
tagStoreRef.current = tagStore;
|
|
2520
|
-
const serverUrlRef =
|
|
2401
|
+
const serverUrlRef = useRef5(serverUrl);
|
|
2521
2402
|
serverUrlRef.current = serverUrl;
|
|
2522
|
-
const websiteIdRef =
|
|
2403
|
+
const websiteIdRef = useRef5(websiteId);
|
|
2523
2404
|
websiteIdRef.current = websiteId;
|
|
2524
|
-
const toursApiBaseRef =
|
|
2405
|
+
const toursApiBaseRef = useRef5(toursApiBase);
|
|
2525
2406
|
toursApiBaseRef.current = toursApiBase;
|
|
2526
|
-
const userProfileRef =
|
|
2407
|
+
const userProfileRef = useRef5(userProfile);
|
|
2527
2408
|
userProfileRef.current = userProfile;
|
|
2528
|
-
|
|
2409
|
+
useEffect7(() => {
|
|
2529
2410
|
if (registeredRef.current) return;
|
|
2530
2411
|
registeredRef.current = true;
|
|
2531
2412
|
const getTagStore = () => tagStoreRef.current;
|
|
@@ -2543,6 +2424,9 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
|
|
|
2543
2424
|
registerAction(createClickAction(getTagStore));
|
|
2544
2425
|
registerAction(createFillAction(getTagStore));
|
|
2545
2426
|
registerAction(createWaitAction(getTagStore));
|
|
2427
|
+
registerAction(createGetUiMessagesAction());
|
|
2428
|
+
registerAction(createGetLastActionEffectAction());
|
|
2429
|
+
registerAction(createInspectElementStateAction(getTagStore));
|
|
2546
2430
|
registerAction(createSearchDocsAction(getServerUrl, getWebsiteId));
|
|
2547
2431
|
registerAction(createSearchTaggedElementsAction(getTagStore));
|
|
2548
2432
|
registerAction(createSearchWorkflowsAction(workflowToolGetters));
|
|
@@ -2553,6 +2437,9 @@ function useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl
|
|
|
2553
2437
|
unregisterAction(BUILTIN_ACTION_IDS.click);
|
|
2554
2438
|
unregisterAction(BUILTIN_ACTION_IDS.fill);
|
|
2555
2439
|
unregisterAction(BUILTIN_ACTION_IDS.wait);
|
|
2440
|
+
unregisterAction(BUILTIN_ACTION_IDS.getUiMessages);
|
|
2441
|
+
unregisterAction(BUILTIN_ACTION_IDS.getLastActionEffect);
|
|
2442
|
+
unregisterAction(BUILTIN_ACTION_IDS.inspectElementState);
|
|
2556
2443
|
unregisterAction(BUILTIN_ACTION_IDS.searchDocs);
|
|
2557
2444
|
unregisterAction(BUILTIN_ACTION_IDS.searchTaggedElements);
|
|
2558
2445
|
unregisterAction(BUILTIN_ACTION_IDS.searchWorkflows);
|
|
@@ -2880,7 +2767,7 @@ function readPreviewSessionSuppression() {
|
|
|
2880
2767
|
}
|
|
2881
2768
|
|
|
2882
2769
|
// src/hooks/useRunCommand.ts
|
|
2883
|
-
import { useCallback as
|
|
2770
|
+
import { useCallback as useCallback4, useContext as useContext2 } from "react";
|
|
2884
2771
|
function searchTaggedElementsForQuery(store, query, limit = 8) {
|
|
2885
2772
|
const allTags = store.getAllTags();
|
|
2886
2773
|
if (allTags.length === 0) return [];
|
|
@@ -2903,7 +2790,7 @@ function useRunCommand(serverUrlOverride) {
|
|
|
2903
2790
|
const context = useContext2(ModelNexContext);
|
|
2904
2791
|
const baseUrl = serverUrlOverride ?? context?.commandUrl ?? context?.serverUrl ?? DEFAULT_MODELNEX_SERVER_URL;
|
|
2905
2792
|
const tagStore = context?.tagStore;
|
|
2906
|
-
return
|
|
2793
|
+
return useCallback4(
|
|
2907
2794
|
async (command, signal) => {
|
|
2908
2795
|
const url = baseUrl.startsWith("/") ? `${window.location.origin}${baseUrl}/agent/command` : `${baseUrl}/agent/command`;
|
|
2909
2796
|
const relevantTaggedElements = tagStore ? searchTaggedElementsForQuery(tagStore, command) : void 0;
|
|
@@ -2931,7 +2818,7 @@ function useRunCommand(serverUrlOverride) {
|
|
|
2931
2818
|
}
|
|
2932
2819
|
|
|
2933
2820
|
// src/ui-state.tsx
|
|
2934
|
-
import { createContext as createContext2, useContext as useContext3, useState as
|
|
2821
|
+
import { createContext as createContext2, useContext as useContext3, useState as useState4, useCallback as useCallback5, useMemo as useMemo2 } from "react";
|
|
2935
2822
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
2936
2823
|
var UIStateContext = createContext2(null);
|
|
2937
2824
|
function UIStateProvider({
|
|
@@ -2940,8 +2827,8 @@ function UIStateProvider({
|
|
|
2940
2827
|
type = "ui",
|
|
2941
2828
|
initialState = {}
|
|
2942
2829
|
}) {
|
|
2943
|
-
const [state, setStateInternal] =
|
|
2944
|
-
const setState =
|
|
2830
|
+
const [state, setStateInternal] = useState4(initialState);
|
|
2831
|
+
const setState = useCallback5((update) => {
|
|
2945
2832
|
setStateInternal((prev) => ({ ...prev, ...update }));
|
|
2946
2833
|
}, []);
|
|
2947
2834
|
const value = useMemo2(() => ({ state, setState }), [state, setState]);
|
|
@@ -2954,7 +2841,7 @@ function useUIState() {
|
|
|
2954
2841
|
}
|
|
2955
2842
|
|
|
2956
2843
|
// src/viewport.ts
|
|
2957
|
-
import { useEffect as
|
|
2844
|
+
import { useEffect as useEffect9, useRef as useRef6, useState as useState5 } from "react";
|
|
2958
2845
|
var visibleIds = /* @__PURE__ */ new Set();
|
|
2959
2846
|
var listeners = /* @__PURE__ */ new Set();
|
|
2960
2847
|
function updateVisibility(id, visible) {
|
|
@@ -2963,9 +2850,9 @@ function updateVisibility(id, visible) {
|
|
|
2963
2850
|
listeners.forEach((fn) => fn());
|
|
2964
2851
|
}
|
|
2965
2852
|
function useViewportTrack(id) {
|
|
2966
|
-
const ref =
|
|
2967
|
-
const [, setTick] =
|
|
2968
|
-
|
|
2853
|
+
const ref = useRef6(null);
|
|
2854
|
+
const [, setTick] = useState5(0);
|
|
2855
|
+
useEffect9(() => {
|
|
2969
2856
|
const el = ref.current;
|
|
2970
2857
|
if (!el) return;
|
|
2971
2858
|
const obs = new IntersectionObserver(
|
|
@@ -2980,7 +2867,7 @@ function useViewportTrack(id) {
|
|
|
2980
2867
|
updateVisibility(id, false);
|
|
2981
2868
|
};
|
|
2982
2869
|
}, [id]);
|
|
2983
|
-
|
|
2870
|
+
useEffect9(() => {
|
|
2984
2871
|
const fn = () => setTick((t) => t + 1);
|
|
2985
2872
|
listeners.add(fn);
|
|
2986
2873
|
return () => {
|
|
@@ -2990,8 +2877,8 @@ function useViewportTrack(id) {
|
|
|
2990
2877
|
return ref;
|
|
2991
2878
|
}
|
|
2992
2879
|
function useVisibleIds() {
|
|
2993
|
-
const [ids, setIds] =
|
|
2994
|
-
|
|
2880
|
+
const [ids, setIds] = useState5([]);
|
|
2881
|
+
useEffect9(() => {
|
|
2995
2882
|
const fn = () => setIds(Array.from(visibleIds));
|
|
2996
2883
|
fn();
|
|
2997
2884
|
listeners.add(fn);
|
|
@@ -3008,11 +2895,11 @@ function useAgentViewport(options = {}) {
|
|
|
3008
2895
|
}
|
|
3009
2896
|
|
|
3010
2897
|
// src/chat-bubble.tsx
|
|
3011
|
-
import { useState as
|
|
2898
|
+
import { useState as useState12, useRef as useRef12, useEffect as useEffect16, useContext as useContext5, useCallback as useCallback11, useMemo as useMemo3 } from "react";
|
|
3012
2899
|
import { createPortal, flushSync } from "react-dom";
|
|
3013
2900
|
|
|
3014
2901
|
// src/hooks/useExperiencePlaybackController.ts
|
|
3015
|
-
import { useCallback as
|
|
2902
|
+
import { useCallback as useCallback7, useEffect as useEffect11, useRef as useRef8, useState as useState7 } from "react";
|
|
3016
2903
|
|
|
3017
2904
|
// src/utils/locationSignature.ts
|
|
3018
2905
|
function getLocationSignature(locationLike) {
|
|
@@ -3021,7 +2908,7 @@ function getLocationSignature(locationLike) {
|
|
|
3021
2908
|
}
|
|
3022
2909
|
|
|
3023
2910
|
// src/hooks/useTourPlayback.ts
|
|
3024
|
-
import { useState as
|
|
2911
|
+
import { useState as useState6, useRef as useRef7, useCallback as useCallback6, useEffect as useEffect10, useContext as useContext4 } from "react";
|
|
3025
2912
|
|
|
3026
2913
|
// src/utils/retryLookup.ts
|
|
3027
2914
|
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -3787,58 +3674,58 @@ function useTourPlayback({
|
|
|
3787
3674
|
enableAutoDiscovery = true
|
|
3788
3675
|
}) {
|
|
3789
3676
|
const locationSignature = typeof window === "undefined" ? "" : getLocationSignature(window.location);
|
|
3790
|
-
const [isActive, setIsActive] =
|
|
3791
|
-
const [currentStepIndex, setCurrentStepIndex] =
|
|
3792
|
-
const [totalSteps, setTotalSteps] =
|
|
3793
|
-
const [activeTour, setActiveTour] =
|
|
3794
|
-
const [playbackState, setPlaybackState] =
|
|
3795
|
-
const [isReviewMode, setIsReviewMode] =
|
|
3796
|
-
const [previewRunId, setPreviewRunId] =
|
|
3797
|
-
const [reviewSubmitting, setReviewSubmitting] =
|
|
3798
|
-
const [reviewStatusMessage, setReviewStatusMessage] =
|
|
3799
|
-
const [pendingTour, setPendingTour] =
|
|
3800
|
-
const [serverState, setServerState] =
|
|
3677
|
+
const [isActive, setIsActive] = useState6(false);
|
|
3678
|
+
const [currentStepIndex, setCurrentStepIndex] = useState6(0);
|
|
3679
|
+
const [totalSteps, setTotalSteps] = useState6(0);
|
|
3680
|
+
const [activeTour, setActiveTour] = useState6(null);
|
|
3681
|
+
const [playbackState, setPlaybackState] = useState6("idle");
|
|
3682
|
+
const [isReviewMode, setIsReviewMode] = useState6(false);
|
|
3683
|
+
const [previewRunId, setPreviewRunId] = useState6(null);
|
|
3684
|
+
const [reviewSubmitting, setReviewSubmitting] = useState6(false);
|
|
3685
|
+
const [reviewStatusMessage, setReviewStatusMessage] = useState6(null);
|
|
3686
|
+
const [pendingTour, setPendingTour] = useState6(null);
|
|
3687
|
+
const [serverState, setServerState] = useState6(null);
|
|
3801
3688
|
const ctx = useContext4(ModelNexContext);
|
|
3802
3689
|
const devMode = ctx?.devMode;
|
|
3803
|
-
const devModeRef =
|
|
3690
|
+
const devModeRef = useRef7(devMode);
|
|
3804
3691
|
devModeRef.current = devMode;
|
|
3805
|
-
const userProfileRef =
|
|
3692
|
+
const userProfileRef = useRef7(userProfile);
|
|
3806
3693
|
userProfileRef.current = userProfile;
|
|
3807
|
-
const experienceTypeRef =
|
|
3694
|
+
const experienceTypeRef = useRef7(experienceType);
|
|
3808
3695
|
experienceTypeRef.current = experienceType;
|
|
3809
|
-
const tourRef =
|
|
3810
|
-
const stepIndexRef =
|
|
3811
|
-
const skipRequestedRef =
|
|
3812
|
-
const advanceRequestedRef =
|
|
3813
|
-
const textInputResolveRef =
|
|
3814
|
-
const voiceInputResolveRef =
|
|
3815
|
-
const askOrFillRef =
|
|
3816
|
-
const pendingManualWaitCleanupRef =
|
|
3817
|
-
const pendingManualInputSyncRef =
|
|
3818
|
-
const llmRespondingRef =
|
|
3819
|
-
const interruptedForQuestionRef =
|
|
3820
|
-
const pendingInputBufRef =
|
|
3821
|
-
const activeExecutionTokenRef =
|
|
3822
|
-
const commandInFlightRef =
|
|
3823
|
-
const reviewModeRef =
|
|
3824
|
-
const previewRunIdRef =
|
|
3825
|
-
const toursApiBaseRef =
|
|
3826
|
-
const pendingTourRef =
|
|
3827
|
-
const pendingTourOptionsRef =
|
|
3828
|
-
const showCaptionsRef =
|
|
3829
|
-
const initialPersistedTourSessionRef =
|
|
3830
|
-
const runIdRef =
|
|
3831
|
-
const turnIdRef =
|
|
3832
|
-
const startRequestedRef =
|
|
3833
|
-
const playbackOwnerIdRef =
|
|
3834
|
-
const claimedPlaybackOwnerKeyRef =
|
|
3835
|
-
const socketRef =
|
|
3836
|
-
const socketIdRef =
|
|
3837
|
-
const commandUrlRef =
|
|
3838
|
-
const websiteIdRef =
|
|
3839
|
-
const onStepChangeRef =
|
|
3840
|
-
const isActiveRef =
|
|
3841
|
-
const activeCommandBatchIdRef =
|
|
3696
|
+
const tourRef = useRef7(null);
|
|
3697
|
+
const stepIndexRef = useRef7(0);
|
|
3698
|
+
const skipRequestedRef = useRef7(false);
|
|
3699
|
+
const advanceRequestedRef = useRef7(false);
|
|
3700
|
+
const textInputResolveRef = useRef7(null);
|
|
3701
|
+
const voiceInputResolveRef = useRef7(null);
|
|
3702
|
+
const askOrFillRef = useRef7(null);
|
|
3703
|
+
const pendingManualWaitCleanupRef = useRef7(null);
|
|
3704
|
+
const pendingManualInputSyncRef = useRef7(null);
|
|
3705
|
+
const llmRespondingRef = useRef7(false);
|
|
3706
|
+
const interruptedForQuestionRef = useRef7(false);
|
|
3707
|
+
const pendingInputBufRef = useRef7(null);
|
|
3708
|
+
const activeExecutionTokenRef = useRef7(0);
|
|
3709
|
+
const commandInFlightRef = useRef7(false);
|
|
3710
|
+
const reviewModeRef = useRef7(false);
|
|
3711
|
+
const previewRunIdRef = useRef7(null);
|
|
3712
|
+
const toursApiBaseRef = useRef7(toursApiBase);
|
|
3713
|
+
const pendingTourRef = useRef7(null);
|
|
3714
|
+
const pendingTourOptionsRef = useRef7(null);
|
|
3715
|
+
const showCaptionsRef = useRef7(showCaptions);
|
|
3716
|
+
const initialPersistedTourSessionRef = useRef7(readPersistedActiveTourSession());
|
|
3717
|
+
const runIdRef = useRef7(initialPersistedTourSessionRef.current?.runId ?? null);
|
|
3718
|
+
const turnIdRef = useRef7(null);
|
|
3719
|
+
const startRequestedRef = useRef7(false);
|
|
3720
|
+
const playbackOwnerIdRef = useRef7(`playback-owner-${Math.random().toString(36).slice(2, 10)}`);
|
|
3721
|
+
const claimedPlaybackOwnerKeyRef = useRef7(null);
|
|
3722
|
+
const socketRef = useRef7(null);
|
|
3723
|
+
const socketIdRef = useRef7(socketId);
|
|
3724
|
+
const commandUrlRef = useRef7(commandUrl);
|
|
3725
|
+
const websiteIdRef = useRef7(websiteId);
|
|
3726
|
+
const onStepChangeRef = useRef7(onStepChange);
|
|
3727
|
+
const isActiveRef = useRef7(false);
|
|
3728
|
+
const activeCommandBatchIdRef = useRef7(null);
|
|
3842
3729
|
socketIdRef.current = socketId;
|
|
3843
3730
|
commandUrlRef.current = commandUrl;
|
|
3844
3731
|
websiteIdRef.current = websiteId;
|
|
@@ -3849,13 +3736,13 @@ function useTourPlayback({
|
|
|
3849
3736
|
toursApiBaseRef.current = toursApiBase;
|
|
3850
3737
|
pendingTourRef.current = pendingTour;
|
|
3851
3738
|
showCaptionsRef.current = showCaptions;
|
|
3852
|
-
const releasePlaybackOwnership =
|
|
3739
|
+
const releasePlaybackOwnership = useCallback6(() => {
|
|
3853
3740
|
const claimedKey = claimedPlaybackOwnerKeyRef.current;
|
|
3854
3741
|
if (!claimedKey) return;
|
|
3855
3742
|
releaseTourPlaybackOwnership(claimedKey, playbackOwnerIdRef.current);
|
|
3856
3743
|
claimedPlaybackOwnerKeyRef.current = null;
|
|
3857
3744
|
}, []);
|
|
3858
|
-
const emitTourInit =
|
|
3745
|
+
const emitTourInit = useCallback6((socket, currentWebsiteId, profile) => {
|
|
3859
3746
|
if (!currentWebsiteId || !profile?.type) return;
|
|
3860
3747
|
const persistedRunId = runIdRef.current ?? readPersistedActiveTourSession()?.runId ?? null;
|
|
3861
3748
|
const payload = {
|
|
@@ -3868,7 +3755,7 @@ function useTourPlayback({
|
|
|
3868
3755
|
}
|
|
3869
3756
|
emitSocketEvent(socket, "tour:init", payload);
|
|
3870
3757
|
}, []);
|
|
3871
|
-
|
|
3758
|
+
useEffect10(() => {
|
|
3872
3759
|
if (disabled) return;
|
|
3873
3760
|
if (typeof window === "undefined") return;
|
|
3874
3761
|
const socket = tourSocketPool.acquire(serverUrl);
|
|
@@ -4017,7 +3904,7 @@ function useTourPlayback({
|
|
|
4017
3904
|
resolve: async () => {
|
|
4018
3905
|
let targetEl = null;
|
|
4019
3906
|
if (params.uid) {
|
|
4020
|
-
const { getElementByUid } = await import("./aom-
|
|
3907
|
+
const { getElementByUid } = await import("./aom-SP2LMWQI.mjs");
|
|
4021
3908
|
targetEl = getElementByUid(params.uid);
|
|
4022
3909
|
}
|
|
4023
3910
|
if (!targetEl) {
|
|
@@ -4495,7 +4382,7 @@ function useTourPlayback({
|
|
|
4495
4382
|
void recordTourEvent(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, "started", websiteId);
|
|
4496
4383
|
}
|
|
4497
4384
|
try {
|
|
4498
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4385
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-SP2LMWQI.mjs");
|
|
4499
4386
|
const aom = generateMinifiedAOM2();
|
|
4500
4387
|
if (socketRef.current === socket) {
|
|
4501
4388
|
emitSocketEvent(socket, "tour:sync_dom", {
|
|
@@ -4579,7 +4466,7 @@ function useTourPlayback({
|
|
|
4579
4466
|
tourSocketPool.release(serverUrl, toClose);
|
|
4580
4467
|
};
|
|
4581
4468
|
}, [serverUrl, disabled, emitTourInit, releasePlaybackOwnership]);
|
|
4582
|
-
|
|
4469
|
+
useEffect10(() => {
|
|
4583
4470
|
if (disabled) return;
|
|
4584
4471
|
const s = socketRef.current;
|
|
4585
4472
|
const profile = userProfile;
|
|
@@ -4589,12 +4476,12 @@ function useTourPlayback({
|
|
|
4589
4476
|
}, 150);
|
|
4590
4477
|
return () => clearTimeout(timer);
|
|
4591
4478
|
}, [disabled, emitTourInit, websiteId, userProfile?.userId, userProfile?.type]);
|
|
4592
|
-
|
|
4479
|
+
useEffect10(() => {
|
|
4593
4480
|
if (!showCaptions || !isReviewMode) {
|
|
4594
4481
|
removeCaption();
|
|
4595
4482
|
}
|
|
4596
4483
|
}, [showCaptions, isReviewMode]);
|
|
4597
|
-
|
|
4484
|
+
useEffect10(() => {
|
|
4598
4485
|
if (!isActiveRef.current) return;
|
|
4599
4486
|
emitSocketEvent(socketRef.current, "tour:client_state", {
|
|
4600
4487
|
runId: runIdRef.current,
|
|
@@ -4608,9 +4495,9 @@ function useTourPlayback({
|
|
|
4608
4495
|
awaitingVoiceResolve: Boolean(voiceInputResolveRef.current)
|
|
4609
4496
|
});
|
|
4610
4497
|
}, [isActive, playbackState, voice.isListening, voice.isSpeaking]);
|
|
4611
|
-
const syncAOM =
|
|
4498
|
+
const syncAOM = useCallback6(async () => {
|
|
4612
4499
|
if (!isActiveRef.current) return;
|
|
4613
|
-
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-
|
|
4500
|
+
const { generateMinifiedAOM: generateMinifiedAOM2 } = await import("./aom-SP2LMWQI.mjs");
|
|
4614
4501
|
const aom = generateMinifiedAOM2();
|
|
4615
4502
|
emitSocketEvent(socketRef.current, "tour:sync_dom", {
|
|
4616
4503
|
url: window.location.pathname + window.location.search + window.location.hash,
|
|
@@ -4618,7 +4505,7 @@ function useTourPlayback({
|
|
|
4618
4505
|
domSummary: captureDomSummary()
|
|
4619
4506
|
});
|
|
4620
4507
|
}, []);
|
|
4621
|
-
const scheduleManualInputSync =
|
|
4508
|
+
const scheduleManualInputSync = useCallback6(() => {
|
|
4622
4509
|
if (pendingManualInputSyncRef.current) {
|
|
4623
4510
|
clearTimeout(pendingManualInputSyncRef.current);
|
|
4624
4511
|
}
|
|
@@ -4630,13 +4517,13 @@ function useTourPlayback({
|
|
|
4630
4517
|
await syncAOM();
|
|
4631
4518
|
}, 150);
|
|
4632
4519
|
}, [syncAOM]);
|
|
4633
|
-
const clearPendingManualInputSync =
|
|
4520
|
+
const clearPendingManualInputSync = useCallback6(() => {
|
|
4634
4521
|
if (pendingManualInputSyncRef.current) {
|
|
4635
4522
|
clearTimeout(pendingManualInputSyncRef.current);
|
|
4636
4523
|
pendingManualInputSyncRef.current = null;
|
|
4637
4524
|
}
|
|
4638
4525
|
}, []);
|
|
4639
|
-
const interruptExecution =
|
|
4526
|
+
const interruptExecution = useCallback6((transcript) => {
|
|
4640
4527
|
if (!isSocketWritable(socketRef.current) || !isActiveRef.current) return false;
|
|
4641
4528
|
if (!commandInFlightRef.current && !voice.isSpeaking) return false;
|
|
4642
4529
|
interruptedForQuestionRef.current = true;
|
|
@@ -4664,7 +4551,7 @@ function useTourPlayback({
|
|
|
4664
4551
|
setPlaybackState("thinking");
|
|
4665
4552
|
return true;
|
|
4666
4553
|
}, [voice]);
|
|
4667
|
-
const stopTour =
|
|
4554
|
+
const stopTour = useCallback6(() => {
|
|
4668
4555
|
skipRequestedRef.current = true;
|
|
4669
4556
|
isActiveRef.current = false;
|
|
4670
4557
|
startRequestedRef.current = false;
|
|
@@ -4715,7 +4602,7 @@ function useTourPlayback({
|
|
|
4715
4602
|
pendingInputBufRef.current = null;
|
|
4716
4603
|
onTourEnd?.();
|
|
4717
4604
|
}, [voice, onTourEnd, serverUrl, websiteId, releasePlaybackOwnership]);
|
|
4718
|
-
const handleTourEnd =
|
|
4605
|
+
const handleTourEnd = useCallback6(() => {
|
|
4719
4606
|
const endingTourId = tourRef.current?.id;
|
|
4720
4607
|
const endingPreviewRunId = previewRunIdRef.current;
|
|
4721
4608
|
const endingStepOrder = stepIndexRef.current;
|
|
@@ -4762,7 +4649,7 @@ function useTourPlayback({
|
|
|
4762
4649
|
}
|
|
4763
4650
|
onTourEnd?.();
|
|
4764
4651
|
}, [experienceType, userProfile, serverUrl, voice, onTourEnd, websiteId, releasePlaybackOwnership]);
|
|
4765
|
-
const runTour =
|
|
4652
|
+
const runTour = useCallback6(async (tour, options) => {
|
|
4766
4653
|
if (!shouldAcceptTourStart({
|
|
4767
4654
|
isPlaybackActive: isActiveRef.current,
|
|
4768
4655
|
startRequested: startRequestedRef.current
|
|
@@ -4827,7 +4714,7 @@ function useTourPlayback({
|
|
|
4827
4714
|
tourContext: compactTourForTransport(tour)
|
|
4828
4715
|
});
|
|
4829
4716
|
}, [serverUrl, websiteId]);
|
|
4830
|
-
|
|
4717
|
+
useEffect10(() => {
|
|
4831
4718
|
if (!shouldRunTourAutoDiscovery({
|
|
4832
4719
|
enableAutoDiscovery,
|
|
4833
4720
|
disabled,
|
|
@@ -4891,7 +4778,7 @@ function useTourPlayback({
|
|
|
4891
4778
|
cancelled = true;
|
|
4892
4779
|
};
|
|
4893
4780
|
}, [serverUrl, toursApiBase, disabled, websiteId, experienceType, enableAutoDiscovery, locationSignature]);
|
|
4894
|
-
|
|
4781
|
+
useEffect10(() => {
|
|
4895
4782
|
if (!shouldRunTourAutoDiscovery({
|
|
4896
4783
|
enableAutoDiscovery,
|
|
4897
4784
|
disabled,
|
|
@@ -4938,22 +4825,22 @@ function useTourPlayback({
|
|
|
4938
4825
|
clearTimeout(timer);
|
|
4939
4826
|
};
|
|
4940
4827
|
}, [websiteId, serverUrl, toursApiBase, disabled, experienceType, userProfile?.userId, userProfile?.type, userProfile?.isNewUser, enableAutoDiscovery, locationSignature]);
|
|
4941
|
-
|
|
4828
|
+
useEffect10(() => {
|
|
4942
4829
|
if (!disabled || !isActiveRef.current) return;
|
|
4943
4830
|
stopTour();
|
|
4944
4831
|
}, [disabled, stopTour]);
|
|
4945
|
-
const startTour =
|
|
4832
|
+
const startTour = useCallback6((tour, options) => {
|
|
4946
4833
|
if (disabled) return;
|
|
4947
4834
|
void runTour(tour, options);
|
|
4948
4835
|
}, [disabled, runTour]);
|
|
4949
|
-
const acceptPendingTour =
|
|
4836
|
+
const acceptPendingTour = useCallback6(() => {
|
|
4950
4837
|
const tour = pendingTourRef.current;
|
|
4951
4838
|
if (!tour || disabled) return;
|
|
4952
4839
|
const opts = pendingTourOptionsRef.current;
|
|
4953
4840
|
pendingTourOptionsRef.current = null;
|
|
4954
4841
|
void runTour(tour, opts ?? void 0);
|
|
4955
4842
|
}, [disabled, runTour]);
|
|
4956
|
-
const dismissPendingTour =
|
|
4843
|
+
const dismissPendingTour = useCallback6(() => {
|
|
4957
4844
|
const tour = pendingTourRef.current;
|
|
4958
4845
|
setPendingTour(null);
|
|
4959
4846
|
pendingTourRef.current = null;
|
|
@@ -4961,7 +4848,7 @@ function useTourPlayback({
|
|
|
4961
4848
|
if (!tour || !userProfile?.userId) return;
|
|
4962
4849
|
void markTourDismissed(serverUrl, toursApiBaseRef.current, tour.id, userProfile.userId, experienceType, websiteId);
|
|
4963
4850
|
}, [experienceType, serverUrl, userProfile, websiteId]);
|
|
4964
|
-
const submitReviewFeedbackAction =
|
|
4851
|
+
const submitReviewFeedbackAction = useCallback6(async (utterance, apply = false) => {
|
|
4965
4852
|
const trimmed = utterance.trim();
|
|
4966
4853
|
const currentTour = tourRef.current;
|
|
4967
4854
|
const currentPreviewRunId = previewRunIdRef.current;
|
|
@@ -5001,10 +4888,10 @@ function useTourPlayback({
|
|
|
5001
4888
|
setReviewSubmitting(false);
|
|
5002
4889
|
}
|
|
5003
4890
|
}, [playbackState, serverUrl, websiteId]);
|
|
5004
|
-
const advanceStep =
|
|
4891
|
+
const advanceStep = useCallback6(() => {
|
|
5005
4892
|
advanceRequestedRef.current = true;
|
|
5006
4893
|
}, []);
|
|
5007
|
-
const skipTour =
|
|
4894
|
+
const skipTour = useCallback6(() => {
|
|
5008
4895
|
skipRequestedRef.current = true;
|
|
5009
4896
|
voiceInputResolveRef.current?.("");
|
|
5010
4897
|
voiceInputResolveRef.current = null;
|
|
@@ -5014,19 +4901,19 @@ function useTourPlayback({
|
|
|
5014
4901
|
askOrFillRef.current = null;
|
|
5015
4902
|
stopTour();
|
|
5016
4903
|
}, [stopTour]);
|
|
5017
|
-
const pauseTour =
|
|
4904
|
+
const pauseTour = useCallback6(() => {
|
|
5018
4905
|
if (isSocketWritable(socketRef.current) && isActiveRef.current) {
|
|
5019
4906
|
emitSocketEvent(socketRef.current, "tour:pause");
|
|
5020
4907
|
setPlaybackState("paused");
|
|
5021
4908
|
}
|
|
5022
4909
|
}, []);
|
|
5023
|
-
const resumeTour =
|
|
4910
|
+
const resumeTour = useCallback6(() => {
|
|
5024
4911
|
if (isSocketWritable(socketRef.current) && isActiveRef.current) {
|
|
5025
4912
|
emitSocketEvent(socketRef.current, "tour:resume");
|
|
5026
4913
|
setPlaybackState("executing");
|
|
5027
4914
|
}
|
|
5028
4915
|
}, []);
|
|
5029
|
-
const repeatStep =
|
|
4916
|
+
const repeatStep = useCallback6(() => {
|
|
5030
4917
|
const tour = tourRef.current;
|
|
5031
4918
|
const step = tour?.steps[stepIndexRef.current];
|
|
5032
4919
|
if (step) {
|
|
@@ -5034,7 +4921,7 @@ function useTourPlayback({
|
|
|
5034
4921
|
voice.speak(text, tour?.voice.ttsVoice);
|
|
5035
4922
|
}
|
|
5036
4923
|
}, [voice]);
|
|
5037
|
-
const handleVoiceInput =
|
|
4924
|
+
const handleVoiceInput = useCallback6((transcript) => {
|
|
5038
4925
|
const text = transcript.trim();
|
|
5039
4926
|
emitSdkDebugLog("[TourAgent] Voice input received", {
|
|
5040
4927
|
textLength: text.length
|
|
@@ -5062,7 +4949,7 @@ function useTourPlayback({
|
|
|
5062
4949
|
pendingInputBufRef.current = text;
|
|
5063
4950
|
}
|
|
5064
4951
|
}, [interruptExecution]);
|
|
5065
|
-
const handleTextInput =
|
|
4952
|
+
const handleTextInput = useCallback6((text) => {
|
|
5066
4953
|
if (voiceInputResolveRef.current) {
|
|
5067
4954
|
const resolve = voiceInputResolveRef.current;
|
|
5068
4955
|
voiceInputResolveRef.current = null;
|
|
@@ -5075,7 +4962,7 @@ function useTourPlayback({
|
|
|
5075
4962
|
handleVoiceInput(text.trim());
|
|
5076
4963
|
}
|
|
5077
4964
|
}, [handleVoiceInput]);
|
|
5078
|
-
|
|
4965
|
+
useEffect10(() => {
|
|
5079
4966
|
return () => {
|
|
5080
4967
|
removeHighlight();
|
|
5081
4968
|
removeCaption();
|
|
@@ -5156,14 +5043,14 @@ function useExperiencePlaybackController({
|
|
|
5156
5043
|
initialExperienceType = "tour"
|
|
5157
5044
|
}) {
|
|
5158
5045
|
const locationSignature = typeof window === "undefined" ? "" : getLocationSignature(window.location);
|
|
5159
|
-
const [activeExperienceType, setActiveExperienceType] =
|
|
5160
|
-
const [startingExperienceType, setStartingExperienceType] =
|
|
5161
|
-
const [pendingPrompt, setPendingPrompt] =
|
|
5162
|
-
const pendingPromptRef =
|
|
5163
|
-
const queuedStartRef =
|
|
5164
|
-
const bufferedVoiceInputsRef =
|
|
5165
|
-
const previewDiscoveryInFlightRef =
|
|
5166
|
-
const previewSessionRef =
|
|
5046
|
+
const [activeExperienceType, setActiveExperienceType] = useState7(initialExperienceType);
|
|
5047
|
+
const [startingExperienceType, setStartingExperienceType] = useState7(null);
|
|
5048
|
+
const [pendingPrompt, setPendingPrompt] = useState7(null);
|
|
5049
|
+
const pendingPromptRef = useRef8(null);
|
|
5050
|
+
const queuedStartRef = useRef8(null);
|
|
5051
|
+
const bufferedVoiceInputsRef = useRef8([]);
|
|
5052
|
+
const previewDiscoveryInFlightRef = useRef8(false);
|
|
5053
|
+
const previewSessionRef = useRef8(readPreviewSessionSuppression());
|
|
5167
5054
|
pendingPromptRef.current = pendingPrompt;
|
|
5168
5055
|
const playback = useTourPlayback({
|
|
5169
5056
|
serverUrl,
|
|
@@ -5182,7 +5069,7 @@ function useExperiencePlaybackController({
|
|
|
5182
5069
|
showCaptions,
|
|
5183
5070
|
enableAutoDiscovery: false
|
|
5184
5071
|
});
|
|
5185
|
-
const queueExperienceStart =
|
|
5072
|
+
const queueExperienceStart = useCallback7((tour, experienceType, options) => {
|
|
5186
5073
|
setPendingPrompt(null);
|
|
5187
5074
|
pendingPromptRef.current = null;
|
|
5188
5075
|
setStartingExperienceType(experienceType);
|
|
@@ -5197,17 +5084,17 @@ function useExperiencePlaybackController({
|
|
|
5197
5084
|
}
|
|
5198
5085
|
playback.startTour(tour, options);
|
|
5199
5086
|
}, [activeExperienceType, playback]);
|
|
5200
|
-
const startExperience =
|
|
5087
|
+
const startExperience = useCallback7((tour, experienceType, options) => {
|
|
5201
5088
|
const resolvedExperienceType = experienceType ?? tour.type ?? activeExperienceType;
|
|
5202
5089
|
queueExperienceStart(tour, resolvedExperienceType, options);
|
|
5203
5090
|
}, [activeExperienceType, queueExperienceStart]);
|
|
5204
|
-
const acceptPendingPrompt =
|
|
5091
|
+
const acceptPendingPrompt = useCallback7((experienceType) => {
|
|
5205
5092
|
const prompt = pendingPromptRef.current;
|
|
5206
5093
|
if (!prompt) return;
|
|
5207
5094
|
if (experienceType && prompt.experienceType !== experienceType) return;
|
|
5208
5095
|
queueExperienceStart(prompt.tour, prompt.experienceType, prompt.options);
|
|
5209
5096
|
}, [queueExperienceStart]);
|
|
5210
|
-
const dismissPendingPrompt =
|
|
5097
|
+
const dismissPendingPrompt = useCallback7((experienceType) => {
|
|
5211
5098
|
const prompt = pendingPromptRef.current;
|
|
5212
5099
|
if (!prompt) return;
|
|
5213
5100
|
if (experienceType && prompt.experienceType !== experienceType) return;
|
|
@@ -5230,7 +5117,7 @@ function useExperiencePlaybackController({
|
|
|
5230
5117
|
websiteId
|
|
5231
5118
|
);
|
|
5232
5119
|
}, [serverUrl, toursApiBase, userProfile, websiteId]);
|
|
5233
|
-
const handleVoiceInput =
|
|
5120
|
+
const handleVoiceInput = useCallback7((transcript, experienceType) => {
|
|
5234
5121
|
const trimmed = transcript.trim();
|
|
5235
5122
|
if (!trimmed) return;
|
|
5236
5123
|
const targetExperienceType = experienceType ?? activeExperienceType;
|
|
@@ -5250,12 +5137,12 @@ function useExperiencePlaybackController({
|
|
|
5250
5137
|
}
|
|
5251
5138
|
playback.handleVoiceInput(trimmed);
|
|
5252
5139
|
}, [activeExperienceType, playback]);
|
|
5253
|
-
|
|
5140
|
+
useEffect11(() => {
|
|
5254
5141
|
if (playback.isActive && startingExperienceType !== null) {
|
|
5255
5142
|
setStartingExperienceType(null);
|
|
5256
5143
|
}
|
|
5257
5144
|
}, [playback.isActive, startingExperienceType]);
|
|
5258
|
-
|
|
5145
|
+
useEffect11(() => {
|
|
5259
5146
|
const queuedStart = queuedStartRef.current;
|
|
5260
5147
|
if (!queuedStart) return;
|
|
5261
5148
|
if (playback.isActive) return;
|
|
@@ -5263,14 +5150,14 @@ function useExperiencePlaybackController({
|
|
|
5263
5150
|
queuedStartRef.current = null;
|
|
5264
5151
|
playback.startTour(queuedStart.tour, queuedStart.options);
|
|
5265
5152
|
}, [activeExperienceType, playback]);
|
|
5266
|
-
|
|
5153
|
+
useEffect11(() => {
|
|
5267
5154
|
if (!playback.isActive) return;
|
|
5268
5155
|
const readyInputs = bufferedVoiceInputsRef.current.filter((item) => item.experienceType === activeExperienceType);
|
|
5269
5156
|
if (readyInputs.length === 0) return;
|
|
5270
5157
|
bufferedVoiceInputsRef.current = bufferedVoiceInputsRef.current.filter((item) => item.experienceType !== activeExperienceType);
|
|
5271
5158
|
readyInputs.forEach((item) => playback.handleVoiceInput(item.transcript));
|
|
5272
5159
|
}, [activeExperienceType, playback]);
|
|
5273
|
-
|
|
5160
|
+
useEffect11(() => {
|
|
5274
5161
|
if (!shouldDiscoverDraftPreview({
|
|
5275
5162
|
disabled,
|
|
5276
5163
|
hasPendingPrompt: Boolean(pendingPromptRef.current),
|
|
@@ -5352,7 +5239,7 @@ function useExperiencePlaybackController({
|
|
|
5352
5239
|
previewDiscoveryInFlightRef.current = false;
|
|
5353
5240
|
};
|
|
5354
5241
|
}, [disabled, playback.isActive, queueExperienceStart, serverUrl, startingExperienceType, toursApiBase, websiteId, locationSignature]);
|
|
5355
|
-
|
|
5242
|
+
useEffect11(() => {
|
|
5356
5243
|
if (!shouldDiscoverEligibleTours({
|
|
5357
5244
|
disabled,
|
|
5358
5245
|
hasPendingPrompt: Boolean(pendingPromptRef.current),
|
|
@@ -5416,7 +5303,7 @@ function useExperiencePlaybackController({
|
|
|
5416
5303
|
}
|
|
5417
5304
|
|
|
5418
5305
|
// src/hooks/useVoice.ts
|
|
5419
|
-
import { useState as
|
|
5306
|
+
import { useState as useState8, useRef as useRef9, useCallback as useCallback8, useEffect as useEffect12 } from "react";
|
|
5420
5307
|
|
|
5421
5308
|
// src/utils/webrtc-aec.ts
|
|
5422
5309
|
function isWebRtcAecSupported() {
|
|
@@ -5785,36 +5672,36 @@ function isFatalSpeechError(error) {
|
|
|
5785
5672
|
return error === "not-allowed" || error === "service-not-allowed" || error === "audio-capture";
|
|
5786
5673
|
}
|
|
5787
5674
|
function useVoice(serverUrl) {
|
|
5788
|
-
const [isSpeaking, setIsSpeaking] =
|
|
5789
|
-
const [isListening, setIsListening] =
|
|
5790
|
-
const [isMuted, setIsMuted] =
|
|
5791
|
-
const audioRef =
|
|
5792
|
-
const audioBlobUrlRef =
|
|
5793
|
-
const speakResolveRef =
|
|
5794
|
-
const recognitionRef =
|
|
5795
|
-
const isMutedRef =
|
|
5796
|
-
const sttCallbacksRef =
|
|
5797
|
-
const stripIndicesRef =
|
|
5798
|
-
const accumulatedRef =
|
|
5799
|
-
const interimDebounceRef =
|
|
5800
|
-
const lastInterimRef =
|
|
5801
|
-
const lastDeliveredRef =
|
|
5802
|
-
const isSpeakingRef =
|
|
5803
|
-
const speechEndTimeRef =
|
|
5804
|
-
const recentTtsRef =
|
|
5805
|
-
const prefetchedSpeechRef =
|
|
5806
|
-
const nearEndTimeoutRef =
|
|
5807
|
-
const queuePromiseRef =
|
|
5808
|
-
const queueSeqRef =
|
|
5809
|
-
const lastStopSeqRef =
|
|
5810
|
-
const listeningSessionIdRef =
|
|
5811
|
-
const listeningStartedAtRef =
|
|
5812
|
-
const loopbackRef =
|
|
5813
|
-
const aecActiveRef =
|
|
5814
|
-
const mediaRecorderRef =
|
|
5815
|
-
const micStreamRef =
|
|
5816
|
-
const sttSocketRef =
|
|
5817
|
-
const stopLiveSttTransport =
|
|
5675
|
+
const [isSpeaking, setIsSpeaking] = useState8(false);
|
|
5676
|
+
const [isListening, setIsListening] = useState8(false);
|
|
5677
|
+
const [isMuted, setIsMuted] = useState8(false);
|
|
5678
|
+
const audioRef = useRef9(null);
|
|
5679
|
+
const audioBlobUrlRef = useRef9(null);
|
|
5680
|
+
const speakResolveRef = useRef9(null);
|
|
5681
|
+
const recognitionRef = useRef9(null);
|
|
5682
|
+
const isMutedRef = useRef9(false);
|
|
5683
|
+
const sttCallbacksRef = useRef9(null);
|
|
5684
|
+
const stripIndicesRef = useRef9({});
|
|
5685
|
+
const accumulatedRef = useRef9([]);
|
|
5686
|
+
const interimDebounceRef = useRef9(null);
|
|
5687
|
+
const lastInterimRef = useRef9("");
|
|
5688
|
+
const lastDeliveredRef = useRef9("");
|
|
5689
|
+
const isSpeakingRef = useRef9(false);
|
|
5690
|
+
const speechEndTimeRef = useRef9(0);
|
|
5691
|
+
const recentTtsRef = useRef9([]);
|
|
5692
|
+
const prefetchedSpeechRef = useRef9(/* @__PURE__ */ new Map());
|
|
5693
|
+
const nearEndTimeoutRef = useRef9(null);
|
|
5694
|
+
const queuePromiseRef = useRef9(Promise.resolve());
|
|
5695
|
+
const queueSeqRef = useRef9(0);
|
|
5696
|
+
const lastStopSeqRef = useRef9(0);
|
|
5697
|
+
const listeningSessionIdRef = useRef9(null);
|
|
5698
|
+
const listeningStartedAtRef = useRef9(0);
|
|
5699
|
+
const loopbackRef = useRef9(null);
|
|
5700
|
+
const aecActiveRef = useRef9(false);
|
|
5701
|
+
const mediaRecorderRef = useRef9(null);
|
|
5702
|
+
const micStreamRef = useRef9(null);
|
|
5703
|
+
const sttSocketRef = useRef9(null);
|
|
5704
|
+
const stopLiveSttTransport = useCallback8(() => {
|
|
5818
5705
|
const socket = sttSocketRef.current;
|
|
5819
5706
|
sttSocketRef.current = null;
|
|
5820
5707
|
if (socket) {
|
|
@@ -5843,16 +5730,16 @@ function useVoice(serverUrl) {
|
|
|
5843
5730
|
}
|
|
5844
5731
|
mediaRecorderRef.current = null;
|
|
5845
5732
|
}, []);
|
|
5846
|
-
|
|
5733
|
+
useEffect12(() => {
|
|
5847
5734
|
isMutedRef.current = isMuted;
|
|
5848
5735
|
}, [isMuted]);
|
|
5849
|
-
|
|
5736
|
+
useEffect12(() => {
|
|
5850
5737
|
isSpeakingRef.current = isSpeaking;
|
|
5851
5738
|
}, [isSpeaking]);
|
|
5852
5739
|
const webSpeechSupported = typeof window !== "undefined" && !!(window.SpeechRecognition || window.webkitSpeechRecognition);
|
|
5853
5740
|
const mediaCaptureSupported = typeof window !== "undefined" && typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia && typeof MediaRecorder !== "undefined";
|
|
5854
5741
|
const sttSupported = typeof window !== "undefined" && (mediaCaptureSupported || webSpeechSupported);
|
|
5855
|
-
const stopSpeaking =
|
|
5742
|
+
const stopSpeaking = useCallback8(() => {
|
|
5856
5743
|
emitVoiceDebug("tts_stop", {
|
|
5857
5744
|
queueSeq: queueSeqRef.current,
|
|
5858
5745
|
wasSpeaking: isSpeakingRef.current
|
|
@@ -5879,8 +5766,8 @@ function useVoice(serverUrl) {
|
|
|
5879
5766
|
speakResolveRef.current?.();
|
|
5880
5767
|
speakResolveRef.current = null;
|
|
5881
5768
|
}, []);
|
|
5882
|
-
const getSpeechCacheKey =
|
|
5883
|
-
const loadAudioDurationMs =
|
|
5769
|
+
const getSpeechCacheKey = useCallback8((text, voiceId) => `${voiceId}::${text}`, []);
|
|
5770
|
+
const loadAudioDurationMs = useCallback8(async (url, fallbackText) => {
|
|
5884
5771
|
try {
|
|
5885
5772
|
const probe = new Audio();
|
|
5886
5773
|
probe.preload = "metadata";
|
|
@@ -5902,7 +5789,7 @@ function useVoice(serverUrl) {
|
|
|
5902
5789
|
return estimateSpeechDurationMs(fallbackText);
|
|
5903
5790
|
}
|
|
5904
5791
|
}, []);
|
|
5905
|
-
const fetchSpeechClip =
|
|
5792
|
+
const fetchSpeechClip = useCallback8(async (text, voiceId) => {
|
|
5906
5793
|
const baseUrl = serverUrl.replace(/\/$/, "");
|
|
5907
5794
|
const debugEnabled = isVoiceDebugEnabled();
|
|
5908
5795
|
const requestId = createVoiceDebugId("tts");
|
|
@@ -5964,7 +5851,7 @@ function useVoice(serverUrl) {
|
|
|
5964
5851
|
});
|
|
5965
5852
|
return { url, durationMs, requestId, buffered: true };
|
|
5966
5853
|
}, [loadAudioDurationMs, serverUrl]);
|
|
5967
|
-
const createStreamingSpeechClipForPlayback =
|
|
5854
|
+
const createStreamingSpeechClipForPlayback = useCallback8((text, voiceId) => {
|
|
5968
5855
|
const requestId = createVoiceDebugId("tts");
|
|
5969
5856
|
const clip = createStreamingSpeechClip(serverUrl, text, voiceId, {
|
|
5970
5857
|
requestId,
|
|
@@ -5979,7 +5866,7 @@ function useVoice(serverUrl) {
|
|
|
5979
5866
|
});
|
|
5980
5867
|
return clip;
|
|
5981
5868
|
}, [serverUrl]);
|
|
5982
|
-
const prefetchSpeech =
|
|
5869
|
+
const prefetchSpeech = useCallback8(async (text, voiceId = DEFAULT_VOICE_ID) => {
|
|
5983
5870
|
if (isMutedRef.current || !text.trim()) return;
|
|
5984
5871
|
const key = getSpeechCacheKey(text, voiceId);
|
|
5985
5872
|
if (!prefetchedSpeechRef.current.has(key)) {
|
|
@@ -5993,7 +5880,7 @@ function useVoice(serverUrl) {
|
|
|
5993
5880
|
}
|
|
5994
5881
|
await prefetchedSpeechRef.current.get(key);
|
|
5995
5882
|
}, [fetchSpeechClip, getSpeechCacheKey]);
|
|
5996
|
-
const playClipDirect =
|
|
5883
|
+
const playClipDirect = useCallback8(async (clip, timeoutMs) => {
|
|
5997
5884
|
const { url } = clip;
|
|
5998
5885
|
const audio = new Audio();
|
|
5999
5886
|
audio.crossOrigin = "anonymous";
|
|
@@ -6089,7 +5976,7 @@ function useVoice(serverUrl) {
|
|
|
6089
5976
|
});
|
|
6090
5977
|
});
|
|
6091
5978
|
}, []);
|
|
6092
|
-
const speak =
|
|
5979
|
+
const speak = useCallback8(async (text, voiceId = DEFAULT_VOICE_ID, options = {}) => {
|
|
6093
5980
|
if (isMutedRef.current) return;
|
|
6094
5981
|
const mySeq = ++queueSeqRef.current;
|
|
6095
5982
|
const interrupt = options.interrupt === true;
|
|
@@ -6181,7 +6068,7 @@ function useVoice(serverUrl) {
|
|
|
6181
6068
|
}
|
|
6182
6069
|
return taskPromise;
|
|
6183
6070
|
}, [createStreamingSpeechClipForPlayback, fetchSpeechClip, getSpeechCacheKey, playClipDirect, stopSpeaking]);
|
|
6184
|
-
const stopListening =
|
|
6071
|
+
const stopListening = useCallback8(() => {
|
|
6185
6072
|
emitVoiceDebug("stt_stop_listening", {
|
|
6186
6073
|
listeningSessionId: listeningSessionIdRef.current,
|
|
6187
6074
|
listeningMs: listeningStartedAtRef.current ? Math.round(performance.now() - listeningStartedAtRef.current) : null
|
|
@@ -6213,7 +6100,7 @@ function useVoice(serverUrl) {
|
|
|
6213
6100
|
accumulatedRef.current = [];
|
|
6214
6101
|
setIsListening(false);
|
|
6215
6102
|
}, [stopLiveSttTransport]);
|
|
6216
|
-
const startListening =
|
|
6103
|
+
const startListening = useCallback8((onResult, onInterruption, onError, options = {}) => {
|
|
6217
6104
|
stopListening();
|
|
6218
6105
|
listeningSessionIdRef.current = createVoiceDebugId("stt");
|
|
6219
6106
|
listeningStartedAtRef.current = performance.now();
|
|
@@ -6497,7 +6384,7 @@ function useVoice(serverUrl) {
|
|
|
6497
6384
|
}
|
|
6498
6385
|
fallbackToWebSpeech();
|
|
6499
6386
|
}, [serverUrl, stopListening, stopSpeaking, stopLiveSttTransport]);
|
|
6500
|
-
const toggleMute =
|
|
6387
|
+
const toggleMute = useCallback8(() => {
|
|
6501
6388
|
setIsMuted((prev) => {
|
|
6502
6389
|
const next = !prev;
|
|
6503
6390
|
if (next) {
|
|
@@ -6509,7 +6396,7 @@ function useVoice(serverUrl) {
|
|
|
6509
6396
|
return next;
|
|
6510
6397
|
});
|
|
6511
6398
|
}, []);
|
|
6512
|
-
|
|
6399
|
+
useEffect12(() => {
|
|
6513
6400
|
return () => {
|
|
6514
6401
|
stopSpeaking();
|
|
6515
6402
|
stopListening();
|
|
@@ -6538,18 +6425,18 @@ function useVoice(serverUrl) {
|
|
|
6538
6425
|
}
|
|
6539
6426
|
|
|
6540
6427
|
// src/hooks/useAudioLevel.ts
|
|
6541
|
-
import { useState as
|
|
6428
|
+
import { useState as useState9, useEffect as useEffect13, useRef as useRef10 } from "react";
|
|
6542
6429
|
var BAR_COUNT = 9;
|
|
6543
6430
|
var SENSITIVITY = 2.8;
|
|
6544
6431
|
var FLOOR = 0.08;
|
|
6545
6432
|
function useAudioLevel(active) {
|
|
6546
|
-
const [levels, setLevels] =
|
|
6547
|
-
const streamRef =
|
|
6548
|
-
const ctxRef =
|
|
6549
|
-
const analyserRef =
|
|
6550
|
-
const rafRef =
|
|
6551
|
-
const timeDataRef =
|
|
6552
|
-
|
|
6433
|
+
const [levels, setLevels] = useState9(() => Array(BAR_COUNT).fill(FLOOR));
|
|
6434
|
+
const streamRef = useRef10(null);
|
|
6435
|
+
const ctxRef = useRef10(null);
|
|
6436
|
+
const analyserRef = useRef10(null);
|
|
6437
|
+
const rafRef = useRef10(0);
|
|
6438
|
+
const timeDataRef = useRef10(null);
|
|
6439
|
+
useEffect13(() => {
|
|
6553
6440
|
if (!active || typeof window === "undefined") {
|
|
6554
6441
|
setLevels(Array(BAR_COUNT).fill(FLOOR));
|
|
6555
6442
|
return;
|
|
@@ -6621,7 +6508,7 @@ function useAudioLevel(active) {
|
|
|
6621
6508
|
}
|
|
6622
6509
|
|
|
6623
6510
|
// src/hooks/useRecordingMode.ts
|
|
6624
|
-
import { useState as
|
|
6511
|
+
import { useState as useState10, useRef as useRef11, useCallback as useCallback9, useEffect as useEffect14 } from "react";
|
|
6625
6512
|
|
|
6626
6513
|
// src/utils/tourStepTypes.ts
|
|
6627
6514
|
function isAskDrivenInputStepType(stepType) {
|
|
@@ -6902,46 +6789,46 @@ function useRecordingMode({
|
|
|
6902
6789
|
onPreview,
|
|
6903
6790
|
experienceType = "tour"
|
|
6904
6791
|
}) {
|
|
6905
|
-
const restoredSessionRef =
|
|
6792
|
+
const restoredSessionRef = useRef11(void 0);
|
|
6906
6793
|
if (restoredSessionRef.current === void 0) {
|
|
6907
6794
|
restoredSessionRef.current = readPersistedRecordingSession();
|
|
6908
6795
|
}
|
|
6909
6796
|
const restoredSession = restoredSessionRef.current;
|
|
6910
6797
|
const restoredPhase = restoredSession ? "active" : "idle";
|
|
6911
|
-
const [phase, setPhase] =
|
|
6912
|
-
const [steps, setSteps] =
|
|
6913
|
-
const [selectedElement, setSelectedElement] =
|
|
6914
|
-
const [selectedStepType, setSelectedStepType] =
|
|
6915
|
-
const [pendingNarration, setPendingNarration] =
|
|
6916
|
-
const [polishedNarration, setPolishedNarration] =
|
|
6917
|
-
const [captureEvents, setCaptureEvents] =
|
|
6918
|
-
const [capturedTranscript, setCapturedTranscript] =
|
|
6919
|
-
const [isVoiceCaptureActive, setIsVoiceCaptureActive] =
|
|
6920
|
-
const stepsRef =
|
|
6798
|
+
const [phase, setPhase] = useState10(restoredPhase);
|
|
6799
|
+
const [steps, setSteps] = useState10(() => restoredSession?.steps ?? []);
|
|
6800
|
+
const [selectedElement, setSelectedElement] = useState10(null);
|
|
6801
|
+
const [selectedStepType, setSelectedStepType] = useState10(() => restoredSession?.selectedStepType ?? "ask_or_fill");
|
|
6802
|
+
const [pendingNarration, setPendingNarration] = useState10("");
|
|
6803
|
+
const [polishedNarration, setPolishedNarration] = useState10("");
|
|
6804
|
+
const [captureEvents, setCaptureEvents] = useState10(() => restoredSession?.captureEvents ?? []);
|
|
6805
|
+
const [capturedTranscript, setCapturedTranscript] = useState10(() => restoredSession?.capturedTranscript ?? "");
|
|
6806
|
+
const [isVoiceCaptureActive, setIsVoiceCaptureActive] = useState10(false);
|
|
6807
|
+
const stepsRef = useRef11([]);
|
|
6921
6808
|
stepsRef.current = steps;
|
|
6922
|
-
const captureEventsRef =
|
|
6923
|
-
const capturedTranscriptRef =
|
|
6809
|
+
const captureEventsRef = useRef11([]);
|
|
6810
|
+
const capturedTranscriptRef = useRef11(capturedTranscript);
|
|
6924
6811
|
capturedTranscriptRef.current = capturedTranscript;
|
|
6925
|
-
const phaseRef =
|
|
6812
|
+
const phaseRef = useRef11(phase);
|
|
6926
6813
|
phaseRef.current = phase;
|
|
6927
|
-
const safeSpeak =
|
|
6814
|
+
const safeSpeak = useCallback9((text) => {
|
|
6928
6815
|
void voice.speak(text).catch((err) => {
|
|
6929
6816
|
console.warn("[Recording] Voice playback unavailable:", err);
|
|
6930
6817
|
});
|
|
6931
6818
|
}, [voice]);
|
|
6932
6819
|
captureEventsRef.current = captureEvents;
|
|
6933
|
-
const pendingClicksRef =
|
|
6934
|
-
const shouldKeepVoiceCaptureRef =
|
|
6935
|
-
const resumeVoiceAfterNarrationRef =
|
|
6936
|
-
const lastAutoNoteRef =
|
|
6937
|
-
const lastHoverKeyRef =
|
|
6938
|
-
const lastHoverAtRef =
|
|
6939
|
-
const selectedStepTypeRef =
|
|
6820
|
+
const pendingClicksRef = useRef11(restoredSession?.pendingClicks ?? []);
|
|
6821
|
+
const shouldKeepVoiceCaptureRef = useRef11(restoredSession?.voiceCaptureEnabled === true);
|
|
6822
|
+
const resumeVoiceAfterNarrationRef = useRef11(false);
|
|
6823
|
+
const lastAutoNoteRef = useRef11("");
|
|
6824
|
+
const lastHoverKeyRef = useRef11("");
|
|
6825
|
+
const lastHoverAtRef = useRef11(0);
|
|
6826
|
+
const selectedStepTypeRef = useRef11("ask_or_fill");
|
|
6940
6827
|
selectedStepTypeRef.current = selectedStepType;
|
|
6941
6828
|
const isRecording = phase !== "idle";
|
|
6942
6829
|
const stepCount = steps.length;
|
|
6943
6830
|
const captureEventCount = captureEvents.length;
|
|
6944
|
-
const persistSnapshot =
|
|
6831
|
+
const persistSnapshot = useCallback9((overrides) => {
|
|
6945
6832
|
const nextPhase = overrides?.phase ?? phaseRef.current;
|
|
6946
6833
|
const nextSteps = overrides?.steps ?? stepsRef.current;
|
|
6947
6834
|
const nextCaptureEvents = overrides?.captureEvents ?? captureEventsRef.current;
|
|
@@ -6964,7 +6851,7 @@ function useRecordingMode({
|
|
|
6964
6851
|
voiceCaptureEnabled: nextVoiceCaptureEnabled
|
|
6965
6852
|
});
|
|
6966
6853
|
}, [experienceType]);
|
|
6967
|
-
const appendCaptureEvent =
|
|
6854
|
+
const appendCaptureEvent = useCallback9((event) => {
|
|
6968
6855
|
const nextEvent = {
|
|
6969
6856
|
id: event.id || newCaptureId(event.type),
|
|
6970
6857
|
order: captureEventsRef.current.length,
|
|
@@ -6976,7 +6863,7 @@ function useRecordingMode({
|
|
|
6976
6863
|
setCaptureEvents(nextCaptureEvents);
|
|
6977
6864
|
persistSnapshot({ captureEvents: nextCaptureEvents });
|
|
6978
6865
|
}, [persistSnapshot]);
|
|
6979
|
-
const updateCaptureEvent =
|
|
6866
|
+
const updateCaptureEvent = useCallback9((id, metadataPatch) => {
|
|
6980
6867
|
const nextCaptureEvents = captureEventsRef.current.map((event) => {
|
|
6981
6868
|
if (event.id === id) {
|
|
6982
6869
|
return {
|
|
@@ -6993,7 +6880,7 @@ function useRecordingMode({
|
|
|
6993
6880
|
setCaptureEvents(nextCaptureEvents);
|
|
6994
6881
|
persistSnapshot({ captureEvents: nextCaptureEvents });
|
|
6995
6882
|
}, [persistSnapshot]);
|
|
6996
|
-
const appendVoiceNote =
|
|
6883
|
+
const appendVoiceNote = useCallback9((transcript) => {
|
|
6997
6884
|
const text = transcript.trim();
|
|
6998
6885
|
if (!text) return;
|
|
6999
6886
|
if (lastAutoNoteRef.current === text) return;
|
|
@@ -7054,11 +6941,11 @@ function useRecordingMode({
|
|
|
7054
6941
|
}
|
|
7055
6942
|
});
|
|
7056
6943
|
}, [appendCaptureEvent, persistSnapshot]);
|
|
7057
|
-
const stopBackgroundVoiceCapture =
|
|
6944
|
+
const stopBackgroundVoiceCapture = useCallback9(() => {
|
|
7058
6945
|
voice.stopListening();
|
|
7059
6946
|
setIsVoiceCaptureActive(false);
|
|
7060
6947
|
}, [voice]);
|
|
7061
|
-
const startBackgroundVoiceCapture =
|
|
6948
|
+
const startBackgroundVoiceCapture = useCallback9(() => {
|
|
7062
6949
|
if (!shouldKeepVoiceCaptureRef.current) return;
|
|
7063
6950
|
if (!isRecording) return;
|
|
7064
6951
|
if (phase === "narrating" || phase === "reviewing") return;
|
|
@@ -7075,7 +6962,7 @@ function useRecordingMode({
|
|
|
7075
6962
|
);
|
|
7076
6963
|
setIsVoiceCaptureActive(true);
|
|
7077
6964
|
}, [appendVoiceNote, isRecording, phase, voice]);
|
|
7078
|
-
const startRecording =
|
|
6965
|
+
const startRecording = useCallback9(() => {
|
|
7079
6966
|
const nextPhase = "active";
|
|
7080
6967
|
const nextCaptureEvents = [{
|
|
7081
6968
|
id: newCaptureId("session_start"),
|
|
@@ -7107,34 +6994,34 @@ function useRecordingMode({
|
|
|
7107
6994
|
voiceCaptureEnabled: false
|
|
7108
6995
|
});
|
|
7109
6996
|
}, [persistSnapshot]);
|
|
7110
|
-
const markStep =
|
|
6997
|
+
const markStep = useCallback9(() => {
|
|
7111
6998
|
if (phase !== "active") return;
|
|
7112
6999
|
setPhase("selecting");
|
|
7113
7000
|
}, [phase]);
|
|
7114
|
-
const selectElement =
|
|
7001
|
+
const selectElement = useCallback9((recorded) => {
|
|
7115
7002
|
setSelectedElement(recorded);
|
|
7116
7003
|
setPhase("configuring");
|
|
7117
7004
|
const isFormField = ["INPUT", "SELECT", "TEXTAREA"].includes(recorded.el.tagName);
|
|
7118
7005
|
setSelectedStepType(isFormField ? "ask_or_fill" : "narrate");
|
|
7119
7006
|
}, []);
|
|
7120
|
-
const selectPageLevel =
|
|
7007
|
+
const selectPageLevel = useCallback9(() => {
|
|
7121
7008
|
setSelectedElement(null);
|
|
7122
7009
|
setSelectedStepType("narrate");
|
|
7123
7010
|
setPhase("configuring");
|
|
7124
7011
|
}, []);
|
|
7125
|
-
const cancelSelection =
|
|
7012
|
+
const cancelSelection = useCallback9(() => {
|
|
7126
7013
|
setSelectedElement(null);
|
|
7127
7014
|
setPhase("active");
|
|
7128
7015
|
}, []);
|
|
7129
|
-
const setStepType =
|
|
7016
|
+
const setStepType = useCallback9((type) => {
|
|
7130
7017
|
setSelectedStepType(type);
|
|
7131
7018
|
}, []);
|
|
7132
|
-
const runPolishAndReview =
|
|
7019
|
+
const runPolishAndReview = useCallback9(async (transcript) => {
|
|
7133
7020
|
setPolishedNarration(transcript);
|
|
7134
7021
|
setPhase("reviewing");
|
|
7135
7022
|
}, []);
|
|
7136
|
-
const pendingNarrationRef =
|
|
7137
|
-
const startNarration =
|
|
7023
|
+
const pendingNarrationRef = useRef11("");
|
|
7024
|
+
const startNarration = useCallback9(() => {
|
|
7138
7025
|
resumeVoiceAfterNarrationRef.current = shouldKeepVoiceCaptureRef.current && isVoiceCaptureActive;
|
|
7139
7026
|
if (isVoiceCaptureActive) {
|
|
7140
7027
|
stopBackgroundVoiceCapture();
|
|
@@ -7156,7 +7043,7 @@ function useRecordingMode({
|
|
|
7156
7043
|
{ continuous: true }
|
|
7157
7044
|
);
|
|
7158
7045
|
}, [isVoiceCaptureActive, stopBackgroundVoiceCapture, voice]);
|
|
7159
|
-
const finishNarration =
|
|
7046
|
+
const finishNarration = useCallback9(async () => {
|
|
7160
7047
|
if (phase !== "narrating") return;
|
|
7161
7048
|
voice.stopListening();
|
|
7162
7049
|
const text = pendingNarrationRef.current;
|
|
@@ -7171,13 +7058,13 @@ function useRecordingMode({
|
|
|
7171
7058
|
}
|
|
7172
7059
|
}
|
|
7173
7060
|
}, [phase, runPolishAndReview, startBackgroundVoiceCapture, voice]);
|
|
7174
|
-
const submitTextNarration =
|
|
7061
|
+
const submitTextNarration = useCallback9(async (text) => {
|
|
7175
7062
|
if (phase !== "narrating" || !text.trim()) return;
|
|
7176
7063
|
voice.stopListening();
|
|
7177
7064
|
setPendingNarration(text);
|
|
7178
7065
|
await runPolishAndReview(text);
|
|
7179
7066
|
}, [phase, voice, runPolishAndReview]);
|
|
7180
|
-
const approveNarration =
|
|
7067
|
+
const approveNarration = useCallback9(() => {
|
|
7181
7068
|
if (phase !== "reviewing") return;
|
|
7182
7069
|
const el = selectedElement;
|
|
7183
7070
|
const narration = polishedNarration || pendingNarration;
|
|
@@ -7267,17 +7154,17 @@ function useRecordingMode({
|
|
|
7267
7154
|
}, 0);
|
|
7268
7155
|
}
|
|
7269
7156
|
}, [appendCaptureEvent, phase, polishedNarration, pendingNarration, persistSnapshot, selectedElement, selectedStepType, startBackgroundVoiceCapture, voice]);
|
|
7270
|
-
const redoNarration =
|
|
7157
|
+
const redoNarration = useCallback9(() => {
|
|
7271
7158
|
if (phase !== "reviewing") return;
|
|
7272
7159
|
setPendingNarration("");
|
|
7273
7160
|
setPolishedNarration("");
|
|
7274
7161
|
startNarration();
|
|
7275
7162
|
}, [phase, startNarration]);
|
|
7276
|
-
const editNarration =
|
|
7163
|
+
const editNarration = useCallback9((text) => {
|
|
7277
7164
|
setPendingNarration(text);
|
|
7278
7165
|
setPolishedNarration(text);
|
|
7279
7166
|
}, []);
|
|
7280
|
-
const continueRecording =
|
|
7167
|
+
const continueRecording = useCallback9(() => {
|
|
7281
7168
|
phaseRef.current = "active";
|
|
7282
7169
|
setPhase("active");
|
|
7283
7170
|
persistSnapshot({ phase: "active" });
|
|
@@ -7287,7 +7174,7 @@ function useRecordingMode({
|
|
|
7287
7174
|
}, 0);
|
|
7288
7175
|
}
|
|
7289
7176
|
}, [persistSnapshot, startBackgroundVoiceCapture]);
|
|
7290
|
-
const undoLastStep =
|
|
7177
|
+
const undoLastStep = useCallback9(() => {
|
|
7291
7178
|
const previous = stepsRef.current;
|
|
7292
7179
|
const nextSteps = previous.slice(0, -1);
|
|
7293
7180
|
stepsRef.current = nextSteps;
|
|
@@ -7299,7 +7186,7 @@ function useRecordingMode({
|
|
|
7299
7186
|
setPhase("active");
|
|
7300
7187
|
persistSnapshot({ steps: nextSteps, phase: "active" });
|
|
7301
7188
|
}, [persistSnapshot, voice]);
|
|
7302
|
-
const previewSteps =
|
|
7189
|
+
const previewSteps = useCallback9(() => {
|
|
7303
7190
|
if (steps.length === 0) {
|
|
7304
7191
|
safeSpeak("No steps recorded yet.");
|
|
7305
7192
|
return;
|
|
@@ -7307,13 +7194,13 @@ function useRecordingMode({
|
|
|
7307
7194
|
onPreview?.(steps);
|
|
7308
7195
|
safeSpeak(`Previewing ${steps.length} step${steps.length !== 1 ? "s" : ""}.`);
|
|
7309
7196
|
}, [onPreview, safeSpeak, steps]);
|
|
7310
|
-
const prepareToStopRecording =
|
|
7197
|
+
const prepareToStopRecording = useCallback9(() => {
|
|
7311
7198
|
shouldKeepVoiceCaptureRef.current = false;
|
|
7312
7199
|
resumeVoiceAfterNarrationRef.current = false;
|
|
7313
7200
|
stopBackgroundVoiceCapture();
|
|
7314
7201
|
persistSnapshot({ voiceCaptureEnabled: false });
|
|
7315
7202
|
}, [persistSnapshot, stopBackgroundVoiceCapture]);
|
|
7316
|
-
const cancelRecording =
|
|
7203
|
+
const cancelRecording = useCallback9(() => {
|
|
7317
7204
|
shouldKeepVoiceCaptureRef.current = false;
|
|
7318
7205
|
stopBackgroundVoiceCapture();
|
|
7319
7206
|
phaseRef.current = "idle";
|
|
@@ -7331,7 +7218,7 @@ function useRecordingMode({
|
|
|
7331
7218
|
clearPersistedRecordingSession();
|
|
7332
7219
|
safeSpeak("Recording cancelled.");
|
|
7333
7220
|
}, [safeSpeak, stopBackgroundVoiceCapture]);
|
|
7334
|
-
|
|
7221
|
+
useEffect14(() => {
|
|
7335
7222
|
if (!isRecording || typeof document === "undefined") return;
|
|
7336
7223
|
const handleMouseMove = (event) => {
|
|
7337
7224
|
const target = event.target;
|
|
@@ -7441,7 +7328,7 @@ function useRecordingMode({
|
|
|
7441
7328
|
window.clearInterval(routePoll);
|
|
7442
7329
|
};
|
|
7443
7330
|
}, [appendCaptureEvent, isRecording]);
|
|
7444
|
-
|
|
7331
|
+
useEffect14(() => {
|
|
7445
7332
|
if (!isRecording || typeof window === "undefined") return;
|
|
7446
7333
|
const flushRecordingSession = () => {
|
|
7447
7334
|
persistSnapshot();
|
|
@@ -7453,7 +7340,7 @@ function useRecordingMode({
|
|
|
7453
7340
|
window.removeEventListener("beforeunload", flushRecordingSession);
|
|
7454
7341
|
};
|
|
7455
7342
|
}, [isRecording, persistSnapshot]);
|
|
7456
|
-
const stopRecording =
|
|
7343
|
+
const stopRecording = useCallback9(async (tourName, targetUserTypes) => {
|
|
7457
7344
|
phaseRef.current = "finishing";
|
|
7458
7345
|
setPhase("finishing");
|
|
7459
7346
|
shouldKeepVoiceCaptureRef.current = false;
|
|
@@ -7527,7 +7414,7 @@ function useRecordingMode({
|
|
|
7527
7414
|
return null;
|
|
7528
7415
|
}
|
|
7529
7416
|
}, [capturedTranscript, experienceType, persistSnapshot, safeSpeak, serverUrl, stopBackgroundVoiceCapture, steps, toursApiBase, websiteId]);
|
|
7530
|
-
const toggleVoiceCapture =
|
|
7417
|
+
const toggleVoiceCapture = useCallback9(() => {
|
|
7531
7418
|
if (isVoiceCaptureActive || shouldKeepVoiceCaptureRef.current) {
|
|
7532
7419
|
shouldKeepVoiceCaptureRef.current = false;
|
|
7533
7420
|
stopBackgroundVoiceCapture();
|
|
@@ -7538,7 +7425,7 @@ function useRecordingMode({
|
|
|
7538
7425
|
persistSnapshot({ voiceCaptureEnabled: true });
|
|
7539
7426
|
startBackgroundVoiceCapture();
|
|
7540
7427
|
}, [isVoiceCaptureActive, persistSnapshot, startBackgroundVoiceCapture, stopBackgroundVoiceCapture]);
|
|
7541
|
-
|
|
7428
|
+
useEffect14(() => {
|
|
7542
7429
|
if (!isRecording) return;
|
|
7543
7430
|
if (phase === "active" && shouldKeepVoiceCaptureRef.current && !isVoiceCaptureActive) {
|
|
7544
7431
|
startBackgroundVoiceCapture();
|
|
@@ -7547,7 +7434,7 @@ function useRecordingMode({
|
|
|
7547
7434
|
stopBackgroundVoiceCapture();
|
|
7548
7435
|
}
|
|
7549
7436
|
}, [isRecording, isVoiceCaptureActive, phase, startBackgroundVoiceCapture, stopBackgroundVoiceCapture]);
|
|
7550
|
-
const handleVoiceCommand =
|
|
7437
|
+
const handleVoiceCommand = useCallback9((transcript) => {
|
|
7551
7438
|
const lower = transcript.toLowerCase().trim();
|
|
7552
7439
|
if (!isRecording) return;
|
|
7553
7440
|
if (lower.includes("mark step") && phase === "active") {
|
|
@@ -7609,7 +7496,7 @@ function useRecordingMode({
|
|
|
7609
7496
|
}
|
|
7610
7497
|
|
|
7611
7498
|
// src/components/RecordingOverlay.tsx
|
|
7612
|
-
import { useEffect as
|
|
7499
|
+
import { useEffect as useEffect15, useState as useState11, useCallback as useCallback10 } from "react";
|
|
7613
7500
|
import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
7614
7501
|
function buildFingerprint(el) {
|
|
7615
7502
|
const tag = el.tagName.toLowerCase();
|
|
@@ -7637,8 +7524,8 @@ var panelBase = {
|
|
|
7637
7524
|
color: "#18181b"
|
|
7638
7525
|
};
|
|
7639
7526
|
function ActiveHUD({ stepCount, captureEventCount, isVoiceCaptureActive, minimized, onToggleVoiceCapture, onToggleMinimized, onStop, onCancel }) {
|
|
7640
|
-
const [hasActivatedVoiceCapture, setHasActivatedVoiceCapture] =
|
|
7641
|
-
|
|
7527
|
+
const [hasActivatedVoiceCapture, setHasActivatedVoiceCapture] = useState11(isVoiceCaptureActive);
|
|
7528
|
+
useEffect15(() => {
|
|
7642
7529
|
if (isVoiceCaptureActive) {
|
|
7643
7530
|
setHasActivatedVoiceCapture(true);
|
|
7644
7531
|
}
|
|
@@ -7883,8 +7770,8 @@ var STEP_TYPES_ADVANCED = [
|
|
|
7883
7770
|
];
|
|
7884
7771
|
function StepPopover({ element, stepIndex, onConfirm, onCancel }) {
|
|
7885
7772
|
const defaultType = element && ["INPUT", "SELECT", "TEXTAREA"].includes(element.el.tagName) ? "ask_or_fill" : "narrate";
|
|
7886
|
-
const [selected, setSelected] =
|
|
7887
|
-
const [showAdvanced, setShowAdvanced] =
|
|
7773
|
+
const [selected, setSelected] = useState11(defaultType);
|
|
7774
|
+
const [showAdvanced, setShowAdvanced] = useState11(false);
|
|
7888
7775
|
const hasTestId = !!element?.testId;
|
|
7889
7776
|
const allTypes = showAdvanced ? [...STEP_TYPES_PRIMARY, ...STEP_TYPES_ADVANCED] : STEP_TYPES_PRIMARY;
|
|
7890
7777
|
const popoverStyle = {
|
|
@@ -8028,8 +7915,8 @@ function StepPopover({ element, stepIndex, onConfirm, onCancel }) {
|
|
|
8028
7915
|
] });
|
|
8029
7916
|
}
|
|
8030
7917
|
function NarrationPanel({ stepIndex, stepCount, pendingNarration, isListening, onSubmitText, onCancel, onStopRecording }) {
|
|
8031
|
-
const [textInput, setTextInput] =
|
|
8032
|
-
const [showTextInput, setShowTextInput] =
|
|
7918
|
+
const [textInput, setTextInput] = useState11("");
|
|
7919
|
+
const [showTextInput, setShowTextInput] = useState11(false);
|
|
8033
7920
|
const audioLevels = useAudioLevel(isListening);
|
|
8034
7921
|
return /* @__PURE__ */ jsxs2(
|
|
8035
7922
|
"div",
|
|
@@ -8196,8 +8083,8 @@ function NarrationPanel({ stepIndex, stepCount, pendingNarration, isListening, o
|
|
|
8196
8083
|
);
|
|
8197
8084
|
}
|
|
8198
8085
|
function ReviewPanel({ stepIndex, stepCount, polishedNarration, rawNarration, onApprove, onRedo, onEdit }) {
|
|
8199
|
-
const [editMode, setEditMode] =
|
|
8200
|
-
const [editText, setEditText] =
|
|
8086
|
+
const [editMode, setEditMode] = useState11(false);
|
|
8087
|
+
const [editText, setEditText] = useState11(polishedNarration || rawNarration);
|
|
8201
8088
|
const displayText = polishedNarration || rawNarration;
|
|
8202
8089
|
return /* @__PURE__ */ jsxs2(
|
|
8203
8090
|
"div",
|
|
@@ -8453,7 +8340,7 @@ function FinishingPanel() {
|
|
|
8453
8340
|
}
|
|
8454
8341
|
);
|
|
8455
8342
|
}
|
|
8456
|
-
var
|
|
8343
|
+
var INTERACTIVE_SELECTOR = [
|
|
8457
8344
|
"button",
|
|
8458
8345
|
"a[href]",
|
|
8459
8346
|
'input:not([type="hidden"])',
|
|
@@ -8491,19 +8378,19 @@ function RecordingOverlay({
|
|
|
8491
8378
|
onCancelRecording,
|
|
8492
8379
|
onStopRecording
|
|
8493
8380
|
}) {
|
|
8494
|
-
const [hoveredEl, setHoveredEl] =
|
|
8495
|
-
const [pendingElement, setPendingElement] =
|
|
8496
|
-
const [showPopover, setShowPopover] =
|
|
8497
|
-
const [hudMinimized, setHudMinimized] =
|
|
8498
|
-
const [voicePanelMinimized, setVoicePanelMinimized] =
|
|
8381
|
+
const [hoveredEl, setHoveredEl] = useState11(null);
|
|
8382
|
+
const [pendingElement, setPendingElement] = useState11(null);
|
|
8383
|
+
const [showPopover, setShowPopover] = useState11(false);
|
|
8384
|
+
const [hudMinimized, setHudMinimized] = useState11(false);
|
|
8385
|
+
const [voicePanelMinimized, setVoicePanelMinimized] = useState11(false);
|
|
8499
8386
|
const isSelecting = phase === "selecting";
|
|
8500
|
-
const handleMouseMove =
|
|
8501
|
-
const target = e.target.closest(
|
|
8387
|
+
const handleMouseMove = useCallback10((e) => {
|
|
8388
|
+
const target = e.target.closest(INTERACTIVE_SELECTOR);
|
|
8502
8389
|
setHoveredEl(target);
|
|
8503
8390
|
}, []);
|
|
8504
|
-
const handleClick =
|
|
8391
|
+
const handleClick = useCallback10((e) => {
|
|
8505
8392
|
if (e.target.closest("[data-modelnex-internal]")) return;
|
|
8506
|
-
const target = e.target.closest(
|
|
8393
|
+
const target = e.target.closest(INTERACTIVE_SELECTOR);
|
|
8507
8394
|
if (target) {
|
|
8508
8395
|
e.preventDefault();
|
|
8509
8396
|
e.stopPropagation();
|
|
@@ -8526,7 +8413,7 @@ function RecordingOverlay({
|
|
|
8526
8413
|
setShowPopover(true);
|
|
8527
8414
|
}
|
|
8528
8415
|
}, []);
|
|
8529
|
-
|
|
8416
|
+
useEffect15(() => {
|
|
8530
8417
|
if (!isSelecting) return;
|
|
8531
8418
|
document.addEventListener("mousemove", handleMouseMove, true);
|
|
8532
8419
|
document.addEventListener("click", handleClick, true);
|
|
@@ -8538,14 +8425,14 @@ function RecordingOverlay({
|
|
|
8538
8425
|
document.body.style.cursor = prev;
|
|
8539
8426
|
};
|
|
8540
8427
|
}, [isSelecting, handleMouseMove, handleClick]);
|
|
8541
|
-
|
|
8428
|
+
useEffect15(() => {
|
|
8542
8429
|
if (isSelecting) {
|
|
8543
8430
|
setShowPopover(false);
|
|
8544
8431
|
setPendingElement(null);
|
|
8545
8432
|
setHoveredEl(null);
|
|
8546
8433
|
}
|
|
8547
8434
|
}, [isSelecting]);
|
|
8548
|
-
const handlePopoverConfirm =
|
|
8435
|
+
const handlePopoverConfirm = useCallback10((type) => {
|
|
8549
8436
|
setShowPopover(false);
|
|
8550
8437
|
if (pendingElement) {
|
|
8551
8438
|
onElementSelected(pendingElement);
|
|
@@ -8555,7 +8442,7 @@ function RecordingOverlay({
|
|
|
8555
8442
|
onStepTypeConfirmed(type);
|
|
8556
8443
|
onStartNarration();
|
|
8557
8444
|
}, [pendingElement, onElementSelected, onPageLevelStep, onStepTypeConfirmed, onStartNarration]);
|
|
8558
|
-
const handlePopoverCancel =
|
|
8445
|
+
const handlePopoverCancel = useCallback10(() => {
|
|
8559
8446
|
setShowPopover(false);
|
|
8560
8447
|
setPendingElement(null);
|
|
8561
8448
|
}, []);
|
|
@@ -9102,8 +8989,8 @@ function getViewportHeight() {
|
|
|
9102
8989
|
return layoutViewportHeight;
|
|
9103
8990
|
}
|
|
9104
8991
|
function useViewportHeight() {
|
|
9105
|
-
const [viewportHeight, setViewportHeight] =
|
|
9106
|
-
|
|
8992
|
+
const [viewportHeight, setViewportHeight] = useState12(() => getViewportHeight());
|
|
8993
|
+
useEffect16(() => {
|
|
9107
8994
|
if (typeof window === "undefined") return;
|
|
9108
8995
|
const updateViewportHeight = () => {
|
|
9109
8996
|
setViewportHeight(getViewportHeight());
|
|
@@ -9122,8 +9009,8 @@ function useViewportHeight() {
|
|
|
9122
9009
|
return viewportHeight;
|
|
9123
9010
|
}
|
|
9124
9011
|
function useMediaQuery(query) {
|
|
9125
|
-
const [matches, setMatches] =
|
|
9126
|
-
|
|
9012
|
+
const [matches, setMatches] = useState12(false);
|
|
9013
|
+
useEffect16(() => {
|
|
9127
9014
|
const media = window.matchMedia(query);
|
|
9128
9015
|
if (media.matches !== matches) setMatches(media.matches);
|
|
9129
9016
|
const listener = () => setMatches(media.matches);
|
|
@@ -9195,8 +9082,8 @@ var GLOBAL_STYLES = `
|
|
|
9195
9082
|
}
|
|
9196
9083
|
`;
|
|
9197
9084
|
function Tooltip({ children, title }) {
|
|
9198
|
-
const [show, setShow] =
|
|
9199
|
-
const ref =
|
|
9085
|
+
const [show, setShow] = useState12(false);
|
|
9086
|
+
const ref = useRef12(null);
|
|
9200
9087
|
const handleShow = () => setShow(true);
|
|
9201
9088
|
const handleHide = () => setShow(false);
|
|
9202
9089
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -9290,11 +9177,11 @@ var TrashIcon = () => /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16",
|
|
|
9290
9177
|
var StopIcon = () => /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
|
|
9291
9178
|
var ChevronDown = ({ open }) => /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { transform: open ? "rotate(180deg)" : "none", transition: "transform 0.2s" }, children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" }) });
|
|
9292
9179
|
function AgentTraces({ debug, command, defaultExpanded = true }) {
|
|
9293
|
-
const [expanded, setExpanded] =
|
|
9294
|
-
const [expandedSteps, setExpandedSteps] =
|
|
9180
|
+
const [expanded, setExpanded] = useState12(defaultExpanded);
|
|
9181
|
+
const [expandedSteps, setExpandedSteps] = useState12(/* @__PURE__ */ new Set());
|
|
9295
9182
|
const traces = debug.traces ?? [];
|
|
9296
9183
|
const hasTraceContent = traces.length > 0 || debug.llmInput || (debug.llmOutput?.length ?? 0) > 0 || (debug.actions?.length ?? 0) > 0;
|
|
9297
|
-
|
|
9184
|
+
useEffect16(() => {
|
|
9298
9185
|
setExpandedSteps(new Set(traces.map((t) => t.step)));
|
|
9299
9186
|
}, [debug]);
|
|
9300
9187
|
const toggleStep = (step) => {
|
|
@@ -9455,21 +9342,21 @@ function ModelNexChatBubble({
|
|
|
9455
9342
|
}) {
|
|
9456
9343
|
const onCommand = void 0;
|
|
9457
9344
|
const recordingExperienceType = "tour";
|
|
9458
|
-
const noop =
|
|
9459
|
-
const noopAsync =
|
|
9345
|
+
const noop = useCallback11(() => void 0, []);
|
|
9346
|
+
const noopAsync = useCallback11(async () => void 0, []);
|
|
9460
9347
|
const runCommand = useRunCommand();
|
|
9461
9348
|
const ctx = useContext5(ModelNexContext);
|
|
9462
|
-
const [hydrated, setHydrated] =
|
|
9463
|
-
const [expanded, setExpanded] =
|
|
9464
|
-
const [docked, setDocked] =
|
|
9465
|
-
const [input, setInput] =
|
|
9349
|
+
const [hydrated, setHydrated] = useState12(false);
|
|
9350
|
+
const [expanded, setExpanded] = useState12(false);
|
|
9351
|
+
const [docked, setDocked] = useState12(false);
|
|
9352
|
+
const [input, setInput] = useState12("");
|
|
9466
9353
|
const messages = ctx?.chatMessages ?? [];
|
|
9467
9354
|
const setMessages = ctx?.setChatMessages ?? (() => {
|
|
9468
9355
|
});
|
|
9469
|
-
const [loading, setLoading] =
|
|
9470
|
-
const messagesEndRef =
|
|
9471
|
-
const abortControllerRef =
|
|
9472
|
-
const panelRef =
|
|
9356
|
+
const [loading, setLoading] = useState12(false);
|
|
9357
|
+
const messagesEndRef = useRef12(null);
|
|
9358
|
+
const abortControllerRef = useRef12(null);
|
|
9359
|
+
const panelRef = useRef12(null);
|
|
9473
9360
|
const serverUrl = ctx?.serverUrl ?? DEFAULT_MODELNEX_SERVER_URL;
|
|
9474
9361
|
const voice = useVoice(serverUrl);
|
|
9475
9362
|
const audioLevels = useAudioLevel(voice.isListening);
|
|
@@ -9478,13 +9365,13 @@ function ModelNexChatBubble({
|
|
|
9478
9365
|
});
|
|
9479
9366
|
const devMode = ctx?.devMode ?? false;
|
|
9480
9367
|
const authoringMode = ctx?.authoringMode ?? devMode;
|
|
9481
|
-
const [recordingTourName, setRecordingTourName] =
|
|
9482
|
-
const [recordingTargetTypes, setRecordingTargetTypes] =
|
|
9483
|
-
const [showStopModal, setShowStopModal] =
|
|
9484
|
-
const [savedDraft, setSavedDraft] =
|
|
9485
|
-
const [reviewDraft, setReviewDraft] =
|
|
9486
|
-
const [tourLiveTranscript, setTourLiveTranscript] =
|
|
9487
|
-
const [activeRecordingExperienceType, setActiveRecordingExperienceType] =
|
|
9368
|
+
const [recordingTourName, setRecordingTourName] = useState12("");
|
|
9369
|
+
const [recordingTargetTypes, setRecordingTargetTypes] = useState12("admin");
|
|
9370
|
+
const [showStopModal, setShowStopModal] = useState12(false);
|
|
9371
|
+
const [savedDraft, setSavedDraft] = useState12(null);
|
|
9372
|
+
const [reviewDraft, setReviewDraft] = useState12("");
|
|
9373
|
+
const [tourLiveTranscript, setTourLiveTranscript] = useState12("");
|
|
9374
|
+
const [activeRecordingExperienceType, setActiveRecordingExperienceType] = useState12(
|
|
9488
9375
|
() => readPersistedRecordingExperienceType() ?? recordingExperienceType
|
|
9489
9376
|
);
|
|
9490
9377
|
const recording = useRecordingMode({
|
|
@@ -9496,7 +9383,7 @@ function ModelNexChatBubble({
|
|
|
9496
9383
|
});
|
|
9497
9384
|
const isGeneratingDraft = isRecordingDraftGenerating(recording.phase);
|
|
9498
9385
|
const showRecordingOverlay = recordingMode && shouldShowRecordingOverlay(recording.phase);
|
|
9499
|
-
|
|
9386
|
+
useEffect16(() => {
|
|
9500
9387
|
const shouldBeRecording = recording.phase !== "idle";
|
|
9501
9388
|
if (shouldBeRecording && !recordingMode) {
|
|
9502
9389
|
setRecordingMode(true);
|
|
@@ -9529,12 +9416,12 @@ function ModelNexChatBubble({
|
|
|
9529
9416
|
const activePlayback = playbackController.playback;
|
|
9530
9417
|
const activeExperienceType = playbackController.activeExperienceType;
|
|
9531
9418
|
const startingExperienceType = playbackController.startingExperienceType;
|
|
9532
|
-
|
|
9419
|
+
useEffect16(() => {
|
|
9533
9420
|
return registerExperienceToolBridge({
|
|
9534
9421
|
startExperience: playbackController.startExperience
|
|
9535
9422
|
});
|
|
9536
9423
|
}, [playbackController.startExperience]);
|
|
9537
|
-
const createPlaybackView =
|
|
9424
|
+
const createPlaybackView = useCallback11((experienceType) => {
|
|
9538
9425
|
const isActiveExperience = activePlayback.isActive && activeExperienceType === experienceType;
|
|
9539
9426
|
const pendingTour = playbackController.pendingPrompt?.experienceType === experienceType ? playbackController.pendingPrompt.tour : null;
|
|
9540
9427
|
return {
|
|
@@ -9566,8 +9453,8 @@ function ModelNexChatBubble({
|
|
|
9566
9453
|
const onboardingPlayback = useMemo3(() => createPlaybackView("onboarding"), [createPlaybackView]);
|
|
9567
9454
|
const tourReviewToggle = getReviewModeToggleConfig(tourPlayback.playbackState);
|
|
9568
9455
|
const tourReviewModeEnabled = isReviewModeEnabled(devMode, tourPlayback.isReviewMode);
|
|
9569
|
-
const lastAutoTaggedUrlRef =
|
|
9570
|
-
const handleAutoTag =
|
|
9456
|
+
const lastAutoTaggedUrlRef = useRef12(null);
|
|
9457
|
+
const handleAutoTag = useCallback11(async () => {
|
|
9571
9458
|
if (!ctx) return;
|
|
9572
9459
|
const { extractedElements, tagStore, commandUrl, serverUrl: serverUrl2, websiteId } = ctx;
|
|
9573
9460
|
if (extractedElements.length === 0) return;
|
|
@@ -9606,7 +9493,7 @@ function ModelNexChatBubble({
|
|
|
9606
9493
|
console.warn("[ModelNex] Auto-tag error:", err);
|
|
9607
9494
|
}
|
|
9608
9495
|
}, [ctx]);
|
|
9609
|
-
|
|
9496
|
+
useEffect16(() => {
|
|
9610
9497
|
if (authoringMode && ctx?.extractedElements.length) {
|
|
9611
9498
|
const timer = setTimeout(handleAutoTag, 1e3);
|
|
9612
9499
|
return () => clearTimeout(timer);
|
|
@@ -9628,7 +9515,7 @@ function ModelNexChatBubble({
|
|
|
9628
9515
|
recordingMode,
|
|
9629
9516
|
pendingNotificationType
|
|
9630
9517
|
});
|
|
9631
|
-
|
|
9518
|
+
useEffect16(() => {
|
|
9632
9519
|
setHydrated(true);
|
|
9633
9520
|
try {
|
|
9634
9521
|
setExpanded(sessionStorage.getItem(BUBBLE_EXPANDED_STORAGE_KEY) === "true");
|
|
@@ -9638,18 +9525,18 @@ function ModelNexChatBubble({
|
|
|
9638
9525
|
setDocked(false);
|
|
9639
9526
|
}
|
|
9640
9527
|
}, []);
|
|
9641
|
-
const sttActiveRef =
|
|
9642
|
-
const [tourListenReady, setTourListenReady] =
|
|
9643
|
-
const [tourSttError, setTourSttError] =
|
|
9644
|
-
const previousTourActiveRef =
|
|
9645
|
-
const tourListenReadyRef =
|
|
9646
|
-
const tourSttErrorRef =
|
|
9647
|
-
const updateTourListenReady =
|
|
9528
|
+
const sttActiveRef = useRef12(false);
|
|
9529
|
+
const [tourListenReady, setTourListenReady] = useState12(false);
|
|
9530
|
+
const [tourSttError, setTourSttError] = useState12(null);
|
|
9531
|
+
const previousTourActiveRef = useRef12(false);
|
|
9532
|
+
const tourListenReadyRef = useRef12(false);
|
|
9533
|
+
const tourSttErrorRef = useRef12(null);
|
|
9534
|
+
const updateTourListenReady = useCallback11((next) => {
|
|
9648
9535
|
if (tourListenReadyRef.current === next) return;
|
|
9649
9536
|
tourListenReadyRef.current = next;
|
|
9650
9537
|
setTourListenReady(next);
|
|
9651
9538
|
}, []);
|
|
9652
|
-
const updateTourSttError =
|
|
9539
|
+
const updateTourSttError = useCallback11((next) => {
|
|
9653
9540
|
if (tourSttErrorRef.current === next) return;
|
|
9654
9541
|
tourSttErrorRef.current = next;
|
|
9655
9542
|
setTourSttError(next);
|
|
@@ -9658,7 +9545,7 @@ function ModelNexChatBubble({
|
|
|
9658
9545
|
() => buildTranscriptPreviewLines(tourLiveTranscript, { maxCharsPerLine: 36, maxLines: 2 }),
|
|
9659
9546
|
[tourLiveTranscript]
|
|
9660
9547
|
);
|
|
9661
|
-
|
|
9548
|
+
useEffect16(() => {
|
|
9662
9549
|
const shouldShowFloatingTranscript = (tourPlayback.isActive || onboardingPlayback.isActive) && tourListenReady && voice.isListening;
|
|
9663
9550
|
if (shouldShowFloatingTranscript) {
|
|
9664
9551
|
showFloatingLiveTranscript({
|
|
@@ -9680,10 +9567,10 @@ function ModelNexChatBubble({
|
|
|
9680
9567
|
tourPlayback.isActive,
|
|
9681
9568
|
voice.isListening
|
|
9682
9569
|
]);
|
|
9683
|
-
|
|
9570
|
+
useEffect16(() => () => {
|
|
9684
9571
|
hideFloatingLiveTranscript();
|
|
9685
9572
|
}, []);
|
|
9686
|
-
const setExpandedState =
|
|
9573
|
+
const setExpandedState = useCallback11((next, opts) => {
|
|
9687
9574
|
setExpanded(next);
|
|
9688
9575
|
try {
|
|
9689
9576
|
sessionStorage.setItem(BUBBLE_EXPANDED_STORAGE_KEY, String(next));
|
|
@@ -9695,14 +9582,14 @@ function ModelNexChatBubble({
|
|
|
9695
9582
|
} catch {
|
|
9696
9583
|
}
|
|
9697
9584
|
}, [tourPlayback.isActive, onboardingPlayback.isActive]);
|
|
9698
|
-
const setDockedState =
|
|
9585
|
+
const setDockedState = useCallback11((next) => {
|
|
9699
9586
|
setDocked(next);
|
|
9700
9587
|
try {
|
|
9701
9588
|
sessionStorage.setItem(BUBBLE_DOCKED_STORAGE_KEY, String(next));
|
|
9702
9589
|
} catch {
|
|
9703
9590
|
}
|
|
9704
9591
|
}, []);
|
|
9705
|
-
|
|
9592
|
+
useEffect16(() => {
|
|
9706
9593
|
if (shouldAutoExpandForPendingPrompt({
|
|
9707
9594
|
pendingPrompt,
|
|
9708
9595
|
isPlaybackActive,
|
|
@@ -9712,8 +9599,8 @@ function ModelNexChatBubble({
|
|
|
9712
9599
|
setExpandedState(true);
|
|
9713
9600
|
}
|
|
9714
9601
|
}, [isPlaybackActive, pendingNotificationType, pendingPrompt, recordingMode, setExpandedState]);
|
|
9715
|
-
const preferredListeningExperienceRef =
|
|
9716
|
-
const playbackVoiceRoutingRef =
|
|
9602
|
+
const preferredListeningExperienceRef = useRef12(null);
|
|
9603
|
+
const playbackVoiceRoutingRef = useRef12({
|
|
9717
9604
|
activeExperienceType,
|
|
9718
9605
|
isActive: activePlayback.isActive,
|
|
9719
9606
|
isReviewMode: activePlayback.isReviewMode,
|
|
@@ -9721,7 +9608,7 @@ function ModelNexChatBubble({
|
|
|
9721
9608
|
playbackState: activePlayback.playbackState,
|
|
9722
9609
|
handleVoiceInput: playbackController.handleVoiceInput
|
|
9723
9610
|
});
|
|
9724
|
-
|
|
9611
|
+
useEffect16(() => {
|
|
9725
9612
|
playbackVoiceRoutingRef.current = {
|
|
9726
9613
|
activeExperienceType,
|
|
9727
9614
|
isActive: activePlayback.isActive,
|
|
@@ -9738,7 +9625,7 @@ function ModelNexChatBubble({
|
|
|
9738
9625
|
playbackController.handleVoiceInput,
|
|
9739
9626
|
playbackController.pendingPrompt?.experienceType
|
|
9740
9627
|
]);
|
|
9741
|
-
const handleVoiceTourInput =
|
|
9628
|
+
const handleVoiceTourInput = useCallback11(createSinglePlaybackTranscriptRouter(
|
|
9742
9629
|
() => ({
|
|
9743
9630
|
isReviewMode: playbackVoiceRoutingRef.current.isReviewMode,
|
|
9744
9631
|
playbackState: playbackVoiceRoutingRef.current.playbackState
|
|
@@ -9753,7 +9640,7 @@ function ModelNexChatBubble({
|
|
|
9753
9640
|
}
|
|
9754
9641
|
}
|
|
9755
9642
|
), []);
|
|
9756
|
-
const startTourListening =
|
|
9643
|
+
const startTourListening = useCallback11((preferredExperience) => {
|
|
9757
9644
|
const listeningState = {
|
|
9758
9645
|
isTourActive: tourPlayback.isActive,
|
|
9759
9646
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9808,7 +9695,7 @@ function ModelNexChatBubble({
|
|
|
9808
9695
|
updateTourListenReady,
|
|
9809
9696
|
updateTourSttError
|
|
9810
9697
|
]);
|
|
9811
|
-
|
|
9698
|
+
useEffect16(() => {
|
|
9812
9699
|
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9813
9700
|
isTourActive: tourPlayback.isActive,
|
|
9814
9701
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9831,12 +9718,12 @@ function ModelNexChatBubble({
|
|
|
9831
9718
|
}
|
|
9832
9719
|
}
|
|
9833
9720
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, setExpandedState, startingExperienceType, updateTourSttError]);
|
|
9834
|
-
|
|
9721
|
+
useEffect16(() => {
|
|
9835
9722
|
if (!tourPlayback.isReviewMode && !onboardingPlayback.isReviewMode) {
|
|
9836
9723
|
setReviewDraft("");
|
|
9837
9724
|
}
|
|
9838
9725
|
}, [tourPlayback.isReviewMode, onboardingPlayback.isReviewMode]);
|
|
9839
|
-
|
|
9726
|
+
useEffect16(() => {
|
|
9840
9727
|
if (!isTourListeningSessionActive({
|
|
9841
9728
|
isTourActive: tourPlayback.isActive,
|
|
9842
9729
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9845,12 +9732,12 @@ function ModelNexChatBubble({
|
|
|
9845
9732
|
preferredListeningExperienceRef.current = null;
|
|
9846
9733
|
}
|
|
9847
9734
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, startingExperienceType]);
|
|
9848
|
-
|
|
9735
|
+
useEffect16(() => {
|
|
9849
9736
|
if (recordingMode) {
|
|
9850
9737
|
setExpandedState(false);
|
|
9851
9738
|
}
|
|
9852
9739
|
}, [recordingMode, setExpandedState]);
|
|
9853
|
-
|
|
9740
|
+
useEffect16(() => {
|
|
9854
9741
|
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9855
9742
|
isTourActive: tourPlayback.isActive,
|
|
9856
9743
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9864,7 +9751,7 @@ function ModelNexChatBubble({
|
|
|
9864
9751
|
}
|
|
9865
9752
|
updateTourListenReady(Boolean(voice.isListening && sttActiveRef.current));
|
|
9866
9753
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice.isListening, startingExperienceType, updateTourListenReady]);
|
|
9867
|
-
|
|
9754
|
+
useEffect16(() => {
|
|
9868
9755
|
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9869
9756
|
isTourActive: tourPlayback.isActive,
|
|
9870
9757
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9879,7 +9766,7 @@ function ModelNexChatBubble({
|
|
|
9879
9766
|
voice.stopListening();
|
|
9880
9767
|
}
|
|
9881
9768
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice, startingExperienceType, updateTourListenReady, updateTourSttError]);
|
|
9882
|
-
|
|
9769
|
+
useEffect16(() => {
|
|
9883
9770
|
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9884
9771
|
isTourActive: tourPlayback.isActive,
|
|
9885
9772
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
@@ -9901,8 +9788,8 @@ function ModelNexChatBubble({
|
|
|
9901
9788
|
window.removeEventListener("keydown", enableTourListeningFromGesture, true);
|
|
9902
9789
|
};
|
|
9903
9790
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, tourListenReady, tourSttError, voice.sttSupported, startTourListening, startingExperienceType]);
|
|
9904
|
-
const [voiceInputMode, setVoiceInputMode] =
|
|
9905
|
-
const toggleVoiceInput =
|
|
9791
|
+
const [voiceInputMode, setVoiceInputMode] = useState12(false);
|
|
9792
|
+
const toggleVoiceInput = useCallback11(() => {
|
|
9906
9793
|
if (voiceInputMode) {
|
|
9907
9794
|
voice.stopListening();
|
|
9908
9795
|
setVoiceInputMode(false);
|
|
@@ -9925,7 +9812,7 @@ function ModelNexChatBubble({
|
|
|
9925
9812
|
);
|
|
9926
9813
|
}
|
|
9927
9814
|
}, [voiceInputMode, voice, recordingMode, recording]);
|
|
9928
|
-
|
|
9815
|
+
useEffect16(() => {
|
|
9929
9816
|
const panel = panelRef.current;
|
|
9930
9817
|
if (!panel) return;
|
|
9931
9818
|
const stopKeyPropagation = (e) => {
|
|
@@ -9949,7 +9836,7 @@ function ModelNexChatBubble({
|
|
|
9949
9836
|
const tourCompletionRatio = tourPlayback.totalSteps > 0 ? Math.min(tourPlayback.currentStepIndex / tourPlayback.totalSteps, 1) : 0;
|
|
9950
9837
|
const tourCurrentStep = tourPlayback.activeTour?.steps?.[tourPlayback.currentStepIndex] || null;
|
|
9951
9838
|
const scrollToBottom = () => messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
9952
|
-
|
|
9839
|
+
useEffect16(() => {
|
|
9953
9840
|
if (messages.length > 0) scrollToBottom();
|
|
9954
9841
|
}, [messages, loading]);
|
|
9955
9842
|
const runAgent = async () => {
|
|
@@ -10006,7 +9893,7 @@ function ModelNexChatBubble({
|
|
|
10006
9893
|
const clearChat = () => {
|
|
10007
9894
|
setMessages([]);
|
|
10008
9895
|
};
|
|
10009
|
-
const startRecordingSession =
|
|
9896
|
+
const startRecordingSession = useCallback11((experienceType) => {
|
|
10010
9897
|
abortControllerRef.current?.abort();
|
|
10011
9898
|
abortControllerRef.current = null;
|
|
10012
9899
|
setLoading(false);
|
|
@@ -11259,7 +11146,7 @@ function ModelNexChatBubble({
|
|
|
11259
11146
|
}
|
|
11260
11147
|
|
|
11261
11148
|
// src/onboarding-panel.tsx
|
|
11262
|
-
import { useCallback as
|
|
11149
|
+
import { useCallback as useCallback12, useContext as useContext6, useEffect as useEffect17, useMemo as useMemo4, useRef as useRef13, useState as useState13 } from "react";
|
|
11263
11150
|
import { createPortal as createPortal2 } from "react-dom";
|
|
11264
11151
|
|
|
11265
11152
|
// src/hooks/useOnboardingPlayback.ts
|
|
@@ -11294,14 +11181,14 @@ function ModelNexOnboardingPanel({
|
|
|
11294
11181
|
const devMode = ctx?.devMode ?? false;
|
|
11295
11182
|
const voice = useVoice(serverUrl);
|
|
11296
11183
|
const audioLevels = useAudioLevel(voice.isListening);
|
|
11297
|
-
const [input, setInput] =
|
|
11298
|
-
const [reviewDraft, setReviewDraft] =
|
|
11299
|
-
const [open, setOpen] =
|
|
11300
|
-
const [currentStepIndex, setCurrentStepIndex] =
|
|
11301
|
-
const [liveTranscript, setLiveTranscript] =
|
|
11302
|
-
const [voiceEnabled, setVoiceEnabled] =
|
|
11303
|
-
const [sttError, setSttError] =
|
|
11304
|
-
const voiceEnabledRef =
|
|
11184
|
+
const [input, setInput] = useState13("");
|
|
11185
|
+
const [reviewDraft, setReviewDraft] = useState13("");
|
|
11186
|
+
const [open, setOpen] = useState13(true);
|
|
11187
|
+
const [currentStepIndex, setCurrentStepIndex] = useState13(0);
|
|
11188
|
+
const [liveTranscript, setLiveTranscript] = useState13("");
|
|
11189
|
+
const [voiceEnabled, setVoiceEnabled] = useState13(false);
|
|
11190
|
+
const [sttError, setSttError] = useState13(null);
|
|
11191
|
+
const voiceEnabledRef = useRef13(false);
|
|
11305
11192
|
const playback = useOnboardingPlayback({
|
|
11306
11193
|
serverUrl,
|
|
11307
11194
|
commandUrl: ctx?.commandUrl,
|
|
@@ -11321,26 +11208,26 @@ function ModelNexOnboardingPanel({
|
|
|
11321
11208
|
voiceEnabledRef.current = false;
|
|
11322
11209
|
}
|
|
11323
11210
|
});
|
|
11324
|
-
const playbackVoiceRoutingRef =
|
|
11211
|
+
const playbackVoiceRoutingRef = useRef13({
|
|
11325
11212
|
isReviewMode: false,
|
|
11326
11213
|
playbackState: playback.playbackState,
|
|
11327
11214
|
handleVoiceInput: playback.handleVoiceInput
|
|
11328
11215
|
});
|
|
11329
11216
|
const reviewToggle = getReviewModeToggleConfig(playback.playbackState);
|
|
11330
11217
|
const reviewModeEnabled = isReviewModeEnabled(devMode, playback.isReviewMode);
|
|
11331
|
-
|
|
11218
|
+
useEffect17(() => {
|
|
11332
11219
|
playbackVoiceRoutingRef.current = {
|
|
11333
11220
|
isReviewMode: playback.isReviewMode,
|
|
11334
11221
|
playbackState: playback.playbackState,
|
|
11335
11222
|
handleVoiceInput: playback.handleVoiceInput
|
|
11336
11223
|
};
|
|
11337
11224
|
}, [playback.isReviewMode, playback.playbackState, playback.handleVoiceInput]);
|
|
11338
|
-
|
|
11225
|
+
useEffect17(() => {
|
|
11339
11226
|
if (playback.isActive && !playback.isReviewMode) {
|
|
11340
11227
|
setOpen(true);
|
|
11341
11228
|
}
|
|
11342
11229
|
}, [playback.activeTour?.id, playback.isActive, playback.isReviewMode]);
|
|
11343
|
-
const startVoiceListening =
|
|
11230
|
+
const startVoiceListening = useCallback12(() => {
|
|
11344
11231
|
if (voiceEnabledRef.current || !voice.sttSupported) return;
|
|
11345
11232
|
voiceEnabledRef.current = true;
|
|
11346
11233
|
setVoiceEnabled(true);
|
|
@@ -11387,7 +11274,7 @@ function ModelNexOnboardingPanel({
|
|
|
11387
11274
|
}
|
|
11388
11275
|
);
|
|
11389
11276
|
}, [playback.isActive, voice]);
|
|
11390
|
-
|
|
11277
|
+
useEffect17(() => {
|
|
11391
11278
|
if (!playback.isActive || voiceEnabledRef.current || !voice.sttSupported) return;
|
|
11392
11279
|
const startOnGesture = (event) => {
|
|
11393
11280
|
if (shouldIgnorePanelGestureStart(event.target)) {
|
|
@@ -11402,7 +11289,7 @@ function ModelNexOnboardingPanel({
|
|
|
11402
11289
|
window.removeEventListener("keydown", startOnGesture, true);
|
|
11403
11290
|
};
|
|
11404
11291
|
}, [playback.isActive, voice.sttSupported, startVoiceListening]);
|
|
11405
|
-
|
|
11292
|
+
useEffect17(() => {
|
|
11406
11293
|
if (!playback.isActive && voiceEnabledRef.current) {
|
|
11407
11294
|
voiceEnabledRef.current = false;
|
|
11408
11295
|
setVoiceEnabled(false);
|
|
@@ -11422,7 +11309,7 @@ function ModelNexOnboardingPanel({
|
|
|
11422
11309
|
() => buildTranscriptPreviewLines(liveTranscript, { maxCharsPerLine: 34, maxLines: 2 }),
|
|
11423
11310
|
[liveTranscript]
|
|
11424
11311
|
);
|
|
11425
|
-
|
|
11312
|
+
useEffect17(() => {
|
|
11426
11313
|
if (voiceEnabled && voice.isListening) {
|
|
11427
11314
|
showFloatingLiveTranscript({
|
|
11428
11315
|
levels: audioLevels,
|
|
@@ -11436,7 +11323,7 @@ function ModelNexOnboardingPanel({
|
|
|
11436
11323
|
}
|
|
11437
11324
|
hideFloatingLiveTranscript();
|
|
11438
11325
|
}, [audioLevels, liveTranscriptLines, voiceEnabled, voice.isListening]);
|
|
11439
|
-
|
|
11326
|
+
useEffect17(() => () => {
|
|
11440
11327
|
hideFloatingLiveTranscript();
|
|
11441
11328
|
}, []);
|
|
11442
11329
|
const actionButtonStyle = {
|
|
@@ -12001,7 +11888,7 @@ var ModelNexProvider = ({
|
|
|
12001
11888
|
const serverUrl = serverUrlProp ?? DEFAULT_MODELNEX_SERVER_URL;
|
|
12002
11889
|
const commandUrl = void 0;
|
|
12003
11890
|
const disableSocket = false;
|
|
12004
|
-
|
|
11891
|
+
useEffect18(() => {
|
|
12005
11892
|
if (process.env.NODE_ENV !== "production" && !serverUrlProp) {
|
|
12006
11893
|
console.warn(
|
|
12007
11894
|
`[ModelNex SDK] ModelNexProvider is using the default server URL (${DEFAULT_MODELNEX_SERVER_URL}). Pass \`serverUrl\` explicitly in local development to avoid accidentally targeting the hosted backend.`
|
|
@@ -12009,21 +11896,21 @@ var ModelNexProvider = ({
|
|
|
12009
11896
|
}
|
|
12010
11897
|
}, [serverUrlProp]);
|
|
12011
11898
|
const renderedChildren = children;
|
|
12012
|
-
const [activeAgentActions, setActiveAgentActions] =
|
|
12013
|
-
const [stagingFields, setStagingFields] =
|
|
12014
|
-
const [executedFields, setExecutedFields] =
|
|
12015
|
-
const [highlightActions, setHighlightActions] =
|
|
12016
|
-
const [studioMode, setStudioMode] =
|
|
12017
|
-
const [recordingMode, setRecordingMode] =
|
|
12018
|
-
const [voiceMuted, setVoiceMuted] =
|
|
12019
|
-
const [socketId, setSocketId] =
|
|
12020
|
-
const [actions, setActions] =
|
|
12021
|
-
const [validatedBrowserDevMode, setValidatedBrowserDevMode] =
|
|
12022
|
-
const [resolvedDevModeKey, setResolvedDevModeKey] =
|
|
12023
|
-
const [previewRuntimeMode, setPreviewRuntimeMode] =
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
11899
|
+
const [activeAgentActions, setActiveAgentActions] = useState14(/* @__PURE__ */ new Set());
|
|
11900
|
+
const [stagingFields, setStagingFields] = useState14(/* @__PURE__ */ new Set());
|
|
11901
|
+
const [executedFields, setExecutedFields] = useState14(/* @__PURE__ */ new Set());
|
|
11902
|
+
const [highlightActions, setHighlightActions] = useState14(false);
|
|
11903
|
+
const [studioMode, setStudioMode] = useState14(false);
|
|
11904
|
+
const [recordingMode, setRecordingMode] = useState14(() => hasPersistedRecordingSession());
|
|
11905
|
+
const [voiceMuted, setVoiceMuted] = useState14(false);
|
|
11906
|
+
const [socketId, setSocketId] = useState14(null);
|
|
11907
|
+
const [actions, setActions] = useState14(/* @__PURE__ */ new Map());
|
|
11908
|
+
const [validatedBrowserDevMode, setValidatedBrowserDevMode] = useState14(false);
|
|
11909
|
+
const [resolvedDevModeKey, setResolvedDevModeKey] = useState14(() => resolveInjectedDevModeKey());
|
|
11910
|
+
const [previewRuntimeMode, setPreviewRuntimeMode] = useState14(() => hasDraftPreviewModeSignal());
|
|
11911
|
+
useEffect18(() => observeInjectedDevModeKey(setResolvedDevModeKey), []);
|
|
11912
|
+
useEffect18(() => observeDraftPreviewModeSignal(setPreviewRuntimeMode), []);
|
|
11913
|
+
useEffect18(() => {
|
|
12027
11914
|
let cancelled = false;
|
|
12028
11915
|
if (!websiteId || !resolvedDevModeKey) {
|
|
12029
11916
|
setValidatedBrowserDevMode(false);
|
|
@@ -12040,14 +11927,14 @@ var ModelNexProvider = ({
|
|
|
12040
11927
|
};
|
|
12041
11928
|
}, [resolvedDevModeKey, serverUrl, websiteId]);
|
|
12042
11929
|
const effectiveDevMode = validatedBrowserDevMode || previewRuntimeMode;
|
|
12043
|
-
const registerAction =
|
|
11930
|
+
const registerAction = useCallback13((action) => {
|
|
12044
11931
|
setActions((prev) => {
|
|
12045
11932
|
const next = new Map(prev);
|
|
12046
11933
|
next.set(action.id, action);
|
|
12047
11934
|
return next;
|
|
12048
11935
|
});
|
|
12049
11936
|
}, []);
|
|
12050
|
-
const unregisterAction =
|
|
11937
|
+
const unregisterAction = useCallback13((id) => {
|
|
12051
11938
|
setActions((prev) => {
|
|
12052
11939
|
const next = new Map(prev);
|
|
12053
11940
|
next.delete(id);
|
|
@@ -12058,8 +11945,8 @@ var ModelNexProvider = ({
|
|
|
12058
11945
|
const tagStore = useTagStore({ serverUrl, websiteId });
|
|
12059
11946
|
useBuiltinActions(registerAction, unregisterAction, tagStore, serverUrl, websiteId, toursApiBase, userProfile);
|
|
12060
11947
|
const CHAT_STORAGE_KEY = "modelnex-chat-messages";
|
|
12061
|
-
const [chatMessages, setChatMessagesRaw] =
|
|
12062
|
-
|
|
11948
|
+
const [chatMessages, setChatMessagesRaw] = useState14([]);
|
|
11949
|
+
useEffect18(() => {
|
|
12063
11950
|
try {
|
|
12064
11951
|
const stored = sessionStorage.getItem(CHAT_STORAGE_KEY);
|
|
12065
11952
|
if (stored) {
|
|
@@ -12068,7 +11955,7 @@ var ModelNexProvider = ({
|
|
|
12068
11955
|
} catch {
|
|
12069
11956
|
}
|
|
12070
11957
|
}, [effectiveDevMode]);
|
|
12071
|
-
|
|
11958
|
+
useEffect18(() => {
|
|
12072
11959
|
setChatMessagesRaw((prev) => {
|
|
12073
11960
|
const next = sanitizeChatMessages(prev, effectiveDevMode);
|
|
12074
11961
|
try {
|
|
@@ -12078,7 +11965,7 @@ var ModelNexProvider = ({
|
|
|
12078
11965
|
return next;
|
|
12079
11966
|
});
|
|
12080
11967
|
}, [effectiveDevMode]);
|
|
12081
|
-
const setChatMessages =
|
|
11968
|
+
const setChatMessages = useCallback13((action) => {
|
|
12082
11969
|
setChatMessagesRaw((prev) => {
|
|
12083
11970
|
const resolved = typeof action === "function" ? action(prev) : action;
|
|
12084
11971
|
const next = sanitizeChatMessages(resolved, effectiveDevMode);
|
|
@@ -12104,14 +11991,14 @@ var ModelNexProvider = ({
|
|
|
12104
11991
|
devMode: effectiveDevMode
|
|
12105
11992
|
});
|
|
12106
11993
|
useFieldHighlight(stagingFields, executedFields, setExecutedFields);
|
|
12107
|
-
|
|
11994
|
+
useEffect18(() => {
|
|
12108
11995
|
document.body.classList.toggle("modelnex-highlight-actions", highlightActions);
|
|
12109
11996
|
return () => {
|
|
12110
11997
|
document.body.classList.remove("modelnex-highlight-actions");
|
|
12111
11998
|
};
|
|
12112
11999
|
}, [highlightActions]);
|
|
12113
|
-
const [mounted, setMounted] =
|
|
12114
|
-
|
|
12000
|
+
const [mounted, setMounted] = useState14(false);
|
|
12001
|
+
useEffect18(() => {
|
|
12115
12002
|
setMounted(true);
|
|
12116
12003
|
}, []);
|
|
12117
12004
|
const value = useMemo5(
|