@xiaou66/vite-plugin-vue-mcp-next 1.2.0 → 1.3.0
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/README.md +41 -0
- package/dist/index.cjs +440 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -926
- package/dist/index.d.ts +5 -926
- package/dist/index.js +412 -94
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +655 -176
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.d.cts +5 -1
- package/dist/runtime/client.d.ts +5 -1
- package/dist/runtime/client.js +655 -176
- package/dist/runtime/client.js.map +1 -1
- package/dist/types-BKXdHkwk.d.cts +1019 -0
- package/dist/types-BKXdHkwk.d.ts +1019 -0
- package/package.json +3 -1
- package/skills/vite-mcp-next/SKILL.md +9 -6
package/dist/runtime/client.cjs
CHANGED
|
@@ -28,6 +28,116 @@ __export(client_exports, {
|
|
|
28
28
|
module.exports = __toCommonJS(client_exports);
|
|
29
29
|
var import_vite_hot_client = require("vite-hot-client");
|
|
30
30
|
|
|
31
|
+
// src/shared/limits.ts
|
|
32
|
+
var DEFAULT_DOM_MAX_DEPTH = 8;
|
|
33
|
+
var DEFAULT_DOM_MAX_NODES = 2e3;
|
|
34
|
+
var DEFAULT_DOM_MAX_TEXT_LENGTH = 300;
|
|
35
|
+
var DEFAULT_CONSOLE_MAX_RECORDS = 1e3;
|
|
36
|
+
var DEFAULT_NETWORK_MAX_RECORDS = 500;
|
|
37
|
+
var DEFAULT_NETWORK_MAX_BODY_SIZE = 1e5;
|
|
38
|
+
var DEFAULT_MASK_HEADERS = [
|
|
39
|
+
"authorization",
|
|
40
|
+
"cookie",
|
|
41
|
+
"set-cookie"
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
// src/constants.ts
|
|
45
|
+
var DEFAULT_MCP_PATH = "/__mcp";
|
|
46
|
+
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
47
|
+
var DEFAULT_SCREENSHOT_SAVE_DIR = ".vite-mcp/screenshot";
|
|
48
|
+
var DEFAULT_PERFORMANCE_SAVE_DIR = ".vite-mcp/performance";
|
|
49
|
+
var DEFAULT_PERFORMANCE_MAX_DURATION_MS = 3e4;
|
|
50
|
+
var DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS = 250;
|
|
51
|
+
var DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS = 50;
|
|
52
|
+
var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
53
|
+
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
54
|
+
var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
|
|
55
|
+
var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
|
|
56
|
+
var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
|
|
57
|
+
var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
58
|
+
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
59
|
+
var RUNTIME_PAGE_CONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-connected";
|
|
60
|
+
var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnected";
|
|
61
|
+
var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
|
|
62
|
+
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
63
|
+
var DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS = 2200;
|
|
64
|
+
var DEFAULT_OPTIONS = {
|
|
65
|
+
mcpPath: DEFAULT_MCP_PATH,
|
|
66
|
+
host: "localhost",
|
|
67
|
+
printUrl: true,
|
|
68
|
+
updateCursorMcpJson: {
|
|
69
|
+
enabled: true,
|
|
70
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
71
|
+
},
|
|
72
|
+
mcpClients: {
|
|
73
|
+
cursor: true,
|
|
74
|
+
codex: true,
|
|
75
|
+
claudeCode: true,
|
|
76
|
+
trae: true,
|
|
77
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
78
|
+
},
|
|
79
|
+
skill: {
|
|
80
|
+
autoConfig: true
|
|
81
|
+
},
|
|
82
|
+
elementPicker: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
shortcut: {
|
|
85
|
+
altKey: true,
|
|
86
|
+
shiftKey: true,
|
|
87
|
+
metaKey: false,
|
|
88
|
+
ctrlKey: false
|
|
89
|
+
},
|
|
90
|
+
toastDurationMs: DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS
|
|
91
|
+
},
|
|
92
|
+
runtime: {
|
|
93
|
+
mode: "auto",
|
|
94
|
+
evaluate: {
|
|
95
|
+
enabled: false,
|
|
96
|
+
timeoutMs: 3e3
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
cdp: {},
|
|
100
|
+
network: {
|
|
101
|
+
mode: "auto",
|
|
102
|
+
maxRecords: DEFAULT_NETWORK_MAX_RECORDS,
|
|
103
|
+
captureRequestBody: true,
|
|
104
|
+
captureResponseBody: true,
|
|
105
|
+
maxBodySize: DEFAULT_NETWORK_MAX_BODY_SIZE,
|
|
106
|
+
maskHeaders: [...DEFAULT_MASK_HEADERS]
|
|
107
|
+
},
|
|
108
|
+
dom: {
|
|
109
|
+
maxDepth: DEFAULT_DOM_MAX_DEPTH,
|
|
110
|
+
maxNodes: DEFAULT_DOM_MAX_NODES,
|
|
111
|
+
maxTextLength: DEFAULT_DOM_MAX_TEXT_LENGTH
|
|
112
|
+
},
|
|
113
|
+
console: {
|
|
114
|
+
maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
|
|
115
|
+
},
|
|
116
|
+
screenshot: {
|
|
117
|
+
type: "path",
|
|
118
|
+
saveDir: DEFAULT_SCREENSHOT_SAVE_DIR,
|
|
119
|
+
prefer: "auto",
|
|
120
|
+
maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
|
|
121
|
+
snapdom: {
|
|
122
|
+
options: {},
|
|
123
|
+
plugins: []
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
performance: {
|
|
127
|
+
mode: "auto",
|
|
128
|
+
maxDurationMs: DEFAULT_PERFORMANCE_MAX_DURATION_MS,
|
|
129
|
+
sampleIntervalMs: DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS,
|
|
130
|
+
longTaskThresholdMs: DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS,
|
|
131
|
+
saveDir: DEFAULT_PERFORMANCE_SAVE_DIR,
|
|
132
|
+
memory: {
|
|
133
|
+
enabled: true
|
|
134
|
+
},
|
|
135
|
+
stacks: {
|
|
136
|
+
enabled: true
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
31
141
|
// src/runtime/consoleHook.ts
|
|
32
142
|
var import_nanoid = require("nanoid");
|
|
33
143
|
|
|
@@ -97,8 +207,227 @@ function installConsoleHook(options) {
|
|
|
97
207
|
};
|
|
98
208
|
}
|
|
99
209
|
|
|
100
|
-
// src/runtime/
|
|
210
|
+
// src/runtime/elementRegistry.ts
|
|
101
211
|
var import_nanoid2 = require("nanoid");
|
|
212
|
+
var RUNTIME_ELEMENT_ID_PREFIX = "runtime:vmcp_";
|
|
213
|
+
var RUNTIME_ELEMENT_ID_SIZE = 8;
|
|
214
|
+
var runtimeElementRegistry = createRuntimeElementRegistry();
|
|
215
|
+
function createRuntimeElementId() {
|
|
216
|
+
return `${RUNTIME_ELEMENT_ID_PREFIX}${(0, import_nanoid2.nanoid)(RUNTIME_ELEMENT_ID_SIZE)}`;
|
|
217
|
+
}
|
|
218
|
+
function createRuntimeElementRegistry() {
|
|
219
|
+
const records = /* @__PURE__ */ new Map();
|
|
220
|
+
return {
|
|
221
|
+
register(element) {
|
|
222
|
+
const elementId = createRuntimeElementId();
|
|
223
|
+
records.set(elementId, {
|
|
224
|
+
elementId,
|
|
225
|
+
element,
|
|
226
|
+
createdAt: Date.now()
|
|
227
|
+
});
|
|
228
|
+
return elementId;
|
|
229
|
+
},
|
|
230
|
+
get(elementId) {
|
|
231
|
+
return records.get(elementId);
|
|
232
|
+
},
|
|
233
|
+
clear() {
|
|
234
|
+
records.clear();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/runtime/elementPicker.ts
|
|
240
|
+
var MCP_ID_ATTR = "data-v-mcp-id";
|
|
241
|
+
var INTERNAL_ATTR = "data-v-mcp-internal";
|
|
242
|
+
var SUCCESS_MESSAGE = "\u5143\u7D20\u4F4D\u7F6E\u5DF2\u590D\u5236\uFF0C\u8BF7\u53D1\u9001\u7ED9 AI";
|
|
243
|
+
var COPY_FAILED_PREFIX = "\u590D\u5236\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u590D\u5236\u5143\u7D20 ID";
|
|
244
|
+
var OVERLAY_Z_INDEX = "2147483647";
|
|
245
|
+
var TOAST_Z_INDEX = "2147483647";
|
|
246
|
+
function installElementPicker(options) {
|
|
247
|
+
if (!options.enabled) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const registry = runtimeElementRegistry;
|
|
251
|
+
const overlay = createOverlay();
|
|
252
|
+
let active = false;
|
|
253
|
+
let currentElement;
|
|
254
|
+
window.addEventListener("keydown", (event) => {
|
|
255
|
+
active = matchesShortcut(event, options.shortcut);
|
|
256
|
+
});
|
|
257
|
+
window.addEventListener("keyup", () => {
|
|
258
|
+
active = false;
|
|
259
|
+
currentElement = void 0;
|
|
260
|
+
updateOverlay(overlay);
|
|
261
|
+
});
|
|
262
|
+
window.addEventListener("mousemove", (event) => {
|
|
263
|
+
if (!active) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
currentElement = document.elementFromPoint(event.clientX, event.clientY) ?? void 0;
|
|
267
|
+
updateOverlay(overlay, currentElement);
|
|
268
|
+
});
|
|
269
|
+
window.addEventListener(
|
|
270
|
+
"click",
|
|
271
|
+
(event) => {
|
|
272
|
+
if (!active || !isElementLike(event.target)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
event.stopPropagation();
|
|
277
|
+
void copyAndNotify(event.target, registry, options.toastDurationMs);
|
|
278
|
+
},
|
|
279
|
+
true
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
function matchesShortcut(event, shortcut) {
|
|
283
|
+
return event.altKey === shortcut.altKey && event.shiftKey === shortcut.shiftKey && event.metaKey === shortcut.metaKey && event.ctrlKey === shortcut.ctrlKey;
|
|
284
|
+
}
|
|
285
|
+
function resolveElementId(element, registry) {
|
|
286
|
+
return element.getAttribute(MCP_ID_ATTR) ?? registry.register(element);
|
|
287
|
+
}
|
|
288
|
+
async function copyAndNotify(element, registry, toastDurationMs) {
|
|
289
|
+
const elementId = resolveElementId(element, registry);
|
|
290
|
+
const copied = await copyElementId(elementId);
|
|
291
|
+
showToast(
|
|
292
|
+
copied ? SUCCESS_MESSAGE : `${COPY_FAILED_PREFIX}: ${elementId}`,
|
|
293
|
+
toastDurationMs
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
async function copyElementId(elementId) {
|
|
297
|
+
try {
|
|
298
|
+
await navigator.clipboard.writeText(elementId);
|
|
299
|
+
return true;
|
|
300
|
+
} catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
function createOverlay() {
|
|
305
|
+
const overlay = document.createElement("div");
|
|
306
|
+
markInternalElement(overlay);
|
|
307
|
+
Object.assign(overlay.style, {
|
|
308
|
+
position: "fixed",
|
|
309
|
+
pointerEvents: "none",
|
|
310
|
+
border: "2px solid #1d4ed8",
|
|
311
|
+
background: "rgba(29, 78, 216, 0.08)",
|
|
312
|
+
zIndex: OVERLAY_Z_INDEX,
|
|
313
|
+
display: "none"
|
|
314
|
+
});
|
|
315
|
+
document.body.appendChild(overlay);
|
|
316
|
+
return overlay;
|
|
317
|
+
}
|
|
318
|
+
function updateOverlay(overlay, element) {
|
|
319
|
+
if (!element || getElementAttr(element, INTERNAL_ATTR) === "true") {
|
|
320
|
+
overlay.style.display = "none";
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const rect = element.getBoundingClientRect();
|
|
324
|
+
Object.assign(overlay.style, {
|
|
325
|
+
display: "block",
|
|
326
|
+
left: `${String(rect.x)}px`,
|
|
327
|
+
top: `${String(rect.y)}px`,
|
|
328
|
+
width: `${String(rect.width)}px`,
|
|
329
|
+
height: `${String(rect.height)}px`
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function showToast(message, durationMs) {
|
|
333
|
+
const toast = document.createElement("div");
|
|
334
|
+
markInternalElement(toast);
|
|
335
|
+
toast.textContent = message;
|
|
336
|
+
Object.assign(toast.style, {
|
|
337
|
+
position: "fixed",
|
|
338
|
+
left: "50%",
|
|
339
|
+
bottom: "32px",
|
|
340
|
+
transform: "translateX(-50%)",
|
|
341
|
+
zIndex: TOAST_Z_INDEX,
|
|
342
|
+
padding: "8px 12px",
|
|
343
|
+
borderRadius: "6px",
|
|
344
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
345
|
+
color: "#fff",
|
|
346
|
+
fontSize: "13px",
|
|
347
|
+
pointerEvents: "none"
|
|
348
|
+
});
|
|
349
|
+
document.body.appendChild(toast);
|
|
350
|
+
globalThis.setTimeout(() => {
|
|
351
|
+
toast.remove();
|
|
352
|
+
}, durationMs);
|
|
353
|
+
}
|
|
354
|
+
function isElementLike(value) {
|
|
355
|
+
return Boolean(
|
|
356
|
+
value && typeof value === "object" && "getAttribute" in value && "getBoundingClientRect" in value
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
function markInternalElement(element) {
|
|
360
|
+
if (typeof element.setAttribute === "function") {
|
|
361
|
+
element.setAttribute(INTERNAL_ATTR, "true");
|
|
362
|
+
}
|
|
363
|
+
element.dataset.vMcpInternal = "true";
|
|
364
|
+
}
|
|
365
|
+
function getElementAttr(element, name) {
|
|
366
|
+
if (typeof element.getAttribute === "function") {
|
|
367
|
+
return element.getAttribute(name);
|
|
368
|
+
}
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/shared/elementId.ts
|
|
373
|
+
var PROJECT_SOURCE_ID_PATTERN = /^(.+\.(?:vue|tsx|jsx|ts|js)):(\d+):(\d+)$/;
|
|
374
|
+
var RUNTIME_ID_PATTERN = /^runtime:([A-Za-z0-9_-]+)$/;
|
|
375
|
+
var PACKAGE_ID_PREFIX = "pkg:";
|
|
376
|
+
function parseElementId(elementId) {
|
|
377
|
+
const sourceMatch = PROJECT_SOURCE_ID_PATTERN.exec(elementId);
|
|
378
|
+
if (sourceMatch) {
|
|
379
|
+
return {
|
|
380
|
+
kind: "project-source",
|
|
381
|
+
elementId,
|
|
382
|
+
file: sourceMatch[1],
|
|
383
|
+
line: Number(sourceMatch[2]),
|
|
384
|
+
column: Number(sourceMatch[3])
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (elementId.startsWith(PACKAGE_ID_PREFIX)) {
|
|
388
|
+
return parsePackageElementId(elementId);
|
|
389
|
+
}
|
|
390
|
+
const runtimeMatch = RUNTIME_ID_PATTERN.exec(elementId);
|
|
391
|
+
if (runtimeMatch) {
|
|
392
|
+
return {
|
|
393
|
+
kind: "runtime",
|
|
394
|
+
elementId,
|
|
395
|
+
runtimeId: runtimeMatch[1]
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
kind: "invalid",
|
|
400
|
+
elementId,
|
|
401
|
+
reason: "unsupported elementId format"
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function parsePackageElementId(elementId) {
|
|
405
|
+
const value = elementId.slice(PACKAGE_ID_PREFIX.length);
|
|
406
|
+
const parts = value.split("/").filter(Boolean);
|
|
407
|
+
if (parts.length < 2) {
|
|
408
|
+
return {
|
|
409
|
+
kind: "invalid",
|
|
410
|
+
elementId,
|
|
411
|
+
reason: "package elementId must include packageName and entryFile"
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const scoped = parts[0]?.startsWith("@");
|
|
415
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
416
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
417
|
+
if (!entryFile) {
|
|
418
|
+
return {
|
|
419
|
+
kind: "invalid",
|
|
420
|
+
elementId,
|
|
421
|
+
reason: "package elementId must include entryFile"
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return {
|
|
425
|
+
kind: "package",
|
|
426
|
+
elementId,
|
|
427
|
+
packageName,
|
|
428
|
+
entryFile
|
|
429
|
+
};
|
|
430
|
+
}
|
|
102
431
|
|
|
103
432
|
// src/shared/sanitize.ts
|
|
104
433
|
function truncateText(text, maxLength) {
|
|
@@ -123,6 +452,270 @@ function maskHeaders(headers = {}, maskNames = []) {
|
|
|
123
452
|
);
|
|
124
453
|
}
|
|
125
454
|
|
|
455
|
+
// src/runtime/domSnapshot.ts
|
|
456
|
+
function createDomSnapshot(root, options) {
|
|
457
|
+
let count = 0;
|
|
458
|
+
function visit(node, depth) {
|
|
459
|
+
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
463
|
+
return createTextSnapshot(node, options, () => {
|
|
464
|
+
count += 1;
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
if (!(node instanceof Element)) {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const tag = node.tagName.toLowerCase();
|
|
471
|
+
if (["script", "style", "noscript"].includes(tag) || isInternalMcpElement(node)) {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
count += 1;
|
|
475
|
+
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
476
|
+
}
|
|
477
|
+
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
478
|
+
}
|
|
479
|
+
function queryDomElements(selector, limit) {
|
|
480
|
+
return Array.from(document.querySelectorAll(selector)).filter((element) => !isInternalMcpElement(element)).slice(0, limit).map((element) => createDomElementSummary(element));
|
|
481
|
+
}
|
|
482
|
+
function createDomElementSummary(element) {
|
|
483
|
+
return {
|
|
484
|
+
tag: element.tagName.toLowerCase(),
|
|
485
|
+
text: element.textContent.trim(),
|
|
486
|
+
attrs: collectAttrs(element),
|
|
487
|
+
rect: serializeRect(element.getBoundingClientRect())
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function createTextSnapshot(node, options, markVisited) {
|
|
491
|
+
const text = node.textContent?.trim();
|
|
492
|
+
if (!text) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
markVisited();
|
|
496
|
+
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
497
|
+
}
|
|
498
|
+
function createElementSnapshot(node, tag, visitChild) {
|
|
499
|
+
const attrs = collectAttrs(node);
|
|
500
|
+
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
501
|
+
return {
|
|
502
|
+
tag,
|
|
503
|
+
...node.textContent.trim() ? { text: node.textContent.trim() } : {},
|
|
504
|
+
...Object.keys(attrs).length ? { attrs } : {},
|
|
505
|
+
...children.length ? { children } : {}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function collectAttrs(element) {
|
|
509
|
+
const attrs = {};
|
|
510
|
+
for (const attr of Array.from(element.attributes)) {
|
|
511
|
+
attrs[attr.name] = attr.value;
|
|
512
|
+
}
|
|
513
|
+
if (typeof HTMLInputElement !== "undefined" && element instanceof HTMLInputElement && element.type === "password") {
|
|
514
|
+
attrs.value = "[masked]";
|
|
515
|
+
}
|
|
516
|
+
return attrs;
|
|
517
|
+
}
|
|
518
|
+
function serializeRect(rect) {
|
|
519
|
+
return {
|
|
520
|
+
x: rect.x,
|
|
521
|
+
y: rect.y,
|
|
522
|
+
width: rect.width,
|
|
523
|
+
height: rect.height,
|
|
524
|
+
top: rect.top,
|
|
525
|
+
right: rect.right,
|
|
526
|
+
bottom: rect.bottom,
|
|
527
|
+
left: rect.left
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function isInternalMcpElement(element) {
|
|
531
|
+
return element.getAttribute("data-v-mcp-internal") === "true";
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// src/runtime/vueComponentLocator.ts
|
|
535
|
+
function locateVueComponentForElement(element, root) {
|
|
536
|
+
const component = findNearestComponent(element);
|
|
537
|
+
if (!component) {
|
|
538
|
+
return void 0;
|
|
539
|
+
}
|
|
540
|
+
const file = getComponentFile(component);
|
|
541
|
+
const name = getComponentName(component);
|
|
542
|
+
if (!file) {
|
|
543
|
+
return { name };
|
|
544
|
+
}
|
|
545
|
+
const packageLocation = parseNodeModulesFile(file);
|
|
546
|
+
if (packageLocation) {
|
|
547
|
+
return {
|
|
548
|
+
name,
|
|
549
|
+
source: void 0,
|
|
550
|
+
packageLocation
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
return {
|
|
554
|
+
name,
|
|
555
|
+
source: {
|
|
556
|
+
file: createProjectRelativePath(root, file)
|
|
557
|
+
},
|
|
558
|
+
packageLocation: void 0
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
function findNearestComponent(element) {
|
|
562
|
+
let current = element;
|
|
563
|
+
while (current) {
|
|
564
|
+
const component = current.__vueParentComponent;
|
|
565
|
+
if (isVueRuntimeComponent(component)) {
|
|
566
|
+
return component;
|
|
567
|
+
}
|
|
568
|
+
current = current.parentElement;
|
|
569
|
+
}
|
|
570
|
+
return void 0;
|
|
571
|
+
}
|
|
572
|
+
function getComponentName(component) {
|
|
573
|
+
return component.type?.name ?? component.type?.__name ?? component.type?.displayName;
|
|
574
|
+
}
|
|
575
|
+
function getComponentFile(component) {
|
|
576
|
+
return component.type?.__file;
|
|
577
|
+
}
|
|
578
|
+
function parseNodeModulesFile(file) {
|
|
579
|
+
const normalized = normalizePath(file);
|
|
580
|
+
const marker = "/node_modules/";
|
|
581
|
+
const index = normalized.lastIndexOf(marker);
|
|
582
|
+
if (index < 0) {
|
|
583
|
+
return void 0;
|
|
584
|
+
}
|
|
585
|
+
const parts = normalized.slice(index + marker.length).split("/").filter(Boolean);
|
|
586
|
+
const scoped = parts[0]?.startsWith("@");
|
|
587
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
588
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
589
|
+
if (!packageName || !entryFile) {
|
|
590
|
+
return void 0;
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
packageName,
|
|
594
|
+
entryFile
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
function normalizePath(path) {
|
|
598
|
+
return path.replace(/\\/g, "/");
|
|
599
|
+
}
|
|
600
|
+
function createProjectRelativePath(root, file) {
|
|
601
|
+
const normalizedRoot = normalizePath(root).replace(/\/$/, "");
|
|
602
|
+
const normalizedFile = normalizePath(file);
|
|
603
|
+
const prefix = `${normalizedRoot}/`;
|
|
604
|
+
if (normalizedFile.startsWith(prefix)) {
|
|
605
|
+
return normalizedFile.slice(prefix.length);
|
|
606
|
+
}
|
|
607
|
+
return normalizedFile.replace(/^\//, "");
|
|
608
|
+
}
|
|
609
|
+
function isVueRuntimeComponent(value) {
|
|
610
|
+
return Boolean(value && typeof value === "object" && "type" in value);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/runtime/elementContext.ts
|
|
614
|
+
var activeResolver;
|
|
615
|
+
function createElementContextResolver(options) {
|
|
616
|
+
return {
|
|
617
|
+
getElementContext(elementId) {
|
|
618
|
+
const parsed = parseElementId(elementId);
|
|
619
|
+
if (parsed.kind === "project-source") {
|
|
620
|
+
const element = options.querySelector(
|
|
621
|
+
`[data-v-mcp-id="${escapeSelector(elementId)}"]`
|
|
622
|
+
);
|
|
623
|
+
return createProjectSourceContext(parsed, element, options.root);
|
|
624
|
+
}
|
|
625
|
+
if (parsed.kind === "package") {
|
|
626
|
+
return createPackageContext(parsed);
|
|
627
|
+
}
|
|
628
|
+
if (parsed.kind === "runtime") {
|
|
629
|
+
const record = options.registry.get(elementId);
|
|
630
|
+
if (!record) {
|
|
631
|
+
return createMissingRuntimeElementError(elementId);
|
|
632
|
+
}
|
|
633
|
+
return createRuntimeContext(elementId, record.element, options.root);
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
ok: false,
|
|
637
|
+
error: parsed.reason,
|
|
638
|
+
elementId,
|
|
639
|
+
limitations: ["please provide a copied elementId from the element picker"]
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
function setElementContextResolver(resolver) {
|
|
645
|
+
activeResolver = resolver;
|
|
646
|
+
}
|
|
647
|
+
function getElementContextResolver() {
|
|
648
|
+
activeResolver ??= createElementContextResolver({
|
|
649
|
+
root: "/",
|
|
650
|
+
registry: runtimeElementRegistry,
|
|
651
|
+
querySelector(selector) {
|
|
652
|
+
return document.querySelector(selector);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
return activeResolver;
|
|
656
|
+
}
|
|
657
|
+
function createProjectSourceContext(parsed, element, root) {
|
|
658
|
+
return {
|
|
659
|
+
ok: true,
|
|
660
|
+
elementId: parsed.elementId,
|
|
661
|
+
editable: true,
|
|
662
|
+
codeLocation: {
|
|
663
|
+
file: parsed.file,
|
|
664
|
+
line: parsed.line,
|
|
665
|
+
column: parsed.column
|
|
666
|
+
},
|
|
667
|
+
...element ? { component: locateVueComponentForElement(element, root) } : {},
|
|
668
|
+
...element ? { dom: createDomElementSummary(element) } : {},
|
|
669
|
+
limitations: element ? [] : ["runtime DOM element was not found"]
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function createPackageContext(parsed) {
|
|
673
|
+
return {
|
|
674
|
+
ok: true,
|
|
675
|
+
elementId: parsed.elementId,
|
|
676
|
+
editable: false,
|
|
677
|
+
packageLocation: {
|
|
678
|
+
packageName: parsed.packageName,
|
|
679
|
+
entryFile: parsed.entryFile
|
|
680
|
+
},
|
|
681
|
+
limitations: ["third-party package source is not editable from this project"]
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function createRuntimeContext(elementId, element, root) {
|
|
685
|
+
const component = locateVueComponentForElement(element, root);
|
|
686
|
+
const sourceFile = component?.source?.file;
|
|
687
|
+
return {
|
|
688
|
+
ok: true,
|
|
689
|
+
elementId,
|
|
690
|
+
editable: Boolean(sourceFile),
|
|
691
|
+
...sourceFile ? { codeLocation: { file: sourceFile, line: 1, column: 1 } } : {},
|
|
692
|
+
component,
|
|
693
|
+
dom: createDomElementSummary(element),
|
|
694
|
+
limitations: sourceFile ? ["runtime id maps to nearest component file, exact template node is unavailable"] : ["runtime id is only valid during the current page lifecycle"]
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function createMissingRuntimeElementError(elementId) {
|
|
698
|
+
return {
|
|
699
|
+
ok: false,
|
|
700
|
+
error: "element not found",
|
|
701
|
+
elementId,
|
|
702
|
+
limitations: [
|
|
703
|
+
"element was removed or page refreshed",
|
|
704
|
+
"please ask the user to pick the element again"
|
|
705
|
+
]
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function escapeSelector(value) {
|
|
709
|
+
const css = globalThis.CSS;
|
|
710
|
+
if (css?.escape) {
|
|
711
|
+
return css.escape(value);
|
|
712
|
+
}
|
|
713
|
+
return value.replace(/["\\]/g, "\\$&");
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/runtime/networkHook.ts
|
|
717
|
+
var import_nanoid3 = require("nanoid");
|
|
718
|
+
|
|
126
719
|
// src/shared/url.ts
|
|
127
720
|
function parseRequestQuery(url) {
|
|
128
721
|
const parsed = new URL(url, "http://vite-plugin-vue-mcp-next.local");
|
|
@@ -152,7 +745,7 @@ function safeUrlPathname(url) {
|
|
|
152
745
|
// src/runtime/networkHook.ts
|
|
153
746
|
function createHookNetworkRecord(input) {
|
|
154
747
|
return {
|
|
155
|
-
id: (0,
|
|
748
|
+
id: (0, import_nanoid3.nanoid)(),
|
|
156
749
|
pageId: input.pageId,
|
|
157
750
|
source: "hook",
|
|
158
751
|
url: input.url,
|
|
@@ -307,12 +900,12 @@ function safeReadXhrResponseText(xhr) {
|
|
|
307
900
|
}
|
|
308
901
|
|
|
309
902
|
// src/runtime/pageIdentity.ts
|
|
310
|
-
var
|
|
903
|
+
var import_nanoid4 = require("nanoid");
|
|
311
904
|
var RUNTIME_CLIENT_ID_STORAGE_KEY = "vite-plugin-vue-mcp-next:runtime-client-id";
|
|
312
905
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_PREFIX = "vite-plugin-vue-mcp-next:runtime-client-id=";
|
|
313
906
|
var RUNTIME_CLIENT_ID_WINDOW_NAME_SEPARATOR = "\n";
|
|
314
907
|
function createRuntimeClientId() {
|
|
315
|
-
return `runtime-client-${(0,
|
|
908
|
+
return `runtime-client-${(0, import_nanoid4.nanoid)()}`;
|
|
316
909
|
}
|
|
317
910
|
function readRuntimeClientIdFromTabScope(tabScope) {
|
|
318
911
|
if (!tabScope) {
|
|
@@ -361,7 +954,7 @@ function getRuntimeClientId(storage, tabScope) {
|
|
|
361
954
|
}
|
|
362
955
|
}
|
|
363
956
|
function createRuntimePageId() {
|
|
364
|
-
return `runtime-${(0,
|
|
957
|
+
return `runtime-${(0, import_nanoid4.nanoid)()}`;
|
|
365
958
|
}
|
|
366
959
|
function getRuntimePageIdentity(input) {
|
|
367
960
|
return {
|
|
@@ -381,102 +974,7 @@ function getRuntimePageIdentity(input) {
|
|
|
381
974
|
}
|
|
382
975
|
|
|
383
976
|
// src/runtime/performanceHook.ts
|
|
384
|
-
var
|
|
385
|
-
|
|
386
|
-
// src/shared/limits.ts
|
|
387
|
-
var DEFAULT_DOM_MAX_DEPTH = 8;
|
|
388
|
-
var DEFAULT_DOM_MAX_NODES = 2e3;
|
|
389
|
-
var DEFAULT_DOM_MAX_TEXT_LENGTH = 300;
|
|
390
|
-
var DEFAULT_CONSOLE_MAX_RECORDS = 1e3;
|
|
391
|
-
var DEFAULT_NETWORK_MAX_RECORDS = 500;
|
|
392
|
-
var DEFAULT_NETWORK_MAX_BODY_SIZE = 1e5;
|
|
393
|
-
var DEFAULT_MASK_HEADERS = [
|
|
394
|
-
"authorization",
|
|
395
|
-
"cookie",
|
|
396
|
-
"set-cookie"
|
|
397
|
-
];
|
|
398
|
-
|
|
399
|
-
// src/constants.ts
|
|
400
|
-
var DEFAULT_MCP_PATH = "/__mcp";
|
|
401
|
-
var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
|
|
402
|
-
var DEFAULT_SCREENSHOT_SAVE_DIR = ".vite-mcp/screenshot";
|
|
403
|
-
var DEFAULT_PERFORMANCE_SAVE_DIR = ".vite-mcp/performance";
|
|
404
|
-
var DEFAULT_PERFORMANCE_MAX_DURATION_MS = 3e4;
|
|
405
|
-
var DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS = 250;
|
|
406
|
-
var DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS = 50;
|
|
407
|
-
var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
408
|
-
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
409
|
-
var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
|
|
410
|
-
var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
|
|
411
|
-
var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
|
|
412
|
-
var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
413
|
-
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
414
|
-
var DEFAULT_OPTIONS = {
|
|
415
|
-
mcpPath: DEFAULT_MCP_PATH,
|
|
416
|
-
host: "localhost",
|
|
417
|
-
printUrl: true,
|
|
418
|
-
updateCursorMcpJson: {
|
|
419
|
-
enabled: true,
|
|
420
|
-
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
421
|
-
},
|
|
422
|
-
mcpClients: {
|
|
423
|
-
cursor: true,
|
|
424
|
-
codex: true,
|
|
425
|
-
claudeCode: true,
|
|
426
|
-
trae: true,
|
|
427
|
-
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
428
|
-
},
|
|
429
|
-
skill: {
|
|
430
|
-
autoConfig: true
|
|
431
|
-
},
|
|
432
|
-
runtime: {
|
|
433
|
-
mode: "auto",
|
|
434
|
-
evaluate: {
|
|
435
|
-
enabled: false,
|
|
436
|
-
timeoutMs: 3e3
|
|
437
|
-
}
|
|
438
|
-
},
|
|
439
|
-
cdp: {},
|
|
440
|
-
network: {
|
|
441
|
-
mode: "auto",
|
|
442
|
-
maxRecords: DEFAULT_NETWORK_MAX_RECORDS,
|
|
443
|
-
captureRequestBody: true,
|
|
444
|
-
captureResponseBody: true,
|
|
445
|
-
maxBodySize: DEFAULT_NETWORK_MAX_BODY_SIZE,
|
|
446
|
-
maskHeaders: [...DEFAULT_MASK_HEADERS]
|
|
447
|
-
},
|
|
448
|
-
dom: {
|
|
449
|
-
maxDepth: DEFAULT_DOM_MAX_DEPTH,
|
|
450
|
-
maxNodes: DEFAULT_DOM_MAX_NODES,
|
|
451
|
-
maxTextLength: DEFAULT_DOM_MAX_TEXT_LENGTH
|
|
452
|
-
},
|
|
453
|
-
console: {
|
|
454
|
-
maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
|
|
455
|
-
},
|
|
456
|
-
screenshot: {
|
|
457
|
-
type: "path",
|
|
458
|
-
saveDir: DEFAULT_SCREENSHOT_SAVE_DIR,
|
|
459
|
-
prefer: "auto",
|
|
460
|
-
maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
|
|
461
|
-
snapdom: {
|
|
462
|
-
options: {},
|
|
463
|
-
plugins: []
|
|
464
|
-
}
|
|
465
|
-
},
|
|
466
|
-
performance: {
|
|
467
|
-
mode: "auto",
|
|
468
|
-
maxDurationMs: DEFAULT_PERFORMANCE_MAX_DURATION_MS,
|
|
469
|
-
sampleIntervalMs: DEFAULT_PERFORMANCE_SAMPLE_INTERVAL_MS,
|
|
470
|
-
longTaskThresholdMs: DEFAULT_PERFORMANCE_LONG_TASK_THRESHOLD_MS,
|
|
471
|
-
saveDir: DEFAULT_PERFORMANCE_SAVE_DIR,
|
|
472
|
-
memory: {
|
|
473
|
-
enabled: true
|
|
474
|
-
},
|
|
475
|
-
stacks: {
|
|
476
|
-
enabled: true
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
};
|
|
977
|
+
var import_nanoid5 = require("nanoid");
|
|
480
978
|
|
|
481
979
|
// src/performance/summary.ts
|
|
482
980
|
function buildPerformanceSummary(input) {
|
|
@@ -650,7 +1148,7 @@ function createPerformanceCollector(deps) {
|
|
|
650
1148
|
};
|
|
651
1149
|
}
|
|
652
1150
|
function createRecordingId() {
|
|
653
|
-
return `performance-${(0,
|
|
1151
|
+
return `performance-${(0, import_nanoid5.nanoid)()}`;
|
|
654
1152
|
}
|
|
655
1153
|
function startSession(state, deps, options) {
|
|
656
1154
|
const recordingId = createRecordingId();
|
|
@@ -1018,78 +1516,6 @@ function createMissingSnapdomError() {
|
|
|
1018
1516
|
var import_devtools_kit = require("@vue/devtools-kit");
|
|
1019
1517
|
var import_vite_dev_rpc = require("vite-dev-rpc");
|
|
1020
1518
|
|
|
1021
|
-
// src/runtime/domSnapshot.ts
|
|
1022
|
-
function createDomSnapshot(root, options) {
|
|
1023
|
-
let count = 0;
|
|
1024
|
-
function visit(node, depth) {
|
|
1025
|
-
if (count >= options.maxNodes || depth > options.maxDepth) {
|
|
1026
|
-
return null;
|
|
1027
|
-
}
|
|
1028
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
1029
|
-
return createTextSnapshot(node, options, () => {
|
|
1030
|
-
count += 1;
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
if (!(node instanceof Element)) {
|
|
1034
|
-
return null;
|
|
1035
|
-
}
|
|
1036
|
-
const tag = node.tagName.toLowerCase();
|
|
1037
|
-
if (["script", "style", "noscript"].includes(tag)) {
|
|
1038
|
-
return null;
|
|
1039
|
-
}
|
|
1040
|
-
count += 1;
|
|
1041
|
-
return createElementSnapshot(node, tag, (child) => visit(child, depth + 1));
|
|
1042
|
-
}
|
|
1043
|
-
return visit(root, 0) ?? { tag: root.tagName.toLowerCase() };
|
|
1044
|
-
}
|
|
1045
|
-
function queryDomElements(selector, limit) {
|
|
1046
|
-
return Array.from(document.querySelectorAll(selector)).slice(0, limit).map((element) => ({
|
|
1047
|
-
tag: element.tagName.toLowerCase(),
|
|
1048
|
-
text: element.textContent.trim(),
|
|
1049
|
-
attrs: collectAttrs(element),
|
|
1050
|
-
rect: serializeRect(element.getBoundingClientRect())
|
|
1051
|
-
}));
|
|
1052
|
-
}
|
|
1053
|
-
function createTextSnapshot(node, options, markVisited) {
|
|
1054
|
-
const text = node.textContent?.trim();
|
|
1055
|
-
if (!text) {
|
|
1056
|
-
return null;
|
|
1057
|
-
}
|
|
1058
|
-
markVisited();
|
|
1059
|
-
return { tag: "#text", text: truncateText(text, options.maxTextLength).text };
|
|
1060
|
-
}
|
|
1061
|
-
function createElementSnapshot(node, tag, visitChild) {
|
|
1062
|
-
const attrs = collectAttrs(node);
|
|
1063
|
-
const children = Array.from(node.childNodes).map((child) => visitChild(child)).filter((child) => Boolean(child));
|
|
1064
|
-
return {
|
|
1065
|
-
tag,
|
|
1066
|
-
...Object.keys(attrs).length ? { attrs } : {},
|
|
1067
|
-
...children.length ? { children } : {}
|
|
1068
|
-
};
|
|
1069
|
-
}
|
|
1070
|
-
function collectAttrs(element) {
|
|
1071
|
-
const attrs = {};
|
|
1072
|
-
for (const attr of Array.from(element.attributes)) {
|
|
1073
|
-
attrs[attr.name] = attr.value;
|
|
1074
|
-
}
|
|
1075
|
-
if (element instanceof HTMLInputElement && element.type === "password") {
|
|
1076
|
-
attrs.value = "[masked]";
|
|
1077
|
-
}
|
|
1078
|
-
return attrs;
|
|
1079
|
-
}
|
|
1080
|
-
function serializeRect(rect) {
|
|
1081
|
-
return {
|
|
1082
|
-
x: rect.x,
|
|
1083
|
-
y: rect.y,
|
|
1084
|
-
width: rect.width,
|
|
1085
|
-
height: rect.height,
|
|
1086
|
-
top: rect.top,
|
|
1087
|
-
right: rect.right,
|
|
1088
|
-
bottom: rect.bottom,
|
|
1089
|
-
left: rect.left
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
1519
|
// src/runtime/evaluateExpression.ts
|
|
1094
1520
|
async function evaluateExpression(request) {
|
|
1095
1521
|
const value = runExpression(request.expression);
|
|
@@ -1517,6 +1943,22 @@ function createRuntimeDevtoolsRpc(getRpc) {
|
|
|
1517
1943
|
);
|
|
1518
1944
|
},
|
|
1519
1945
|
onDomQueryUpdated: () => void 0,
|
|
1946
|
+
getElementContext(options) {
|
|
1947
|
+
try {
|
|
1948
|
+
getRpc().onElementContextUpdated(
|
|
1949
|
+
options.event,
|
|
1950
|
+
getElementContextResolver().getElementContext(options.elementId)
|
|
1951
|
+
);
|
|
1952
|
+
} catch (error) {
|
|
1953
|
+
getRpc().onElementContextUpdated(options.event, {
|
|
1954
|
+
ok: false,
|
|
1955
|
+
elementId: options.elementId,
|
|
1956
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1957
|
+
limitations: ["runtime element context lookup failed"]
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
},
|
|
1961
|
+
onElementContextUpdated: () => void 0,
|
|
1520
1962
|
reloadPage(options) {
|
|
1521
1963
|
getRpc().onPageReloaded(options.event, { ok: true, source: "hook" });
|
|
1522
1964
|
setTimeout(() => {
|
|
@@ -1853,13 +2295,16 @@ function createMissingComponentError(componentName) {
|
|
|
1853
2295
|
}
|
|
1854
2296
|
|
|
1855
2297
|
// src/runtime/client.ts
|
|
1856
|
-
async function startRuntimeClient(
|
|
2298
|
+
async function startRuntimeClient(runtimeOptions = {
|
|
2299
|
+
elementPicker: DEFAULT_OPTIONS.elementPicker
|
|
2300
|
+
}) {
|
|
1857
2301
|
initializeVueDevtoolsHook();
|
|
1858
2302
|
const hot = await (0, import_vite_hot_client.createHotContext)("vite-plugin-vue-mcp-next", "/");
|
|
1859
2303
|
if (!hot) {
|
|
1860
2304
|
return;
|
|
1861
2305
|
}
|
|
1862
2306
|
installVueBridge(hot);
|
|
2307
|
+
installElementPicker(runtimeOptions.elementPicker);
|
|
1863
2308
|
const identity = getRuntimePageIdentity({
|
|
1864
2309
|
href: window.location.href,
|
|
1865
2310
|
title: document.title,
|
|
@@ -1868,7 +2313,20 @@ async function startRuntimeClient() {
|
|
|
1868
2313
|
innerHeight: window.innerHeight,
|
|
1869
2314
|
readyState: document.readyState
|
|
1870
2315
|
});
|
|
1871
|
-
|
|
2316
|
+
setElementContextResolver(
|
|
2317
|
+
createElementContextResolver({
|
|
2318
|
+
root: runtimeOptions.projectRoot ?? "/",
|
|
2319
|
+
registry: runtimeElementRegistry,
|
|
2320
|
+
querySelector(selector) {
|
|
2321
|
+
return document.querySelector(selector);
|
|
2322
|
+
}
|
|
2323
|
+
})
|
|
2324
|
+
);
|
|
2325
|
+
hot.send(RUNTIME_PAGE_CONNECTED_EVENT, identity);
|
|
2326
|
+
installRuntimePageLifecycle({
|
|
2327
|
+
pageId: identity.pageId,
|
|
2328
|
+
send: hot.send.bind(hot)
|
|
2329
|
+
});
|
|
1872
2330
|
installPerformanceHook({
|
|
1873
2331
|
pageId: identity.pageId,
|
|
1874
2332
|
send(report) {
|
|
@@ -1890,6 +2348,27 @@ async function startRuntimeClient() {
|
|
|
1890
2348
|
}
|
|
1891
2349
|
});
|
|
1892
2350
|
}
|
|
2351
|
+
function installRuntimePageLifecycle(options) {
|
|
2352
|
+
let disconnected = false;
|
|
2353
|
+
const heartbeatTimer = setInterval(() => {
|
|
2354
|
+
options.send(RUNTIME_PAGE_HEARTBEAT_EVENT, {
|
|
2355
|
+
pageId: options.pageId,
|
|
2356
|
+
timestamp: Date.now()
|
|
2357
|
+
});
|
|
2358
|
+
}, DEFAULT_RUNTIME_PAGE_HEARTBEAT_INTERVAL_MS);
|
|
2359
|
+
const disconnect = () => {
|
|
2360
|
+
if (disconnected) {
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
disconnected = true;
|
|
2364
|
+
clearInterval(heartbeatTimer);
|
|
2365
|
+
options.send(RUNTIME_PAGE_DISCONNECTED_EVENT, {
|
|
2366
|
+
pageId: options.pageId
|
|
2367
|
+
});
|
|
2368
|
+
};
|
|
2369
|
+
window.addEventListener("pagehide", disconnect, { once: true });
|
|
2370
|
+
window.addEventListener("beforeunload", disconnect, { once: true });
|
|
2371
|
+
}
|
|
1893
2372
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1894
2373
|
0 && (module.exports = {
|
|
1895
2374
|
evaluateExpression,
|