@vite-plugin-opencode-assistant/components 1.0.18 → 1.0.20
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/es/index.d.ts +1 -1
- package/es/index.js +1 -1
- package/es/open-code-widget/composables/use-inspector.js +253 -35
- package/lib/@vite-plugin-opencode-assistant/components.cjs.js +242 -31
- package/lib/@vite-plugin-opencode-assistant/components.es.js +219 -31
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/open-code-widget/composables/use-inspector.js +263 -35
- package/lib/web-types.json +1 -1
- package/package.json +3 -2
package/es/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import OpenCodeWidget from './open-code-widget';
|
|
2
2
|
import type { App } from 'vue';
|
|
3
|
-
declare const version = "1.0.
|
|
3
|
+
declare const version = "1.0.20";
|
|
4
4
|
declare function install(app: App<any>, options?: any): void;
|
|
5
5
|
export { install, version, OpenCodeWidget };
|
|
6
6
|
export default install;
|
package/es/index.js
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { ref, watch, onMounted, onUnmounted } from "vue";
|
|
2
2
|
import { truncate } from "@vite-plugin-opencode-assistant/shared";
|
|
3
|
+
import getCssSelector from "css-selector-generator";
|
|
4
|
+
function throttle(fn, delay) {
|
|
5
|
+
let lastCall = 0;
|
|
6
|
+
let rafId = null;
|
|
7
|
+
return ((...args) => {
|
|
8
|
+
const now = performance.now();
|
|
9
|
+
if (now - lastCall >= delay) {
|
|
10
|
+
lastCall = now;
|
|
11
|
+
fn(...args);
|
|
12
|
+
} else if (!rafId) {
|
|
13
|
+
rafId = requestAnimationFrame(() => {
|
|
14
|
+
rafId = null;
|
|
15
|
+
lastCall = performance.now();
|
|
16
|
+
fn(...args);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
3
21
|
function getDirectText(element) {
|
|
4
22
|
let text = "";
|
|
5
23
|
for (let i = 0; i < element.childNodes.length; i++) {
|
|
@@ -10,24 +28,178 @@ function getDirectText(element) {
|
|
|
10
28
|
}
|
|
11
29
|
return text.trim();
|
|
12
30
|
}
|
|
31
|
+
const DYNAMIC_ID_PATTERN = /^(?:el-|:r[0-9]+:|radix-|uid-|ts-|uuid-|id-[a-f0-9]{4,}|.*[0-9]{4,}.*|.*-[a-f0-9]{6,}$)/i;
|
|
32
|
+
const STATE_CLASS_PATTERN = /^(?:hover|active|focus|focus-visible|focus-within|disabled|enabled|checked|selected|open|closed|loading|error|success|warning|hidden|visible|show|hide|current|expanded|collapsed|pressed|dragging|droppable|sortable|placeholder|transition|enter|leave|appear|move)$/i;
|
|
33
|
+
const STATE_CLASS_PREFIX_PATTERN = /^(?:is-|has-|was-|are-|can-|should-|will-|did-|does-|on-|off-|in-|out-|at-|to-|from-)/i;
|
|
34
|
+
function isDynamicId(id) {
|
|
35
|
+
if (!id) return false;
|
|
36
|
+
if (DYNAMIC_ID_PATTERN.test(id)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const digitCount = (id.match(/\d/g) || []).length;
|
|
40
|
+
const letterCount = (id.match(/[a-zA-Z]/g) || []).length;
|
|
41
|
+
if (digitCount > letterCount && digitCount >= 3) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
const dashParts = id.split("-");
|
|
45
|
+
if (dashParts.length >= 3) {
|
|
46
|
+
const lastPart = dashParts[dashParts.length - 1];
|
|
47
|
+
if (/^\d+$/.test(lastPart) || /^[a-f0-9]{4,}$/i.test(lastPart)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
function isStateClass(className) {
|
|
54
|
+
if (!className) return false;
|
|
55
|
+
if (STATE_CLASS_PATTERN.test(className)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (STATE_CLASS_PREFIX_PATTERN.test(className)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (className.includes("-active") || className.includes("-hover") || className.includes("-focus")) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (/^(?:router-link|nuxt-link)/.test(className)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function filterStateClasses(classes) {
|
|
70
|
+
return classes.filter((cls) => !isStateClass(cls));
|
|
71
|
+
}
|
|
13
72
|
function getElementDescription(element) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
73
|
+
try {
|
|
74
|
+
const selector = getCssSelector(element, {
|
|
75
|
+
selectors: ["id", "class", "tag", "nthchild"],
|
|
76
|
+
combineWithinSelector: true,
|
|
77
|
+
combineBetweenSelectors: true,
|
|
78
|
+
maxCombinations: 100,
|
|
79
|
+
maxCandidates: 100,
|
|
80
|
+
blacklist: [
|
|
81
|
+
(selectorValue) => {
|
|
82
|
+
const idMatch = selectorValue.match(/^#(.+)$/);
|
|
83
|
+
if (idMatch) {
|
|
84
|
+
return isDynamicId(idMatch[1]);
|
|
85
|
+
}
|
|
86
|
+
const classMatch = selectorValue.match(/^\.([a-zA-Z_-][\w-]*)$/);
|
|
87
|
+
if (classMatch) {
|
|
88
|
+
return isStateClass(classMatch[1]);
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
return selector;
|
|
95
|
+
} catch (e) {
|
|
96
|
+
const tag = element.tagName.toLowerCase();
|
|
97
|
+
const parts = [tag];
|
|
98
|
+
const id = element.id;
|
|
99
|
+
if (id && !isDynamicId(id)) parts.push(`#${id}`);
|
|
100
|
+
const className = element.className;
|
|
101
|
+
if (typeof className === "string") {
|
|
102
|
+
const classes = filterStateClasses(className.trim().split(/\s+/).filter(Boolean)).slice(0, 2);
|
|
103
|
+
if (classes.length > 0) parts.push(`.${classes.join(".")}`);
|
|
104
|
+
} else {
|
|
105
|
+
const svgClass = className.baseVal;
|
|
106
|
+
if (svgClass) {
|
|
107
|
+
const classes = filterStateClasses(svgClass.trim().split(/\s+/).filter(Boolean)).slice(
|
|
108
|
+
0,
|
|
109
|
+
2
|
|
110
|
+
);
|
|
111
|
+
if (classes.length > 0) parts.push(`.${classes.join(".")}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const name = element.getAttribute("name");
|
|
115
|
+
if (name) parts.push(`[name="${name}"]`);
|
|
116
|
+
const placeholder = element.getAttribute("placeholder");
|
|
117
|
+
if (placeholder) parts.push(`[placeholder="${placeholder.substring(0, 20)}"]`);
|
|
118
|
+
const src = element.getAttribute("src");
|
|
119
|
+
if (src) parts.push(`[src]`);
|
|
120
|
+
const href = element.getAttribute("href");
|
|
121
|
+
if (href && href !== "#") parts.push(`[href]`);
|
|
122
|
+
return parts.join("");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function getFileInfoFromAttributes(element) {
|
|
126
|
+
const file = element.getAttribute("data-v-inspector-file");
|
|
127
|
+
if (file) {
|
|
128
|
+
const line = element.getAttribute("data-v-inspector-line");
|
|
129
|
+
const column = element.getAttribute("data-v-inspector-column");
|
|
130
|
+
return {
|
|
131
|
+
file,
|
|
132
|
+
line: line ? parseInt(line, 10) : null,
|
|
133
|
+
column: column ? parseInt(column, 10) : null
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function getFileInfoFromVueInstance(element) {
|
|
139
|
+
var _a, _b, _c, _d;
|
|
140
|
+
const vue3Instance = element.__vueParentComponent;
|
|
141
|
+
if (vue3Instance) {
|
|
142
|
+
let current = vue3Instance;
|
|
143
|
+
while (current) {
|
|
144
|
+
const file = ((_a = current.type) == null ? void 0 : _a.__file) || ((_c = (_b = current.vnode) == null ? void 0 : _b.type) == null ? void 0 : _c.__file);
|
|
145
|
+
if (file) {
|
|
146
|
+
return { file, line: null, column: null };
|
|
147
|
+
}
|
|
148
|
+
current = current.parent;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const vue2Instance = element.__vue__;
|
|
152
|
+
if (vue2Instance) {
|
|
153
|
+
let current = vue2Instance;
|
|
154
|
+
while (current) {
|
|
155
|
+
const file = (_d = current.$options) == null ? void 0 : _d.__file;
|
|
156
|
+
if (file) {
|
|
157
|
+
return { file, line: null, column: null };
|
|
158
|
+
}
|
|
159
|
+
current = current.$parent;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function findFileInfo(element, inspector) {
|
|
165
|
+
var _a, _b;
|
|
166
|
+
let current = element;
|
|
167
|
+
let fallbackFileInfo = null;
|
|
168
|
+
while (current) {
|
|
169
|
+
const attrInfo = getFileInfoFromAttributes(current);
|
|
170
|
+
if (attrInfo && attrInfo.line !== null) {
|
|
171
|
+
return attrInfo;
|
|
172
|
+
}
|
|
173
|
+
if (attrInfo && !fallbackFileInfo) {
|
|
174
|
+
fallbackFileInfo = attrInfo;
|
|
175
|
+
}
|
|
176
|
+
const fakeEvent = {
|
|
177
|
+
clientX: 0,
|
|
178
|
+
clientY: 0,
|
|
179
|
+
target: current,
|
|
180
|
+
currentTarget: current
|
|
181
|
+
};
|
|
182
|
+
const { params } = inspector.getTargetNode(fakeEvent);
|
|
183
|
+
if (params && params.file) {
|
|
184
|
+
const info = {
|
|
185
|
+
file: params.file,
|
|
186
|
+
line: (_a = params.line) != null ? _a : null,
|
|
187
|
+
column: (_b = params.column) != null ? _b : null
|
|
188
|
+
};
|
|
189
|
+
if (info.line !== null) {
|
|
190
|
+
return info;
|
|
191
|
+
}
|
|
192
|
+
if (!fallbackFileInfo) {
|
|
193
|
+
fallbackFileInfo = info;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const vueInfo = getFileInfoFromVueInstance(current);
|
|
197
|
+
if (vueInfo && !fallbackFileInfo) {
|
|
198
|
+
fallbackFileInfo = vueInfo;
|
|
199
|
+
}
|
|
200
|
+
current = current.parentElement;
|
|
201
|
+
}
|
|
202
|
+
return fallbackFileInfo || { file: null, line: null, column: null };
|
|
31
203
|
}
|
|
32
204
|
function getPreciseElementAtPoint(x, y, boundary) {
|
|
33
205
|
var _a, _b;
|
|
@@ -44,7 +216,12 @@ function getPreciseElementAtPoint(x, y, boundary) {
|
|
|
44
216
|
if (el.closest("#vue-inspector-container")) continue;
|
|
45
217
|
if (el.closest(".opencode-widget")) continue;
|
|
46
218
|
if (el.hasAttribute("data-v-inspector-ignore")) continue;
|
|
47
|
-
if (boundary
|
|
219
|
+
if (boundary) {
|
|
220
|
+
if (boundary.contains(el) || el === boundary) {
|
|
221
|
+
element = el;
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
48
225
|
element = el;
|
|
49
226
|
break;
|
|
50
227
|
}
|
|
@@ -68,14 +245,35 @@ function useInspector(options) {
|
|
|
68
245
|
const tooltipContent = ref({ description: "", fileInfo: "" });
|
|
69
246
|
const INSPECTOR_CHECK_INTERVAL = 500;
|
|
70
247
|
let inspectorCheckTimer = null;
|
|
71
|
-
function
|
|
248
|
+
function handleMouseMoveCore(e) {
|
|
249
|
+
var _a, _b;
|
|
72
250
|
if (!options.selectMode.value) return;
|
|
73
251
|
const inspector = window.__VUE_INSPECTOR__;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
const
|
|
78
|
-
|
|
252
|
+
let elementToHighlight = null;
|
|
253
|
+
let fileInfo = { file: null, line: null, column: null };
|
|
254
|
+
if (inspector) {
|
|
255
|
+
const { targetNode, params } = inspector.getTargetNode(e);
|
|
256
|
+
if (targetNode) {
|
|
257
|
+
const preciseElement = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode);
|
|
258
|
+
elementToHighlight = preciseElement || targetNode;
|
|
259
|
+
if (params && params.file) {
|
|
260
|
+
fileInfo = {
|
|
261
|
+
file: params.file,
|
|
262
|
+
line: (_a = params.line) != null ? _a : null,
|
|
263
|
+
column: (_b = params.column) != null ? _b : null
|
|
264
|
+
};
|
|
265
|
+
} else if (elementToHighlight) {
|
|
266
|
+
fileInfo = findFileInfo(elementToHighlight, inspector);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!elementToHighlight) {
|
|
271
|
+
elementToHighlight = getPreciseElementAtPoint(e.clientX, e.clientY, null);
|
|
272
|
+
}
|
|
273
|
+
if (elementToHighlight && !fileInfo.file) {
|
|
274
|
+
fileInfo = getFileInfoFromVueInstance(elementToHighlight) || fileInfo;
|
|
275
|
+
}
|
|
276
|
+
if (elementToHighlight) {
|
|
79
277
|
const rect = elementToHighlight.getBoundingClientRect();
|
|
80
278
|
const widget = document.querySelector(".opencode-widget");
|
|
81
279
|
let primary = "#3b82f6";
|
|
@@ -95,12 +293,12 @@ function useInspector(options) {
|
|
|
95
293
|
background: primaryBg
|
|
96
294
|
};
|
|
97
295
|
const description = getElementDescription(elementToHighlight);
|
|
98
|
-
const fileName =
|
|
296
|
+
const fileName = fileInfo.file ? fileInfo.file.split("/").pop() : "";
|
|
99
297
|
let lineInfo = "";
|
|
100
|
-
if (
|
|
101
|
-
lineInfo = `:${
|
|
102
|
-
if (
|
|
103
|
-
lineInfo += `:${
|
|
298
|
+
if (fileInfo.line) {
|
|
299
|
+
lineInfo = `:${fileInfo.line}`;
|
|
300
|
+
if (fileInfo.column) {
|
|
301
|
+
lineInfo += `:${fileInfo.column}`;
|
|
104
302
|
}
|
|
105
303
|
}
|
|
106
304
|
tooltipContent.value = {
|
|
@@ -127,23 +325,43 @@ function useInspector(options) {
|
|
|
127
325
|
tooltipVisible.value = false;
|
|
128
326
|
}
|
|
129
327
|
}
|
|
328
|
+
const handleMouseMove = throttle(handleMouseMoveCore, 16);
|
|
130
329
|
function setupInspectorHook() {
|
|
131
330
|
const inspector = window.__VUE_INSPECTOR__;
|
|
132
331
|
if (!inspector || inspector.__opencode_hooked) return;
|
|
133
332
|
const originalHandleClick = inspector.handleClick.bind(inspector);
|
|
134
333
|
inspector.handleClick = function(e) {
|
|
135
|
-
var _a, _b
|
|
334
|
+
var _a, _b;
|
|
136
335
|
if (options.selectMode.value) {
|
|
336
|
+
let elementToSelect = null;
|
|
337
|
+
let fileInfo = { file: null, line: null, column: null };
|
|
137
338
|
const { targetNode, params } = inspector.getTargetNode(e);
|
|
138
|
-
if (targetNode
|
|
339
|
+
if (targetNode) {
|
|
139
340
|
const preciseElement = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode);
|
|
140
|
-
|
|
341
|
+
elementToSelect = preciseElement || targetNode;
|
|
342
|
+
if (params && params.file) {
|
|
343
|
+
fileInfo = {
|
|
344
|
+
file: params.file,
|
|
345
|
+
line: (_a = params.line) != null ? _a : null,
|
|
346
|
+
column: (_b = params.column) != null ? _b : null
|
|
347
|
+
};
|
|
348
|
+
} else if (elementToSelect) {
|
|
349
|
+
fileInfo = findFileInfo(elementToSelect, inspector);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (!elementToSelect) {
|
|
353
|
+
elementToSelect = getPreciseElementAtPoint(e.clientX, e.clientY, null);
|
|
354
|
+
}
|
|
355
|
+
if (elementToSelect && !fileInfo.file) {
|
|
356
|
+
fileInfo = getFileInfoFromVueInstance(elementToSelect) || fileInfo;
|
|
357
|
+
}
|
|
358
|
+
if (elementToSelect) {
|
|
141
359
|
const innerText = getDirectText(elementToSelect);
|
|
142
360
|
const description = getElementDescription(elementToSelect);
|
|
143
361
|
const elementInfo = {
|
|
144
|
-
filePath:
|
|
145
|
-
line:
|
|
146
|
-
column:
|
|
362
|
+
filePath: fileInfo.file,
|
|
363
|
+
line: fileInfo.line,
|
|
364
|
+
column: fileInfo.column,
|
|
147
365
|
innerText: truncate(innerText, 200),
|
|
148
366
|
description
|
|
149
367
|
};
|