vite-plugin-devtools-vue2 0.1.0 → 0.1.2
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 +3 -3
- package/lib/config.js +22 -0
- package/lib/events.js +71 -0
- package/lib/index.js +2 -2
- package/lib/main.js +1 -0
- package/lib/panel.js +181 -19
- package/lib/vuex.js +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# vite-plugin-vue2
|
|
1
|
+
# vite-plugin-devtools-vue2
|
|
2
2
|
|
|
3
3
|
一个**仅用于开发环境**的 Vite 插件,为 Vue 2.6 应用注入一个悬浮的组件审查面板(devtools)。
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
## 安装
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
npm i -D vite-plugin-vue2
|
|
27
|
+
npm i -D vite-plugin-devtools-vue2
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## 使用
|
|
@@ -33,7 +33,7 @@ npm i -D vite-plugin-vue2-devtools
|
|
|
33
33
|
|
|
34
34
|
```js
|
|
35
35
|
import { createVuePlugin } from 'vite-plugin-vue2';
|
|
36
|
-
import vueDevtools from 'vite-plugin-vue2
|
|
36
|
+
import vueDevtools from 'vite-plugin-devtools-vue2';
|
|
37
37
|
|
|
38
38
|
export default {
|
|
39
39
|
plugins: [createVuePlugin(), vueDevtools()]
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// config.js — shared tunables for the devtools panel.
|
|
2
|
+
|
|
3
|
+
// localStorage key for persisted UI state (open/closed, tab, docked position).
|
|
4
|
+
export const STORE_KEY = 'vue-devtools:ui';
|
|
5
|
+
|
|
6
|
+
// Panel size (keep in sync with .panel CSS) — used to keep it on-screen.
|
|
7
|
+
export const PANEL_W = 620;
|
|
8
|
+
export const PANEL_H = 420;
|
|
9
|
+
|
|
10
|
+
// Margin between the floating entry and the viewport edge.
|
|
11
|
+
export const EDGE_MARGIN = 12;
|
|
12
|
+
|
|
13
|
+
// Distance from the viewport edge to the panel when open (leaves room for the
|
|
14
|
+
// floating entry to sit in the gutter, matching the official devtools).
|
|
15
|
+
export const PANEL_EDGE = EDGE_MARGIN + 30 / 2;
|
|
16
|
+
|
|
17
|
+
// Pointer travel (px) before a press on the entry counts as a drag vs a click.
|
|
18
|
+
export const DRAG_THRESHOLD = 4;
|
|
19
|
+
|
|
20
|
+
// Show a per-section (props/data/computed/attrs…) filter input once a section
|
|
21
|
+
// has more than this many keys.
|
|
22
|
+
export const SECTION_FILTER_THRESHOLD = 10;
|
package/lib/events.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// events.js — records component events by patching Vue 2's `$emit`.
|
|
2
|
+
//
|
|
3
|
+
// We capture the Vue constructor from the global hook's `init` event (fired
|
|
4
|
+
// before the app mounts, since this module is imported early), then wrap
|
|
5
|
+
// `Vue.prototype.$emit` to log every custom event with its payload + source
|
|
6
|
+
// component. Lifecycle `hook:*` events are filtered out as noise.
|
|
7
|
+
|
|
8
|
+
import hook from './hook.js';
|
|
9
|
+
|
|
10
|
+
const events = [];
|
|
11
|
+
const listeners = new Set();
|
|
12
|
+
let patched = false;
|
|
13
|
+
let seq = 0;
|
|
14
|
+
|
|
15
|
+
const MAX_EVENTS = 200;
|
|
16
|
+
|
|
17
|
+
const emit = () => {
|
|
18
|
+
listeners.forEach(l => l());
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const componentName = vm => {
|
|
22
|
+
const o = (vm && vm.$options) || {};
|
|
23
|
+
let n = o.name || o._componentTag;
|
|
24
|
+
if (!n && o.__file)
|
|
25
|
+
n = String(o.__file)
|
|
26
|
+
.split(/[\\/]/)
|
|
27
|
+
.pop()
|
|
28
|
+
.replace(/\.vue$/, '');
|
|
29
|
+
if (!n && vm && vm.$root === vm) n = 'Root';
|
|
30
|
+
return n || 'Anonymous';
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const record = (vm, name, args) => {
|
|
34
|
+
if (typeof name === 'string' && name.indexOf('hook:') === 0) return; // lifecycle noise
|
|
35
|
+
events.push({
|
|
36
|
+
id: ++seq,
|
|
37
|
+
name: String(name),
|
|
38
|
+
args,
|
|
39
|
+
component: componentName(vm),
|
|
40
|
+
time: Date.now()
|
|
41
|
+
});
|
|
42
|
+
if (events.length > MAX_EVENTS) events.shift();
|
|
43
|
+
emit();
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
hook.on('init', Vue => {
|
|
47
|
+
if (patched || !Vue || !Vue.prototype) return;
|
|
48
|
+
patched = true;
|
|
49
|
+
const original = Vue.prototype.$emit;
|
|
50
|
+
// Must stay a real function: `this` is the emitting component instance.
|
|
51
|
+
Vue.prototype.$emit = function (name, ...args) {
|
|
52
|
+
try {
|
|
53
|
+
record(this, name, args);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
/* never break the app because of devtools */
|
|
56
|
+
}
|
|
57
|
+
return original.apply(this, arguments);
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export const getEvents = () => events;
|
|
62
|
+
|
|
63
|
+
export const subscribe = cb => {
|
|
64
|
+
listeners.add(cb);
|
|
65
|
+
return () => listeners.delete(cb);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const clearEvents = () => {
|
|
69
|
+
events.length = 0;
|
|
70
|
+
emit();
|
|
71
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,7 @@ const CLIENT_ENTRY = resolve(__dirname, 'main.js');
|
|
|
13
13
|
const PLUGIN_ROOT = __dirname;
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* vite-plugin-vue2
|
|
16
|
+
* vite-plugin-devtools-vue2
|
|
17
17
|
*
|
|
18
18
|
* Dev-only plugin. Injects a small client bundle that hooks into the Vue 2.6
|
|
19
19
|
* global devtools hook, walks the component tree and renders a floating
|
|
@@ -24,7 +24,7 @@ const PLUGIN_ROOT = __dirname;
|
|
|
24
24
|
*/
|
|
25
25
|
const vueDevTools = () => {
|
|
26
26
|
return {
|
|
27
|
-
name: 'vite-plugin-vue2
|
|
27
|
+
name: 'vite-plugin-devtools-vue2',
|
|
28
28
|
// Inspector is a development aid only; never touch the production build.
|
|
29
29
|
apply: 'serve',
|
|
30
30
|
|
package/lib/main.js
CHANGED
package/lib/panel.js
CHANGED
|
@@ -3,25 +3,14 @@
|
|
|
3
3
|
// on every Vue scheduler flush.
|
|
4
4
|
|
|
5
5
|
import { LitElement, css, html } from 'lit';
|
|
6
|
+
import { DRAG_THRESHOLD, EDGE_MARGIN, PANEL_EDGE, PANEL_H, PANEL_W, SECTION_FILTER_THRESHOLD, STORE_KEY } from './config.js';
|
|
6
7
|
import hook from './hook.js';
|
|
7
8
|
import { hide, highlight } from './inspector.js';
|
|
8
9
|
import { isPicking, startPicking, stopPicking } from './picker.js';
|
|
9
10
|
import { commitAll, getSnapshots, getStore, hasStore, travelTo, subscribe as vuexSubscribe } from './vuex.js';
|
|
11
|
+
import { clearEvents, getEvents, subscribe as eventsSubscribe } from './events.js';
|
|
10
12
|
import { buildTree, formatValue, getInstance } from './walker.js';
|
|
11
13
|
|
|
12
|
-
// Persisted UI state (survives reloads). Only stable, cheap bits — not the
|
|
13
|
-
// component tree / expanded set (ids are regenerated each load).
|
|
14
|
-
const STORE_KEY = 'vue-devtools:ui';
|
|
15
|
-
|
|
16
|
-
// Panel size (keep in sync with .panel CSS) — used to keep it on-screen.
|
|
17
|
-
const PANEL_W = 620;
|
|
18
|
-
const PANEL_H = 420;
|
|
19
|
-
const EDGE_MARGIN = 12;
|
|
20
|
-
// Distance from the viewport edge to the panel when open (leaves room for the
|
|
21
|
-
// floating entry to sit in the gutter, matching the official devtools).
|
|
22
|
-
const PANEL_EDGE = EDGE_MARGIN + 30 / 2;
|
|
23
|
-
const DRAG_THRESHOLD = 4;
|
|
24
|
-
|
|
25
14
|
export class VueDevToolsPanel extends LitElement {
|
|
26
15
|
static properties = {
|
|
27
16
|
tree: { state: true },
|
|
@@ -32,6 +21,7 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
32
21
|
query: { state: true },
|
|
33
22
|
tab: { state: true },
|
|
34
23
|
vuexSelected: { state: true },
|
|
24
|
+
timelineSelected: { state: true },
|
|
35
25
|
renderCodeText: { state: true }
|
|
36
26
|
};
|
|
37
27
|
|
|
@@ -47,9 +37,12 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
47
37
|
this.query = '';
|
|
48
38
|
this.tab = ui.tab || 'components';
|
|
49
39
|
this.vuexSelected = 0;
|
|
40
|
+
this.timelineSelected = null;
|
|
50
41
|
this.renderCodeText = null;
|
|
51
42
|
this.valueExpanded = new Set();
|
|
52
43
|
this.sectionCollapsed = new Set();
|
|
44
|
+
// Per-section (props/data/…) key filter text, keyed by section title.
|
|
45
|
+
this._sectionFilter = {};
|
|
53
46
|
// Docked position of the entry/panel. Defaults to the bottom edge near
|
|
54
47
|
// the right; { edge: 'left'|'right'|'top'|'bottom', along: number }.
|
|
55
48
|
this._pos = ui.pos || { edge: 'bottom', along: Number.POSITIVE_INFINITY };
|
|
@@ -219,6 +212,7 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
219
212
|
window.addEventListener('keydown', this._onKeydown, true);
|
|
220
213
|
window.addEventListener('resize', this._onResize);
|
|
221
214
|
this._vuexUnsub = vuexSubscribe(() => this.requestUpdate());
|
|
215
|
+
this._eventsUnsub = eventsSubscribe(() => this.requestUpdate());
|
|
222
216
|
// DOM-based fallback refresh (see constructor). Only childList/subtree —
|
|
223
217
|
// our own panel renders inside a shadow root (not observed), and the
|
|
224
218
|
// inspector highlight box only mutates via style, so this won't loop.
|
|
@@ -243,6 +237,7 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
243
237
|
this._domObserver = null;
|
|
244
238
|
}
|
|
245
239
|
if (this._vuexUnsub) this._vuexUnsub();
|
|
240
|
+
if (this._eventsUnsub) this._eventsUnsub();
|
|
246
241
|
stopPicking();
|
|
247
242
|
}
|
|
248
243
|
|
|
@@ -520,13 +515,48 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
520
515
|
|
|
521
516
|
// Render an object as a titled, collapsible section of expandable value rows.
|
|
522
517
|
// `editable` enables inline editing of primitive leaves (writes back into obj).
|
|
518
|
+
// Sections with many keys get a live filter input in the header.
|
|
523
519
|
_renderKvSection(title, obj, editable) {
|
|
524
|
-
const
|
|
525
|
-
if (!
|
|
520
|
+
const allKeys = obj ? Object.keys(obj) : [];
|
|
521
|
+
if (!allKeys.length) return null;
|
|
526
522
|
const collapsed = this.sectionCollapsed.has(title);
|
|
523
|
+
const showFilter = allKeys.length > SECTION_FILTER_THRESHOLD;
|
|
524
|
+
const q = (this._sectionFilter[title] || '').trim().toLowerCase();
|
|
525
|
+
const keys = q ? allKeys.filter(k => k.toLowerCase().includes(q)) : allKeys;
|
|
527
526
|
return html`
|
|
528
|
-
<div class="section-title" @click=${() => this._toggleSection(title)}
|
|
529
|
-
|
|
527
|
+
<div class="section-title" @click=${() => this._toggleSection(title)}>
|
|
528
|
+
${this._caret(!collapsed, false)}
|
|
529
|
+
<span class="section-name">${title}</span>
|
|
530
|
+
${showFilter
|
|
531
|
+
? html`
|
|
532
|
+
<input
|
|
533
|
+
class="section-filter"
|
|
534
|
+
type="search"
|
|
535
|
+
placeholder="filter…"
|
|
536
|
+
.value=${this._sectionFilter[title] || ''}
|
|
537
|
+
@click=${e => e.stopPropagation()}
|
|
538
|
+
@keydown=${e => {
|
|
539
|
+
if (e.key === 'Escape') {
|
|
540
|
+
this._sectionFilter[title] = '';
|
|
541
|
+
this.requestUpdate();
|
|
542
|
+
}
|
|
543
|
+
e.stopPropagation();
|
|
544
|
+
}}
|
|
545
|
+
@input=${e => {
|
|
546
|
+
this._sectionFilter[title] = e.target.value;
|
|
547
|
+
this.requestUpdate();
|
|
548
|
+
}}
|
|
549
|
+
/>
|
|
550
|
+
`
|
|
551
|
+
: null}
|
|
552
|
+
</div>
|
|
553
|
+
${collapsed
|
|
554
|
+
? null
|
|
555
|
+
: keys.length
|
|
556
|
+
? keys.map(k => this._renderValueRow(k, obj[k], `${title}.${k}`, 0, obj, editable))
|
|
557
|
+
: html`
|
|
558
|
+
<div class="empty">No match</div>
|
|
559
|
+
`}
|
|
530
560
|
`;
|
|
531
561
|
}
|
|
532
562
|
|
|
@@ -833,6 +863,10 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
833
863
|
${this._icon('vuex')}
|
|
834
864
|
<span class="tip">Vuex</span>
|
|
835
865
|
</button>
|
|
866
|
+
<button class="side-tab ${this.tab === 'timeline' ? 'active' : ''}" @click=${() => (this.tab = 'timeline')}>
|
|
867
|
+
${this._icon('timeline')}
|
|
868
|
+
<span class="tip">Timeline</span>
|
|
869
|
+
</button>
|
|
836
870
|
<span class="side-spacer"></span>
|
|
837
871
|
${this.tab === 'components'
|
|
838
872
|
? html`
|
|
@@ -847,7 +881,9 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
847
881
|
<span class="tip">Minimize</span>
|
|
848
882
|
</button>
|
|
849
883
|
</nav>
|
|
850
|
-
<div class="main"
|
|
884
|
+
<div class="main">
|
|
885
|
+
${this.tab === 'components' ? this._renderComponents() : this.tab === 'vuex' ? this._renderVuex() : this._renderTimeline()}
|
|
886
|
+
</div>
|
|
851
887
|
${this.renderCodeText != null
|
|
852
888
|
? html`
|
|
853
889
|
<div class="code-overlay">
|
|
@@ -955,7 +991,96 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
955
991
|
`;
|
|
956
992
|
}
|
|
957
993
|
|
|
958
|
-
//
|
|
994
|
+
// Merge component events + Vuex mutations into one chronological timeline
|
|
995
|
+
// (like the vue-devtools v7 Timeline).
|
|
996
|
+
_timelineEntries() {
|
|
997
|
+
const entries = [];
|
|
998
|
+
for (const e of getEvents()) {
|
|
999
|
+
entries.push({ id: `e${e.id}`, kind: 'event', time: e.time, title: e.name, sub: `<${e.component}>`, event: e });
|
|
1000
|
+
}
|
|
1001
|
+
getSnapshots().forEach((s, i) => {
|
|
1002
|
+
if (s.base) return;
|
|
1003
|
+
entries.push({ id: `m${i}`, kind: 'mutation', time: s.time || 0, title: s.type, sub: 'vuex', snap: s, index: i });
|
|
1004
|
+
});
|
|
1005
|
+
entries.sort((a, b) => a.time - b.time);
|
|
1006
|
+
return entries;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
_renderTimeline() {
|
|
1010
|
+
const entries = this._timelineEntries();
|
|
1011
|
+
if (!entries.length) {
|
|
1012
|
+
return html`
|
|
1013
|
+
<div class="body"><div class="empty">No timeline activity yet</div></div>
|
|
1014
|
+
`;
|
|
1015
|
+
}
|
|
1016
|
+
const sel = entries.find(e => e.id === this.timelineSelected) || entries[entries.length - 1];
|
|
1017
|
+
return html`
|
|
1018
|
+
<div class="body">
|
|
1019
|
+
<div class="tree">
|
|
1020
|
+
<div class="vuex-bar">
|
|
1021
|
+
<button
|
|
1022
|
+
class="btn"
|
|
1023
|
+
title="Clear recorded events"
|
|
1024
|
+
@click=${() => {
|
|
1025
|
+
clearEvents();
|
|
1026
|
+
this.timelineSelected = null;
|
|
1027
|
+
}}
|
|
1028
|
+
>
|
|
1029
|
+
✕ Clear events
|
|
1030
|
+
</button>
|
|
1031
|
+
</div>
|
|
1032
|
+
${entries.map(
|
|
1033
|
+
e => html`
|
|
1034
|
+
<div class="node ${e.id === sel.id ? 'selected' : ''}" @click=${() => (this.timelineSelected = e.id)}>
|
|
1035
|
+
<span class="tl-badge tl-${e.kind}">${e.kind === 'event' ? 'evt' : 'mut'}</span>
|
|
1036
|
+
<span class="ev-time">${this._formatTime(e.time)}</span>
|
|
1037
|
+
<span class="tag">${e.title}</span>
|
|
1038
|
+
<span class="ev-comp">${e.sub}</span>
|
|
1039
|
+
</div>
|
|
1040
|
+
`
|
|
1041
|
+
)}
|
|
1042
|
+
</div>
|
|
1043
|
+
<div class="detail">${this._renderTimelineDetail(sel)}</div>
|
|
1044
|
+
</div>
|
|
1045
|
+
`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
_renderTimelineDetail(entry) {
|
|
1049
|
+
if (!entry) return null;
|
|
1050
|
+
if (entry.kind === 'event') {
|
|
1051
|
+
const e = entry.event;
|
|
1052
|
+
const argsObj = {};
|
|
1053
|
+
(e.args || []).forEach((a, i) => {
|
|
1054
|
+
argsObj[i] = a;
|
|
1055
|
+
});
|
|
1056
|
+
return html`
|
|
1057
|
+
${this._renderKvSection('event', { name: e.name, from: e.component, time: new Date(e.time).toLocaleTimeString() }, false)}
|
|
1058
|
+
${e.args && e.args.length
|
|
1059
|
+
? this._renderKvSection('payload', argsObj, false)
|
|
1060
|
+
: html`
|
|
1061
|
+
<div class="empty">No payload</div>
|
|
1062
|
+
`}
|
|
1063
|
+
`;
|
|
1064
|
+
}
|
|
1065
|
+
const s = entry.snap;
|
|
1066
|
+
const store = getStore();
|
|
1067
|
+
const payloadObj = s.payload === undefined ? null : { payload: s.payload };
|
|
1068
|
+
return html`
|
|
1069
|
+
<button class="btn on time-travel" @click=${() => travelTo(entry.index)}>⏱ Time Travel</button>
|
|
1070
|
+
${this._renderKvSection('mutation', { type: s.type, time: new Date(s.time).toLocaleTimeString() }, false)}
|
|
1071
|
+
${payloadObj ? this._renderKvSection('payload', payloadObj, false) : null}
|
|
1072
|
+
${this._renderKvSection('state', s.state || {}, false)}
|
|
1073
|
+
${store ? this._renderKvSection('getters (live)', store.getters || {}, false) : null}
|
|
1074
|
+
`;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
_formatTime(t) {
|
|
1078
|
+
const d = new Date(t);
|
|
1079
|
+
const p = (n, l = 2) => String(n).padStart(l, '0');
|
|
1080
|
+
return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}.${p(d.getMilliseconds(), 3)}`;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// Plugin logo
|
|
959
1084
|
_vueLogo() {
|
|
960
1085
|
return html`
|
|
961
1086
|
<svg fill-rule="evenodd" viewBox="64 64 896 896" fill="#2932E1" aria-hidden="true">
|
|
@@ -986,6 +1111,12 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
986
1111
|
<path d="M4 11v6c0 1.7 3.6 3 8 3s8-1.3 8-3v-6" />
|
|
987
1112
|
</svg>
|
|
988
1113
|
`;
|
|
1114
|
+
case 'timeline':
|
|
1115
|
+
return html`
|
|
1116
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
1117
|
+
<path d="M3 12h4l3 8 4-16 3 8h4" />
|
|
1118
|
+
</svg>
|
|
1119
|
+
`;
|
|
989
1120
|
case 'pick':
|
|
990
1121
|
return html`
|
|
991
1122
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -1442,6 +1573,22 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
1442
1573
|
&:hover {
|
|
1443
1574
|
color: #475467;
|
|
1444
1575
|
}
|
|
1576
|
+
& .section-filter {
|
|
1577
|
+
margin-inline-start: 6px;
|
|
1578
|
+
inline-size: 96px;
|
|
1579
|
+
padding: 1px 6px;
|
|
1580
|
+
border: 1px solid var(--field-border);
|
|
1581
|
+
border-radius: 5px;
|
|
1582
|
+
background: var(--bg);
|
|
1583
|
+
color: var(--text-strong);
|
|
1584
|
+
font-size: 11px;
|
|
1585
|
+
text-transform: none;
|
|
1586
|
+
outline: none;
|
|
1587
|
+
cursor: text;
|
|
1588
|
+
}
|
|
1589
|
+
& .section-filter:focus {
|
|
1590
|
+
border-color: var(--accent);
|
|
1591
|
+
}
|
|
1445
1592
|
}
|
|
1446
1593
|
.vrow {
|
|
1447
1594
|
display: flex;
|
|
@@ -1544,6 +1691,21 @@ export class VueDevToolsPanel extends LitElement {
|
|
|
1544
1691
|
display: inline-block;
|
|
1545
1692
|
margin-block: 4px 8px;
|
|
1546
1693
|
}
|
|
1694
|
+
.ev-time {
|
|
1695
|
+
flex: none;
|
|
1696
|
+
font-size: 10px;
|
|
1697
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
1698
|
+
color: var(--muted);
|
|
1699
|
+
}
|
|
1700
|
+
.ev-comp {
|
|
1701
|
+
margin-inline-start: auto;
|
|
1702
|
+
padding-inline-start: 8px;
|
|
1703
|
+
color: var(--muted);
|
|
1704
|
+
}
|
|
1705
|
+
.node.selected .ev-time,
|
|
1706
|
+
.node.selected .ev-comp {
|
|
1707
|
+
color: rgb(255 255 255 / 0.85);
|
|
1708
|
+
}
|
|
1547
1709
|
`;
|
|
1548
1710
|
}
|
|
1549
1711
|
|
package/lib/vuex.js
CHANGED