@vite-plugin-opencode-assistant/components 1.0.18 → 1.0.19

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 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.18";
3
+ declare const version = "1.0.19";
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,5 @@
1
1
  import OpenCodeWidget from "./open-code-widget";
2
- const version = "1.0.18";
2
+ const version = "1.0.19";
3
3
  function install(app, options) {
4
4
  const components = [
5
5
  OpenCodeWidget
@@ -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
- const tag = element.tagName.toLowerCase();
15
- const parts = [tag];
16
- const id = element.id;
17
- if (id) parts.push(`#${id}`);
18
- if (typeof element.className === "string") {
19
- const className = element.className.trim().split(/\s+/).filter(Boolean).slice(0, 2).join(".");
20
- if (className) parts.push(`.${className}`);
21
- }
22
- const name = element.getAttribute("name");
23
- if (name) parts.push(`[name="${name}"]`);
24
- const placeholder = element.getAttribute("placeholder");
25
- if (placeholder) parts.push(`[placeholder="${placeholder.substring(0, 20)}"]`);
26
- const src = element.getAttribute("src");
27
- if (src) parts.push(`[src]`);
28
- const href = element.getAttribute("href");
29
- if (href && href !== "#") parts.push(`[href]`);
30
- return parts.join("");
73
+ try {
74
+ const selector = getCssSelector(element, {
75
+ selectors: ["id", "class", "tag", "attribute", "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.contains(el) || el === 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 handleMouseMove(e) {
248
+ function handleMouseMoveCore(e) {
249
+ var _a, _b;
72
250
  if (!options.selectMode.value) return;
73
251
  const inspector = window.__VUE_INSPECTOR__;
74
- if (!inspector) return;
75
- const { targetNode, params } = inspector.getTargetNode(e);
76
- if (targetNode && params) {
77
- const preciseElement = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode);
78
- const elementToHighlight = preciseElement || targetNode;
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 = params.file ? params.file.split("/").pop() : "";
296
+ const fileName = fileInfo.file ? fileInfo.file.split("/").pop() : "";
99
297
  let lineInfo = "";
100
- if (params.line) {
101
- lineInfo = `:${params.line}`;
102
- if (params.column) {
103
- lineInfo += `:${params.column}`;
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, _c;
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 && params) {
339
+ if (targetNode) {
139
340
  const preciseElement = getPreciseElementAtPoint(e.clientX, e.clientY, targetNode);
140
- const elementToSelect = preciseElement || targetNode;
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: (_a = params.file) != null ? _a : null,
145
- line: (_b = params.line) != null ? _b : null,
146
- column: (_c = params.column) != null ? _c : null,
362
+ filePath: fileInfo.file,
363
+ line: fileInfo.line,
364
+ column: fileInfo.column,
147
365
  innerText: truncate(innerText, 200),
148
366
  description
149
367
  };