vite-plugin-vue-inspector-plus 1.0.0 → 1.0.1-alpha.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/LICENSE +21 -21
- package/README.md +36 -36
- package/dist/inspector/App.vue +443 -443
- package/package.json +48 -48
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024 varHarrie
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 varHarrie
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
# vite-plugin-vue-inspector-plus
|
|
2
|
-
|
|
3
|
-
<center>
|
|
4
|
-
<img src="./screenshot.png" />
|
|
5
|
-
<p>A Vite plugin that adds a shortcut dropdown menu for the Vue Inspector.</p>
|
|
6
|
-
</center>
|
|
7
|
-
|
|
8
|
-
## Installation
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install vite-plugin-vue-inspector-plus --save-dev
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Usage
|
|
15
|
-
|
|
16
|
-
This plugin needs to be used with [Vue Devtools](https://github.com/vuejs/devtools) or [Vue Inspector](https://github.com/webfansplz/vite-plugin-vue-inspector).
|
|
17
|
-
|
|
18
|
-
```js
|
|
19
|
-
// vite.config.js
|
|
20
|
-
|
|
21
|
-
import { defineConfig } from 'vite';
|
|
22
|
-
import VueDevTools from 'vite-plugin-vue-devtools';
|
|
23
|
-
import VueInspectorPlus from 'vite-plugin-vue-inspector-plus';
|
|
24
|
-
|
|
25
|
-
export default defineConfig({
|
|
26
|
-
plugins: [VueDevTools(), VueInspectorPlus()],
|
|
27
|
-
});
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Options
|
|
31
|
-
|
|
32
|
-
- modifierKey: `string` - The modifier key to show the inspector dropdown. Default: `'ctrl|meta'`.
|
|
33
|
-
|
|
34
|
-
## License
|
|
35
|
-
|
|
36
|
-
[MIT](./LICENSE)
|
|
1
|
+
# vite-plugin-vue-inspector-plus
|
|
2
|
+
|
|
3
|
+
<center>
|
|
4
|
+
<img src="./screenshot.png" />
|
|
5
|
+
<p>A Vite plugin that adds a shortcut dropdown menu for the Vue Inspector.</p>
|
|
6
|
+
</center>
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install vite-plugin-vue-inspector-plus --save-dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
This plugin needs to be used with [Vue Devtools](https://github.com/vuejs/devtools) or [Vue Inspector](https://github.com/webfansplz/vite-plugin-vue-inspector).
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// vite.config.js
|
|
20
|
+
|
|
21
|
+
import { defineConfig } from 'vite';
|
|
22
|
+
import VueDevTools from 'vite-plugin-vue-devtools';
|
|
23
|
+
import VueInspectorPlus from 'vite-plugin-vue-inspector-plus';
|
|
24
|
+
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
plugins: [VueDevTools(), VueInspectorPlus()],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Options
|
|
31
|
+
|
|
32
|
+
- modifierKey: `string` - The modifier key to show the inspector dropdown. Default: `'ctrl|meta'`.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
[MIT](./LICENSE)
|
package/dist/inspector/App.vue
CHANGED
|
@@ -1,443 +1,443 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div v-if="activeItemRectStyle" :class="$style.overlay" :style="activeItemRectStyle"></div>
|
|
3
|
-
<transition
|
|
4
|
-
:enter-active-class="$style['scale-fade-active']"
|
|
5
|
-
:leave-active-class="$style['scale-fade-active']"
|
|
6
|
-
:enter-from-class="$style['scale-fade-inactive']"
|
|
7
|
-
:leave-to-class="$style['scale-fade-inactive']"
|
|
8
|
-
>
|
|
9
|
-
<ul
|
|
10
|
-
v-if="stack.length"
|
|
11
|
-
ref="dropdownRef"
|
|
12
|
-
:class="$style.dropdown"
|
|
13
|
-
:style="{ top: `${position.top}px`, left: `${position.left}px` }"
|
|
14
|
-
@mouseleave="handleDropdownMouseLeave"
|
|
15
|
-
>
|
|
16
|
-
<li
|
|
17
|
-
:class="[
|
|
18
|
-
$style.item,
|
|
19
|
-
{
|
|
20
|
-
[$style.element]: item.type === 'element',
|
|
21
|
-
[$style.component]: item.type === 'component',
|
|
22
|
-
[$style.route]: item.route,
|
|
23
|
-
[$style.splitted]: item.file && item.location,
|
|
24
|
-
},
|
|
25
|
-
]"
|
|
26
|
-
v-for="(item, index) in stack"
|
|
27
|
-
:key="index"
|
|
28
|
-
:dataFile="item.file"
|
|
29
|
-
:dataLocation="item.location"
|
|
30
|
-
@mouseenter="handleItemMouseEnter(item)"
|
|
31
|
-
>
|
|
32
|
-
<span v-if="item.file" :class="$style.icon" @click="handleOpenLocation(item.file)">
|
|
33
|
-
<svg
|
|
34
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
35
|
-
width="24"
|
|
36
|
-
height="24"
|
|
37
|
-
viewBox="0 0 24 24"
|
|
38
|
-
fill="none"
|
|
39
|
-
stroke="currentColor"
|
|
40
|
-
stroke-width="2"
|
|
41
|
-
stroke-linecap="round"
|
|
42
|
-
stroke-linejoin="round"
|
|
43
|
-
>
|
|
44
|
-
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
45
|
-
<path
|
|
46
|
-
d="M21 16.008v-8.018a1.98 1.98 0 0 0 -1 -1.717l-7 -4.008a2.016 2.016 0 0 0 -2 0l-7 4.008c-.619 .355 -1 1.01 -1 1.718v8.018c0 .709 .381 1.363 1 1.717l7 4.008a2.016 2.016 0 0 0 2 0l7 -4.008c.619 -.355 1 -1.01 1 -1.718z"
|
|
47
|
-
/>
|
|
48
|
-
<path d="M12 22v-10" />
|
|
49
|
-
<path d="M12 12l8.73 -5.04" />
|
|
50
|
-
<path d="M3.27 6.96l8.73 5.04" />
|
|
51
|
-
</svg>
|
|
52
|
-
</span>
|
|
53
|
-
<span v-else :class="$style.icon" @click="handleOpenLocation(item.location)">
|
|
54
|
-
<svg
|
|
55
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
-
width="24"
|
|
57
|
-
height="24"
|
|
58
|
-
viewBox="0 0 24 24"
|
|
59
|
-
fill="none"
|
|
60
|
-
stroke="currentColor"
|
|
61
|
-
stroke-width="2"
|
|
62
|
-
stroke-linecap="round"
|
|
63
|
-
stroke-linejoin="round"
|
|
64
|
-
>
|
|
65
|
-
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
66
|
-
<circle cx="12" cy="12" r=".5" fill="currentColor" />
|
|
67
|
-
<path d="M12 12m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" />
|
|
68
|
-
<path d="M12 3l0 2" />
|
|
69
|
-
<path d="M3 12l2 0" />
|
|
70
|
-
<path d="M12 19l0 2" />
|
|
71
|
-
<path d="M19 12l2 0" />
|
|
72
|
-
</svg>
|
|
73
|
-
</span>
|
|
74
|
-
<div :class="$style.main" @click="handleOpenLocation(item.location || item.file)">
|
|
75
|
-
<span :class="$style.title">
|
|
76
|
-
{{ item.title || 'Unknown' }}
|
|
77
|
-
</span>
|
|
78
|
-
<span :class="$style.location">
|
|
79
|
-
{{ toRelativePath(item.location || item.file) }}
|
|
80
|
-
</span>
|
|
81
|
-
</div>
|
|
82
|
-
</li>
|
|
83
|
-
</ul>
|
|
84
|
-
</transition>
|
|
85
|
-
</template>
|
|
86
|
-
|
|
87
|
-
<script lang="ts">
|
|
88
|
-
declare global {
|
|
89
|
-
interface Window {
|
|
90
|
-
__VUE_INSPECTOR__?: {
|
|
91
|
-
openInEditor: (baseUrl: string, file: string, line: string, column: string) => void;
|
|
92
|
-
};
|
|
93
|
-
__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__: string;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
type VueInstance = {
|
|
98
|
-
vnode: {
|
|
99
|
-
el: HTMLElement;
|
|
100
|
-
props: Record<string, unknown>;
|
|
101
|
-
};
|
|
102
|
-
parent: VueInstance;
|
|
103
|
-
type: {
|
|
104
|
-
name?: string;
|
|
105
|
-
__name?: string;
|
|
106
|
-
__file?: string;
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
type VueHtmlElement = HTMLElement & {
|
|
111
|
-
__vueParentComponent: VueInstance;
|
|
112
|
-
__vnode: {
|
|
113
|
-
props: Record<string, unknown>;
|
|
114
|
-
};
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
type VueStackItem = {
|
|
118
|
-
type: 'element' | 'component';
|
|
119
|
-
el?: VueHtmlElement;
|
|
120
|
-
title?: string;
|
|
121
|
-
location?: string;
|
|
122
|
-
file?: string;
|
|
123
|
-
route?: boolean;
|
|
124
|
-
};
|
|
125
|
-
</script>
|
|
126
|
-
|
|
127
|
-
<script lang="ts" setup>
|
|
128
|
-
import options from 'virtual:vue-inspector-plus/options';
|
|
129
|
-
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
|
130
|
-
|
|
131
|
-
const SUPPORTED_MODIFIER_KEYS = ['shift', 'ctrl', 'alt', 'meta'];
|
|
132
|
-
const DATA_KEY = '__v_inspector';
|
|
133
|
-
|
|
134
|
-
const inspector = window.__VUE_INSPECTOR__;
|
|
135
|
-
const baseUrl = window.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__;
|
|
136
|
-
|
|
137
|
-
const dropdownRef = ref<HTMLElement>();
|
|
138
|
-
const position = ref({ top: 0, left: 0 });
|
|
139
|
-
const stack = ref<VueStackItem[]>([]);
|
|
140
|
-
const activeItem = ref<VueStackItem>();
|
|
141
|
-
|
|
142
|
-
const activeItemRectStyle = computed(() => {
|
|
143
|
-
if (!activeItem.value || activeItem.value.el?.nodeType !== Node.ELEMENT_NODE) return null;
|
|
144
|
-
|
|
145
|
-
const { top, left, width, height } = activeItem.value.el.getBoundingClientRect();
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
top: `${top}px`,
|
|
149
|
-
left: `${left}px`,
|
|
150
|
-
width: `${width}px`,
|
|
151
|
-
height: `${height}px`,
|
|
152
|
-
};
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
function matchModifierKey(e: MouseEvent) {
|
|
156
|
-
const conditions = options.modifierKey.split('|');
|
|
157
|
-
|
|
158
|
-
return conditions.some((condition) => {
|
|
159
|
-
return condition
|
|
160
|
-
.toLowerCase()
|
|
161
|
-
.split('+')
|
|
162
|
-
.filter((name) => SUPPORTED_MODIFIER_KEYS.includes(name))
|
|
163
|
-
.every((name) => (e as unknown as Record<string, boolean>)[name + 'Key']);
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function pascalize(str?: string) {
|
|
168
|
-
return str
|
|
169
|
-
?.split('-')
|
|
170
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
171
|
-
.join('');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function getFileName(path?: string) {
|
|
175
|
-
if (!path) return undefined;
|
|
176
|
-
const name = path.split('/').pop();
|
|
177
|
-
return name?.replace(/\.\w+$/, '');
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function toRelativePath(path?: string) {
|
|
181
|
-
const result = path?.replace(options.root, '');
|
|
182
|
-
return result?.startsWith('/') ? result : '/' + result;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function getLocation(path: string) {
|
|
186
|
-
const [, file, line, column] = path?.match(/(.+):([\d]+):([\d]+)$/) || [];
|
|
187
|
-
|
|
188
|
-
return {
|
|
189
|
-
file: file ?? path,
|
|
190
|
-
line: line ?? 1,
|
|
191
|
-
column: column ?? 1,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function getVueElement(el: VueHtmlElement) {
|
|
196
|
-
const vnode = el.__vnode;
|
|
197
|
-
if (!vnode) return null;
|
|
198
|
-
|
|
199
|
-
const tag = el.tagName.toLowerCase();
|
|
200
|
-
const id = el.id ? `#${el.id}` : '';
|
|
201
|
-
const classes = el.getAttribute('class')
|
|
202
|
-
? `.${el.getAttribute('class')!.replace(/\s+/g, '.')}`
|
|
203
|
-
: '';
|
|
204
|
-
const title = `${tag}${id}${classes}`;
|
|
205
|
-
|
|
206
|
-
const location = vnode.props?.[DATA_KEY];
|
|
207
|
-
|
|
208
|
-
return { type: 'element', el, title, file: undefined, location, route: undefined };
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function getVueComponents(el: VueHtmlElement) {
|
|
212
|
-
const vms = [];
|
|
213
|
-
let vm = el.__vueParentComponent;
|
|
214
|
-
|
|
215
|
-
while (vm) {
|
|
216
|
-
vms.push(vm);
|
|
217
|
-
vm = vm.parent;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return vms.map((vm) => {
|
|
221
|
-
if (vm.type.name === 'RouterView') {
|
|
222
|
-
return { type: 'component' };
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const el = vm.vnode.el;
|
|
226
|
-
const file = vm.type.__file;
|
|
227
|
-
const title = pascalize(vm.type.__name || vm.type.name || getFileName(file));
|
|
228
|
-
const location = vm.vnode.props?.[DATA_KEY];
|
|
229
|
-
const route = vm.parent?.type.name === 'RouterView';
|
|
230
|
-
|
|
231
|
-
return { type: 'component', el, title, file, location, route };
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function getVueStack(el: VueHtmlElement) {
|
|
236
|
-
return [getVueElement(el), ...getVueComponents(el)].filter(
|
|
237
|
-
(item) => item && (item.file || item.location),
|
|
238
|
-
) as VueStackItem[];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function updatePosition(left: number, top: number) {
|
|
242
|
-
position.value = { left, top };
|
|
243
|
-
|
|
244
|
-
nextTick().then(() => {
|
|
245
|
-
if (!dropdownRef.value) return;
|
|
246
|
-
const width = dropdownRef.value.offsetWidth;
|
|
247
|
-
const height = dropdownRef.value.offsetHeight;
|
|
248
|
-
|
|
249
|
-
if (left + width > window.innerWidth) {
|
|
250
|
-
position.value.left = Math.max(0, window.innerWidth - width - 10);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (top + height > window.innerHeight) {
|
|
254
|
-
position.value.top = Math.max(0, window.innerHeight - height - 10);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const handleClick = (e: MouseEvent) => {
|
|
260
|
-
clear();
|
|
261
|
-
|
|
262
|
-
if (!matchModifierKey(e)) return;
|
|
263
|
-
if (dropdownRef.value?.contains(e.target as HTMLElement)) return;
|
|
264
|
-
|
|
265
|
-
e.preventDefault();
|
|
266
|
-
e.stopPropagation();
|
|
267
|
-
|
|
268
|
-
stack.value = getVueStack(e.target as VueHtmlElement);
|
|
269
|
-
updatePosition(e.clientX + 2, e.clientY + 2);
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const clear = () => {
|
|
273
|
-
stack.value = [];
|
|
274
|
-
activeItem.value = undefined;
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const handleOpenLocation = (location?: string) => {
|
|
278
|
-
if (!location) return;
|
|
279
|
-
const { file, line, column } = getLocation(location);
|
|
280
|
-
inspector?.openInEditor(baseUrl, file, line, column);
|
|
281
|
-
};
|
|
282
|
-
|
|
283
|
-
const handleItemMouseEnter = (item: VueStackItem) => {
|
|
284
|
-
if (!stack.value.length) return;
|
|
285
|
-
activeItem.value = item;
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
const handleDropdownMouseLeave = () => {
|
|
289
|
-
activeItem.value = undefined;
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
onMounted(() => {
|
|
293
|
-
document.addEventListener('click', handleClick, { capture: true });
|
|
294
|
-
window.addEventListener('resize', clear);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
onUnmounted(() => {
|
|
298
|
-
document.removeEventListener('click', handleClick);
|
|
299
|
-
window.removeEventListener('resize', clear);
|
|
300
|
-
});
|
|
301
|
-
</script>
|
|
302
|
-
|
|
303
|
-
<style module>
|
|
304
|
-
.overlay {
|
|
305
|
-
position: fixed;
|
|
306
|
-
z-index: 100000;
|
|
307
|
-
padding: 4px;
|
|
308
|
-
border: 1px solid rgba(66, 184, 131, 0.8);
|
|
309
|
-
background: rgba(66, 184, 131, 0.2);
|
|
310
|
-
pointer-events: none;
|
|
311
|
-
box-sizing: border-box;
|
|
312
|
-
transition: all 0.2s;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
.dropdown {
|
|
316
|
-
position: fixed;
|
|
317
|
-
z-index: 2147483647;
|
|
318
|
-
margin: 0;
|
|
319
|
-
padding: 0;
|
|
320
|
-
max-width: 350px;
|
|
321
|
-
list-style: none;
|
|
322
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
323
|
-
background: white;
|
|
324
|
-
box-shadow: 0px 9px 28px 8px rgba(0, 0, 0, 0.05), 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
|
|
325
|
-
0px 3px 6px -4px rgba(0, 0, 0, 0.12);
|
|
326
|
-
border-radius: 6px;
|
|
327
|
-
transition: top 0.2s, left 0.2s;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
.item {
|
|
331
|
-
display: flex;
|
|
332
|
-
align-items: center;
|
|
333
|
-
line-height: 32px;
|
|
334
|
-
word-break: keep-all;
|
|
335
|
-
white-space: nowrap;
|
|
336
|
-
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
337
|
-
color: rgba(0, 0, 0, 0.3);
|
|
338
|
-
cursor: pointer;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
.item.element .title {
|
|
342
|
-
color: #878b95;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.item.component .title {
|
|
346
|
-
color: #42b883;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.item.component.route .title {
|
|
350
|
-
color: #4b7eff;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.item.splitted .icon {
|
|
354
|
-
border-right-color: rgba(0, 0, 0, 0.06);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.item:not(.splitted):hover .icon,
|
|
358
|
-
.item:not(.splitted):hover .main {
|
|
359
|
-
background: rgba(0, 0, 0, 0.03);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
.item:not(.splitted):hover .main::after {
|
|
363
|
-
visibility: visible;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
.icon {
|
|
367
|
-
display: flex;
|
|
368
|
-
align-items: center;
|
|
369
|
-
justify-content: center;
|
|
370
|
-
width: 32px;
|
|
371
|
-
height: 32px;
|
|
372
|
-
border-right: 1px solid transparent;
|
|
373
|
-
color: #8e67cd;
|
|
374
|
-
transition: background 0.2s;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.icon:hover {
|
|
378
|
-
background: rgba(0, 0, 0, 0.03);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
.icon svg {
|
|
382
|
-
width: 16px;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
.main {
|
|
386
|
-
padding-left: 8px;
|
|
387
|
-
padding-right: 8px;
|
|
388
|
-
display: flex;
|
|
389
|
-
align-items: center;
|
|
390
|
-
flex: 1;
|
|
391
|
-
gap: 8px;
|
|
392
|
-
min-width: 0;
|
|
393
|
-
transition: background 0.15s;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.main:hover {
|
|
397
|
-
background: rgba(0, 0, 0, 0.03);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
.main::after {
|
|
401
|
-
margin-left: -4px;
|
|
402
|
-
display: block;
|
|
403
|
-
width: 4px;
|
|
404
|
-
height: 4px;
|
|
405
|
-
border-top: 1px solid rgba(0, 0, 0, 0.3);
|
|
406
|
-
border-right: 1px solid rgba(0, 0, 0, 0.3);
|
|
407
|
-
content: '';
|
|
408
|
-
transform: rotate(45deg);
|
|
409
|
-
visibility: hidden;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
.main:hover::after {
|
|
413
|
-
visibility: visible;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.title {
|
|
417
|
-
max-width: 150px;
|
|
418
|
-
font-weight: bold;
|
|
419
|
-
overflow: hidden;
|
|
420
|
-
text-overflow: ellipsis;
|
|
421
|
-
white-space: nowrap;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
.location {
|
|
425
|
-
margin-right: auto;
|
|
426
|
-
flex: 1;
|
|
427
|
-
min-width: 0;
|
|
428
|
-
overflow: hidden;
|
|
429
|
-
text-overflow: ellipsis;
|
|
430
|
-
white-space: nowrap;
|
|
431
|
-
user-select: none;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
.scale-fade-active {
|
|
435
|
-
transition: transform 0.13s, opacity 0.1s;
|
|
436
|
-
transform-origin: top center;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
.scale-fade-inactive {
|
|
440
|
-
transform: scaleY(0.9);
|
|
441
|
-
opacity: 0;
|
|
442
|
-
}
|
|
443
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="activeItemRectStyle" :class="$style.overlay" :style="activeItemRectStyle"></div>
|
|
3
|
+
<transition
|
|
4
|
+
:enter-active-class="$style['scale-fade-active']"
|
|
5
|
+
:leave-active-class="$style['scale-fade-active']"
|
|
6
|
+
:enter-from-class="$style['scale-fade-inactive']"
|
|
7
|
+
:leave-to-class="$style['scale-fade-inactive']"
|
|
8
|
+
>
|
|
9
|
+
<ul
|
|
10
|
+
v-if="stack.length"
|
|
11
|
+
ref="dropdownRef"
|
|
12
|
+
:class="$style.dropdown"
|
|
13
|
+
:style="{ top: `${position.top}px`, left: `${position.left}px` }"
|
|
14
|
+
@mouseleave="handleDropdownMouseLeave"
|
|
15
|
+
>
|
|
16
|
+
<li
|
|
17
|
+
:class="[
|
|
18
|
+
$style.item,
|
|
19
|
+
{
|
|
20
|
+
[$style.element]: item.type === 'element',
|
|
21
|
+
[$style.component]: item.type === 'component',
|
|
22
|
+
[$style.route]: item.route,
|
|
23
|
+
[$style.splitted]: item.file && item.location,
|
|
24
|
+
},
|
|
25
|
+
]"
|
|
26
|
+
v-for="(item, index) in stack"
|
|
27
|
+
:key="index"
|
|
28
|
+
:dataFile="item.file"
|
|
29
|
+
:dataLocation="item.location"
|
|
30
|
+
@mouseenter="handleItemMouseEnter(item)"
|
|
31
|
+
>
|
|
32
|
+
<span v-if="item.file" :class="$style.icon" @click="handleOpenLocation(item.file)">
|
|
33
|
+
<svg
|
|
34
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
35
|
+
width="24"
|
|
36
|
+
height="24"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
fill="none"
|
|
39
|
+
stroke="currentColor"
|
|
40
|
+
stroke-width="2"
|
|
41
|
+
stroke-linecap="round"
|
|
42
|
+
stroke-linejoin="round"
|
|
43
|
+
>
|
|
44
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
45
|
+
<path
|
|
46
|
+
d="M21 16.008v-8.018a1.98 1.98 0 0 0 -1 -1.717l-7 -4.008a2.016 2.016 0 0 0 -2 0l-7 4.008c-.619 .355 -1 1.01 -1 1.718v8.018c0 .709 .381 1.363 1 1.717l7 4.008a2.016 2.016 0 0 0 2 0l7 -4.008c.619 -.355 1 -1.01 1 -1.718z"
|
|
47
|
+
/>
|
|
48
|
+
<path d="M12 22v-10" />
|
|
49
|
+
<path d="M12 12l8.73 -5.04" />
|
|
50
|
+
<path d="M3.27 6.96l8.73 5.04" />
|
|
51
|
+
</svg>
|
|
52
|
+
</span>
|
|
53
|
+
<span v-else :class="$style.icon" @click="handleOpenLocation(item.location)">
|
|
54
|
+
<svg
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
width="24"
|
|
57
|
+
height="24"
|
|
58
|
+
viewBox="0 0 24 24"
|
|
59
|
+
fill="none"
|
|
60
|
+
stroke="currentColor"
|
|
61
|
+
stroke-width="2"
|
|
62
|
+
stroke-linecap="round"
|
|
63
|
+
stroke-linejoin="round"
|
|
64
|
+
>
|
|
65
|
+
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
|
66
|
+
<circle cx="12" cy="12" r=".5" fill="currentColor" />
|
|
67
|
+
<path d="M12 12m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" />
|
|
68
|
+
<path d="M12 3l0 2" />
|
|
69
|
+
<path d="M3 12l2 0" />
|
|
70
|
+
<path d="M12 19l0 2" />
|
|
71
|
+
<path d="M19 12l2 0" />
|
|
72
|
+
</svg>
|
|
73
|
+
</span>
|
|
74
|
+
<div :class="$style.main" @click="handleOpenLocation(item.location || item.file)">
|
|
75
|
+
<span :class="$style.title">
|
|
76
|
+
{{ item.title || 'Unknown' }}
|
|
77
|
+
</span>
|
|
78
|
+
<span :class="$style.location">
|
|
79
|
+
{{ toRelativePath(item.location || item.file) }}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
</li>
|
|
83
|
+
</ul>
|
|
84
|
+
</transition>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<script lang="ts">
|
|
88
|
+
declare global {
|
|
89
|
+
interface Window {
|
|
90
|
+
__VUE_INSPECTOR__?: {
|
|
91
|
+
openInEditor: (baseUrl: string, file: string, line: string, column: string) => void;
|
|
92
|
+
};
|
|
93
|
+
__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__: string;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
type VueInstance = {
|
|
98
|
+
vnode: {
|
|
99
|
+
el: HTMLElement;
|
|
100
|
+
props: Record<string, unknown>;
|
|
101
|
+
};
|
|
102
|
+
parent: VueInstance;
|
|
103
|
+
type: {
|
|
104
|
+
name?: string;
|
|
105
|
+
__name?: string;
|
|
106
|
+
__file?: string;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
type VueHtmlElement = HTMLElement & {
|
|
111
|
+
__vueParentComponent: VueInstance;
|
|
112
|
+
__vnode: {
|
|
113
|
+
props: Record<string, unknown>;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
type VueStackItem = {
|
|
118
|
+
type: 'element' | 'component';
|
|
119
|
+
el?: VueHtmlElement;
|
|
120
|
+
title?: string;
|
|
121
|
+
location?: string;
|
|
122
|
+
file?: string;
|
|
123
|
+
route?: boolean;
|
|
124
|
+
};
|
|
125
|
+
</script>
|
|
126
|
+
|
|
127
|
+
<script lang="ts" setup>
|
|
128
|
+
import options from 'virtual:vue-inspector-plus/options';
|
|
129
|
+
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue';
|
|
130
|
+
|
|
131
|
+
const SUPPORTED_MODIFIER_KEYS = ['shift', 'ctrl', 'alt', 'meta'];
|
|
132
|
+
const DATA_KEY = '__v_inspector';
|
|
133
|
+
|
|
134
|
+
const inspector = window.__VUE_INSPECTOR__;
|
|
135
|
+
const baseUrl = window.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__;
|
|
136
|
+
|
|
137
|
+
const dropdownRef = ref<HTMLElement>();
|
|
138
|
+
const position = ref({ top: 0, left: 0 });
|
|
139
|
+
const stack = ref<VueStackItem[]>([]);
|
|
140
|
+
const activeItem = ref<VueStackItem>();
|
|
141
|
+
|
|
142
|
+
const activeItemRectStyle = computed(() => {
|
|
143
|
+
if (!activeItem.value || activeItem.value.el?.nodeType !== Node.ELEMENT_NODE) return null;
|
|
144
|
+
|
|
145
|
+
const { top, left, width, height } = activeItem.value.el.getBoundingClientRect();
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
top: `${top}px`,
|
|
149
|
+
left: `${left}px`,
|
|
150
|
+
width: `${width}px`,
|
|
151
|
+
height: `${height}px`,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
function matchModifierKey(e: MouseEvent) {
|
|
156
|
+
const conditions = options.modifierKey.split('|');
|
|
157
|
+
|
|
158
|
+
return conditions.some((condition) => {
|
|
159
|
+
return condition
|
|
160
|
+
.toLowerCase()
|
|
161
|
+
.split('+')
|
|
162
|
+
.filter((name) => SUPPORTED_MODIFIER_KEYS.includes(name))
|
|
163
|
+
.every((name) => (e as unknown as Record<string, boolean>)[name + 'Key']);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function pascalize(str?: string) {
|
|
168
|
+
return str
|
|
169
|
+
?.split('-')
|
|
170
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
171
|
+
.join('');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getFileName(path?: string) {
|
|
175
|
+
if (!path) return undefined;
|
|
176
|
+
const name = path.split('/').pop();
|
|
177
|
+
return name?.replace(/\.\w+$/, '');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function toRelativePath(path?: string) {
|
|
181
|
+
const result = path?.replace(options.root, '');
|
|
182
|
+
return result?.startsWith('/') ? result : '/' + result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getLocation(path: string) {
|
|
186
|
+
const [, file, line, column] = path?.match(/(.+):([\d]+):([\d]+)$/) || [];
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
file: file ?? path,
|
|
190
|
+
line: line ?? 1,
|
|
191
|
+
column: column ?? 1,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function getVueElement(el: VueHtmlElement) {
|
|
196
|
+
const vnode = el.__vnode;
|
|
197
|
+
if (!vnode) return null;
|
|
198
|
+
|
|
199
|
+
const tag = el.tagName.toLowerCase();
|
|
200
|
+
const id = el.id ? `#${el.id}` : '';
|
|
201
|
+
const classes = el.getAttribute('class')
|
|
202
|
+
? `.${el.getAttribute('class')!.replace(/\s+/g, '.')}`
|
|
203
|
+
: '';
|
|
204
|
+
const title = `${tag}${id}${classes}`;
|
|
205
|
+
|
|
206
|
+
const location = vnode.props?.[DATA_KEY];
|
|
207
|
+
|
|
208
|
+
return { type: 'element', el, title, file: undefined, location, route: undefined };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getVueComponents(el: VueHtmlElement) {
|
|
212
|
+
const vms = [];
|
|
213
|
+
let vm = el.__vueParentComponent;
|
|
214
|
+
|
|
215
|
+
while (vm) {
|
|
216
|
+
vms.push(vm);
|
|
217
|
+
vm = vm.parent;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return vms.map((vm) => {
|
|
221
|
+
if (vm.type.name === 'RouterView') {
|
|
222
|
+
return { type: 'component' };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const el = vm.vnode.el;
|
|
226
|
+
const file = vm.type.__file;
|
|
227
|
+
const title = pascalize(vm.type.__name || vm.type.name || getFileName(file));
|
|
228
|
+
const location = vm.vnode.props?.[DATA_KEY];
|
|
229
|
+
const route = vm.parent?.type.name === 'RouterView';
|
|
230
|
+
|
|
231
|
+
return { type: 'component', el, title, file, location, route };
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function getVueStack(el: VueHtmlElement) {
|
|
236
|
+
return [getVueElement(el), ...getVueComponents(el)].filter(
|
|
237
|
+
(item) => item && (item.file || item.location),
|
|
238
|
+
) as VueStackItem[];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function updatePosition(left: number, top: number) {
|
|
242
|
+
position.value = { left, top };
|
|
243
|
+
|
|
244
|
+
nextTick().then(() => {
|
|
245
|
+
if (!dropdownRef.value) return;
|
|
246
|
+
const width = dropdownRef.value.offsetWidth;
|
|
247
|
+
const height = dropdownRef.value.offsetHeight;
|
|
248
|
+
|
|
249
|
+
if (left + width > window.innerWidth) {
|
|
250
|
+
position.value.left = Math.max(0, window.innerWidth - width - 10);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (top + height > window.innerHeight) {
|
|
254
|
+
position.value.top = Math.max(0, window.innerHeight - height - 10);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const handleClick = (e: MouseEvent) => {
|
|
260
|
+
clear();
|
|
261
|
+
|
|
262
|
+
if (!matchModifierKey(e)) return;
|
|
263
|
+
if (dropdownRef.value?.contains(e.target as HTMLElement)) return;
|
|
264
|
+
|
|
265
|
+
e.preventDefault();
|
|
266
|
+
e.stopPropagation();
|
|
267
|
+
|
|
268
|
+
stack.value = getVueStack(e.target as VueHtmlElement);
|
|
269
|
+
updatePosition(e.clientX + 2, e.clientY + 2);
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const clear = () => {
|
|
273
|
+
stack.value = [];
|
|
274
|
+
activeItem.value = undefined;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const handleOpenLocation = (location?: string) => {
|
|
278
|
+
if (!location) return;
|
|
279
|
+
const { file, line, column } = getLocation(location);
|
|
280
|
+
inspector?.openInEditor(baseUrl, file, line, column);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const handleItemMouseEnter = (item: VueStackItem) => {
|
|
284
|
+
if (!stack.value.length) return;
|
|
285
|
+
activeItem.value = item;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const handleDropdownMouseLeave = () => {
|
|
289
|
+
activeItem.value = undefined;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
onMounted(() => {
|
|
293
|
+
document.addEventListener('click', handleClick, { capture: true });
|
|
294
|
+
window.addEventListener('resize', clear);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
onUnmounted(() => {
|
|
298
|
+
document.removeEventListener('click', handleClick);
|
|
299
|
+
window.removeEventListener('resize', clear);
|
|
300
|
+
});
|
|
301
|
+
</script>
|
|
302
|
+
|
|
303
|
+
<style module>
|
|
304
|
+
.overlay {
|
|
305
|
+
position: fixed;
|
|
306
|
+
z-index: 100000;
|
|
307
|
+
padding: 4px;
|
|
308
|
+
border: 1px solid rgba(66, 184, 131, 0.8);
|
|
309
|
+
background: rgba(66, 184, 131, 0.2);
|
|
310
|
+
pointer-events: none;
|
|
311
|
+
box-sizing: border-box;
|
|
312
|
+
transition: all 0.2s;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.dropdown {
|
|
316
|
+
position: fixed;
|
|
317
|
+
z-index: 2147483647;
|
|
318
|
+
margin: 0;
|
|
319
|
+
padding: 0;
|
|
320
|
+
max-width: 350px;
|
|
321
|
+
list-style: none;
|
|
322
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
323
|
+
background: white;
|
|
324
|
+
box-shadow: 0px 9px 28px 8px rgba(0, 0, 0, 0.05), 0px 6px 16px 0px rgba(0, 0, 0, 0.08),
|
|
325
|
+
0px 3px 6px -4px rgba(0, 0, 0, 0.12);
|
|
326
|
+
border-radius: 6px;
|
|
327
|
+
transition: top 0.2s, left 0.2s;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.item {
|
|
331
|
+
display: flex;
|
|
332
|
+
align-items: center;
|
|
333
|
+
line-height: 32px;
|
|
334
|
+
word-break: keep-all;
|
|
335
|
+
white-space: nowrap;
|
|
336
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
|
337
|
+
color: rgba(0, 0, 0, 0.3);
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.item.element .title {
|
|
342
|
+
color: #878b95;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.item.component .title {
|
|
346
|
+
color: #42b883;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.item.component.route .title {
|
|
350
|
+
color: #4b7eff;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.item.splitted .icon {
|
|
354
|
+
border-right-color: rgba(0, 0, 0, 0.06);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.item:not(.splitted):hover .icon,
|
|
358
|
+
.item:not(.splitted):hover .main {
|
|
359
|
+
background: rgba(0, 0, 0, 0.03);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.item:not(.splitted):hover .main::after {
|
|
363
|
+
visibility: visible;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.icon {
|
|
367
|
+
display: flex;
|
|
368
|
+
align-items: center;
|
|
369
|
+
justify-content: center;
|
|
370
|
+
width: 32px;
|
|
371
|
+
height: 32px;
|
|
372
|
+
border-right: 1px solid transparent;
|
|
373
|
+
color: #8e67cd;
|
|
374
|
+
transition: background 0.2s;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.icon:hover {
|
|
378
|
+
background: rgba(0, 0, 0, 0.03);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.icon svg {
|
|
382
|
+
width: 16px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.main {
|
|
386
|
+
padding-left: 8px;
|
|
387
|
+
padding-right: 8px;
|
|
388
|
+
display: flex;
|
|
389
|
+
align-items: center;
|
|
390
|
+
flex: 1;
|
|
391
|
+
gap: 8px;
|
|
392
|
+
min-width: 0;
|
|
393
|
+
transition: background 0.15s;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.main:hover {
|
|
397
|
+
background: rgba(0, 0, 0, 0.03);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.main::after {
|
|
401
|
+
margin-left: -4px;
|
|
402
|
+
display: block;
|
|
403
|
+
width: 4px;
|
|
404
|
+
height: 4px;
|
|
405
|
+
border-top: 1px solid rgba(0, 0, 0, 0.3);
|
|
406
|
+
border-right: 1px solid rgba(0, 0, 0, 0.3);
|
|
407
|
+
content: '';
|
|
408
|
+
transform: rotate(45deg);
|
|
409
|
+
visibility: hidden;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.main:hover::after {
|
|
413
|
+
visibility: visible;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.title {
|
|
417
|
+
max-width: 150px;
|
|
418
|
+
font-weight: bold;
|
|
419
|
+
overflow: hidden;
|
|
420
|
+
text-overflow: ellipsis;
|
|
421
|
+
white-space: nowrap;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.location {
|
|
425
|
+
margin-right: auto;
|
|
426
|
+
flex: 1;
|
|
427
|
+
min-width: 0;
|
|
428
|
+
overflow: hidden;
|
|
429
|
+
text-overflow: ellipsis;
|
|
430
|
+
white-space: nowrap;
|
|
431
|
+
user-select: none;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.scale-fade-active {
|
|
435
|
+
transition: transform 0.13s, opacity 0.1s;
|
|
436
|
+
transform-origin: top center;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.scale-fade-inactive {
|
|
440
|
+
transform: scaleY(0.9);
|
|
441
|
+
opacity: 0;
|
|
442
|
+
}
|
|
443
|
+
</style>
|
package/package.json
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vite-plugin-vue-inspector-plus",
|
|
3
|
-
"type": "module",
|
|
4
|
-
"version": "1.0.0",
|
|
5
|
-
"description": "Add a shortcut dropdown menu for Vue Inspector.",
|
|
6
|
-
"main": "./dist/index.cjs",
|
|
7
|
-
"module": "./dist/index.js",
|
|
8
|
-
"types": "./dist/index.d.ts",
|
|
9
|
-
"files": [
|
|
10
|
-
"dist"
|
|
11
|
-
],
|
|
12
|
-
"scripts": {
|
|
13
|
-
"build": "unbuild",
|
|
14
|
-
"prepack": "unbuild"
|
|
15
|
-
},
|
|
16
|
-
"exports": {
|
|
17
|
-
".": {
|
|
18
|
-
"types": "./dist/index.d.ts",
|
|
19
|
-
"import": "./dist/index.js",
|
|
20
|
-
"require": "./dist/index.cjs"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"repository": {
|
|
24
|
-
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/varHarrie/vite-plugin-vue-inspector-plus.git"
|
|
26
|
-
},
|
|
27
|
-
"keywords": [
|
|
28
|
-
"vue",
|
|
29
|
-
"inspector"
|
|
30
|
-
],
|
|
31
|
-
"author": "varHarrie <varharrie@gmail.com>",
|
|
32
|
-
"license": "MIT",
|
|
33
|
-
"bugs": {
|
|
34
|
-
"url": "https://github.com/varHarrie/vite-plugin-vue-inspector-plus/issues"
|
|
35
|
-
},
|
|
36
|
-
"homepage": "https://github.com/varHarrie/vite-plugin-vue-inspector-plus#readme",
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@types/node": "^22.10.2",
|
|
39
|
-
"@vue/tsconfig": "^0.7.0",
|
|
40
|
-
"mkdist": "^2.1.0",
|
|
41
|
-
"unbuild": "^3.0.1",
|
|
42
|
-
"vite": "^5.4.11",
|
|
43
|
-
"vue": "^3.0.0"
|
|
44
|
-
},
|
|
45
|
-
"peerDependencies": {
|
|
46
|
-
"vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0",
|
|
47
|
-
"vue": "^3.0.0"
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-vue-inspector-plus",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "1.0.1-alpha.0",
|
|
5
|
+
"description": "Add a shortcut dropdown menu for Vue Inspector.",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "unbuild",
|
|
14
|
+
"prepack": "unbuild"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/varHarrie/vite-plugin-vue-inspector-plus.git"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"vue",
|
|
29
|
+
"inspector"
|
|
30
|
+
],
|
|
31
|
+
"author": "varHarrie <varharrie@gmail.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/varHarrie/vite-plugin-vue-inspector-plus/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/varHarrie/vite-plugin-vue-inspector-plus#readme",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.10.2",
|
|
39
|
+
"@vue/tsconfig": "^0.7.0",
|
|
40
|
+
"mkdist": "^2.1.0",
|
|
41
|
+
"unbuild": "^3.0.1",
|
|
42
|
+
"vite": "^5.4.11",
|
|
43
|
+
"vue": "^3.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 ",
|
|
47
|
+
"vue": "^3.0.0"
|
|
48
|
+
}
|
|
49
49
|
}
|