nuxt-devtools-observatory 0.1.31 → 0.1.33
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 +79 -46
- package/client/.env.example +1 -0
- package/client/dist/assets/index-BqKYgjVB.js +20 -0
- package/client/dist/assets/index-bs1JBJ2u.css +1 -0
- package/client/dist/index.html +2 -2
- package/client/src/App.vue +4 -0
- package/client/src/components/Flamegraph.vue +7 -8
- package/client/src/components/SpanInspector.vue +1 -1
- package/client/src/components/TraceFilter.vue +0 -2
- package/client/src/components/WaterfallView.vue +1 -1
- package/client/src/composables/composable-search.ts +127 -0
- package/client/src/composables/trace-render-aggregation.ts +263 -0
- package/client/src/composables/useExportImport.ts +63 -0
- package/client/src/composables/useTraceFilter.ts +1 -5
- package/client/src/composables/useVirtualizationConfig.ts +40 -0
- package/client/src/composables/useVirtualizationFlags.ts +129 -0
- package/client/src/stores/observatory.ts +9 -1
- package/client/src/views/ComposableTracker.vue +273 -97
- package/client/src/views/FetchDashboard.vue +181 -16
- package/client/src/views/ProvideInjectGraph.vue +41 -18
- package/client/src/views/RenderHeatmap.vue +392 -76
- package/client/src/views/TraceViewer.vue +797 -14
- package/client/src/views/TransitionTimeline.vue +112 -19
- package/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +12 -23
- package/dist/runtime/composables/composable-registry.d.ts +19 -0
- package/dist/runtime/composables/composable-registry.js +63 -5
- package/dist/runtime/composables/render-registry.js +23 -13
- package/dist/runtime/instrumentation/asyncData.d.ts +1 -1
- package/dist/runtime/instrumentation/fetch.d.ts +7 -1
- package/dist/runtime/instrumentation/fetch.js +22 -1
- package/dist/runtime/nitro/fetch-capture.d.ts +1 -2
- package/dist/runtime/nitro/fetch-capture.js +85 -7
- package/dist/runtime/nitro/ssr-trace-store.d.ts +85 -0
- package/dist/runtime/nitro/ssr-trace-store.js +84 -0
- package/dist/runtime/plugin.js +48 -1
- package/dist/runtime/test-bridge.d.ts +18 -0
- package/dist/runtime/test-bridge.js +86 -0
- package/dist/runtime/tracing/trace.d.ts +1 -1
- package/package.json +18 -3
- package/client/.env +0 -17
- package/client/dist/assets/index-BuMXDBO9.js +0 -17
- package/client/dist/assets/index-CwcspZ6w.css +0 -1
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed } from 'vue'
|
|
3
|
+
import { useVirtualizer } from '@tanstack/vue-virtual'
|
|
4
|
+
import { useVirtualizationConfig } from '@observatory-client/composables/useVirtualizationConfig'
|
|
5
|
+
import { useVirtualizationFlags } from '@observatory-client/composables/useVirtualizationFlags'
|
|
3
6
|
import {
|
|
4
7
|
useObservatoryData,
|
|
5
8
|
setComposableMode,
|
|
6
9
|
editComposableValue,
|
|
7
10
|
openInEditor as openInEditorFromStore,
|
|
8
11
|
} from '@observatory-client/stores/observatory'
|
|
12
|
+
import { matchesComposableEntryQuery } from '@observatory-client/composables/composable-search'
|
|
9
13
|
import type { ComposableEntry as RuntimeComposableEntry } from '@observatory/types/snapshot'
|
|
10
14
|
|
|
11
15
|
const { composables: rawEntries, connected, features, clearComposables } = useObservatoryData()
|
|
@@ -92,6 +96,10 @@ function openInEditor(file: string) {
|
|
|
92
96
|
const filter = ref('all')
|
|
93
97
|
const search = ref('')
|
|
94
98
|
const expanded = ref<string | null>(null)
|
|
99
|
+
const listScrollRef = ref<HTMLElement | null>(null)
|
|
100
|
+
|
|
101
|
+
const { effective: virtualizationFlags } = useVirtualizationFlags()
|
|
102
|
+
const { preset: virtualizationPreset } = useVirtualizationConfig({ rowHeight: 88, overscan: 6 })
|
|
95
103
|
|
|
96
104
|
const entries = computed<RuntimeComposableEntry[]>(() => rawEntries.value)
|
|
97
105
|
|
|
@@ -118,23 +126,8 @@ const filtered = computed(() => {
|
|
|
118
126
|
return false
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (q) {
|
|
124
|
-
const matchesName = entry.name.toLowerCase().includes(q)
|
|
125
|
-
const matchesFile = entry.componentFile.toLowerCase().includes(q)
|
|
126
|
-
const matchesRef = Object.keys(entry.refs).some((k) => k.toLowerCase().includes(q))
|
|
127
|
-
const matchesVal = Object.values(entry.refs).some((v) => {
|
|
128
|
-
try {
|
|
129
|
-
return String(JSON.stringify(v.value)).toLowerCase().includes(q)
|
|
130
|
-
} catch {
|
|
131
|
-
return false
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
if (!matchesName && !matchesFile && !matchesRef && !matchesVal) {
|
|
136
|
-
return false
|
|
137
|
-
}
|
|
129
|
+
if (search.value.trim() && !matchesComposableEntryQuery(entry, search.value)) {
|
|
130
|
+
return false
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
return true
|
|
@@ -156,6 +149,99 @@ const filtered = computed(() => {
|
|
|
156
149
|
return [...layoutEntries, ...regularEntries]
|
|
157
150
|
})
|
|
158
151
|
|
|
152
|
+
const virtualizedCardsEnabled = computed(() => virtualizationFlags.value.composables && expanded.value === null)
|
|
153
|
+
|
|
154
|
+
const listVirtualizerOptions = computed(() => ({
|
|
155
|
+
count: filtered.value.length,
|
|
156
|
+
getScrollElement: () => listScrollRef.value,
|
|
157
|
+
estimateSize: () => virtualizationPreset.value.rowHeight,
|
|
158
|
+
overscan: virtualizationPreset.value.overscan,
|
|
159
|
+
}))
|
|
160
|
+
|
|
161
|
+
const listVirtualizer = useVirtualizer(listVirtualizerOptions)
|
|
162
|
+
|
|
163
|
+
const listVirtualItems = computed(() => {
|
|
164
|
+
if (!virtualizedCardsEnabled.value) {
|
|
165
|
+
return []
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return listVirtualizer.value.getVirtualItems()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const topListPadding = computed(() => {
|
|
172
|
+
if (!virtualizedCardsEnabled.value || listVirtualItems.value.length === 0) {
|
|
173
|
+
return 0
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return listVirtualItems.value[0].start
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const bottomListPadding = computed(() => {
|
|
180
|
+
if (!virtualizedCardsEnabled.value || listVirtualItems.value.length === 0) {
|
|
181
|
+
return 0
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const total = listVirtualizer.value.getTotalSize()
|
|
185
|
+
const last = listVirtualItems.value[listVirtualItems.value.length - 1]
|
|
186
|
+
|
|
187
|
+
return Math.max(0, total - last.end)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const visibleEntries = computed(() => {
|
|
191
|
+
if (!virtualizedCardsEnabled.value) {
|
|
192
|
+
return filtered.value
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return listVirtualItems.value
|
|
196
|
+
.map((item) => filtered.value[item.index])
|
|
197
|
+
.filter((entry): entry is RuntimeComposableEntry => Boolean(entry))
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const visibleEntryCards = computed<VisibleEntryCard[]>(() => {
|
|
201
|
+
return visibleEntries.value.map((entry) => {
|
|
202
|
+
const refEntries = Object.entries(entry.refs) as Array<[string, RuntimeComposableEntry['refs'][string]]>
|
|
203
|
+
const detailRefs: RefRowView[] = refEntries.map(([key, ref]) => {
|
|
204
|
+
const isLong = isLongValue(ref.value)
|
|
205
|
+
const expanded = isLong && isRefExpanded(entry.id, key)
|
|
206
|
+
const shared = Boolean(entry.sharedKeys?.includes(key))
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
key,
|
|
210
|
+
ref,
|
|
211
|
+
isLong,
|
|
212
|
+
expanded,
|
|
213
|
+
displayValue: isLong && !expanded ? formatVal(ref.value) : formatValFull(ref.value),
|
|
214
|
+
typeClass: typeBadgeClass(ref.type),
|
|
215
|
+
shared,
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const history = entry.history ?? []
|
|
220
|
+
const historyRows: HistoryRowView[] = []
|
|
221
|
+
const end = Math.max(history.length - 20, 0)
|
|
222
|
+
|
|
223
|
+
for (let i = history.length - 1; i >= end; i--) {
|
|
224
|
+
const evt = history[i]
|
|
225
|
+
historyRows.push({
|
|
226
|
+
id: `${entry.id}-${evt.key}-${evt.t}-${i}`,
|
|
227
|
+
timeLabel: `+${(evt.t / 1000).toFixed(2)}s`,
|
|
228
|
+
key: evt.key,
|
|
229
|
+
value: formatVal(evt.value),
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
entry,
|
|
235
|
+
refCount: refEntries.length,
|
|
236
|
+
previewRefs: detailRefs.slice(0, 3),
|
|
237
|
+
detailRefs,
|
|
238
|
+
historyRows,
|
|
239
|
+
historyHiddenCount: Math.max(0, history.length - 20),
|
|
240
|
+
lifecycle: lifecycleRows(entry),
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
})
|
|
244
|
+
|
|
159
245
|
function lifecycleRows(entry: RuntimeComposableEntry) {
|
|
160
246
|
return [
|
|
161
247
|
{
|
|
@@ -234,22 +320,94 @@ function toggleRefExpand(entryId: string, refKey: string) {
|
|
|
234
320
|
}
|
|
235
321
|
|
|
236
322
|
// ── Reverse lookup ────────────────────────────────────────────────────────
|
|
237
|
-
// Clicking a ref key
|
|
323
|
+
// Clicking a ref key prefers identity-based lookup for shared/global refs.
|
|
324
|
+
// For non-shared keys, fallback to legacy key-name lookup.
|
|
325
|
+
|
|
326
|
+
interface LookupTarget {
|
|
327
|
+
key: string
|
|
328
|
+
composableName: string
|
|
329
|
+
identityGroup?: string
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
interface RefRowView {
|
|
333
|
+
key: string
|
|
334
|
+
ref: RuntimeComposableEntry['refs'][string]
|
|
335
|
+
isLong: boolean
|
|
336
|
+
expanded: boolean
|
|
337
|
+
displayValue: string
|
|
338
|
+
typeClass: string
|
|
339
|
+
shared: boolean
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
interface HistoryRowView {
|
|
343
|
+
id: string
|
|
344
|
+
timeLabel: string
|
|
345
|
+
key: string
|
|
346
|
+
value: string
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
interface VisibleEntryCard {
|
|
350
|
+
entry: RuntimeComposableEntry
|
|
351
|
+
refCount: number
|
|
352
|
+
previewRefs: RefRowView[]
|
|
353
|
+
detailRefs: RefRowView[]
|
|
354
|
+
historyRows: HistoryRowView[]
|
|
355
|
+
historyHiddenCount: number
|
|
356
|
+
lifecycle: ReturnType<typeof lifecycleRows>
|
|
357
|
+
}
|
|
238
358
|
|
|
239
|
-
const
|
|
359
|
+
const lookupTarget = ref<LookupTarget | null>(null)
|
|
240
360
|
|
|
241
361
|
const lookupResults = computed(() => {
|
|
242
|
-
if (!
|
|
362
|
+
if (!lookupTarget.value) {
|
|
243
363
|
return []
|
|
244
364
|
}
|
|
245
365
|
|
|
246
|
-
const
|
|
366
|
+
const target = lookupTarget.value
|
|
247
367
|
|
|
248
|
-
|
|
368
|
+
if (target.identityGroup) {
|
|
369
|
+
return entries.value.filter(
|
|
370
|
+
(entry) =>
|
|
371
|
+
entry.name === target.composableName &&
|
|
372
|
+
entry.sharedKeyGroups?.[target.key] === target.identityGroup &&
|
|
373
|
+
target.key in entry.refs
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return entries.value.filter((entry) => target.key in entry.refs)
|
|
249
378
|
})
|
|
250
379
|
|
|
251
|
-
|
|
252
|
-
|
|
380
|
+
const lookupTitle = computed(() => {
|
|
381
|
+
if (!lookupTarget.value) {
|
|
382
|
+
return ''
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (lookupTarget.value.identityGroup) {
|
|
386
|
+
return `${lookupTarget.value.key} (shared identity)`
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return lookupTarget.value.key
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
function openLookup(entry: RuntimeComposableEntry, key: string) {
|
|
393
|
+
const identityGroup = entry.sharedKeyGroups?.[key]
|
|
394
|
+
const next: LookupTarget = {
|
|
395
|
+
key,
|
|
396
|
+
composableName: entry.name,
|
|
397
|
+
identityGroup,
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (
|
|
401
|
+
lookupTarget.value?.key === next.key &&
|
|
402
|
+
lookupTarget.value?.composableName === next.composableName &&
|
|
403
|
+
lookupTarget.value?.identityGroup === next.identityGroup
|
|
404
|
+
) {
|
|
405
|
+
lookupTarget.value = null
|
|
406
|
+
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
lookupTarget.value = next
|
|
253
411
|
}
|
|
254
412
|
|
|
255
413
|
// ── Inline editing ────────────────────────────────────────────────────────
|
|
@@ -340,127 +498,133 @@ function applyEdit() {
|
|
|
340
498
|
</button>
|
|
341
499
|
</div>
|
|
342
500
|
|
|
343
|
-
<div class="composable-tracker__list">
|
|
501
|
+
<div ref="listScrollRef" class="composable-tracker__list">
|
|
502
|
+
<div
|
|
503
|
+
v-if="virtualizedCardsEnabled && topListPadding > 0"
|
|
504
|
+
class="composable-tracker__virtual-spacer"
|
|
505
|
+
:style="{ height: `${topListPadding}px` }"
|
|
506
|
+
aria-hidden="true"
|
|
507
|
+
/>
|
|
344
508
|
<div
|
|
345
|
-
v-for="
|
|
346
|
-
:key="entry.id"
|
|
509
|
+
v-for="card in visibleEntryCards"
|
|
510
|
+
:key="card.entry.id"
|
|
347
511
|
class="composable-tracker__card"
|
|
348
512
|
:class="{
|
|
349
|
-
'composable-tracker__card--leak': entry.leak,
|
|
350
|
-
'composable-tracker__card--expanded': expanded === entry.id,
|
|
513
|
+
'composable-tracker__card--leak': card.entry.leak,
|
|
514
|
+
'composable-tracker__card--expanded': expanded === card.entry.id,
|
|
351
515
|
}"
|
|
352
|
-
@click="expanded = expanded === entry.id ? null : entry.id"
|
|
516
|
+
@click="expanded = expanded === card.entry.id ? null : card.entry.id"
|
|
353
517
|
>
|
|
354
518
|
<div class="composable-tracker__card-header">
|
|
355
519
|
<div class="composable-tracker__identity">
|
|
356
|
-
<span class="composable-tracker__name mono">{{ entry.name }}</span>
|
|
357
|
-
<span class="composable-tracker__file muted mono">{{ basename(entry.componentFile) }}</span>
|
|
520
|
+
<span class="composable-tracker__name mono">{{ card.entry.name }}</span>
|
|
521
|
+
<span class="composable-tracker__file muted mono">{{ basename(card.entry.componentFile) }}</span>
|
|
358
522
|
</div>
|
|
359
523
|
<div class="composable-tracker__meta">
|
|
360
|
-
<span v-if="entry.watcherCount > 0 && !entry.leak" class="badge badge-warn">
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
<span v-
|
|
524
|
+
<span v-if="card.entry.watcherCount > 0 && !card.entry.leak" class="badge badge-warn">
|
|
525
|
+
{{ card.entry.watcherCount }}w
|
|
526
|
+
</span>
|
|
527
|
+
<span v-if="card.entry.intervalCount > 0 && !card.entry.leak" class="badge badge-warn">
|
|
528
|
+
{{ card.entry.intervalCount }}t
|
|
529
|
+
</span>
|
|
530
|
+
<span v-if="card.entry.leak" class="badge badge-err">leak</span>
|
|
531
|
+
<span v-else-if="card.entry.status === 'mounted'" class="badge badge-ok">mounted</span>
|
|
364
532
|
<span v-else class="badge badge-gray">unmounted</span>
|
|
365
533
|
</div>
|
|
366
534
|
</div>
|
|
367
535
|
|
|
368
536
|
<!-- Inline ref preview — shows up to 3 refs without expanding -->
|
|
369
|
-
<div v-if="
|
|
537
|
+
<div v-if="card.refCount" class="composable-tracker__refs-preview">
|
|
370
538
|
<span
|
|
371
|
-
v-for="
|
|
372
|
-
:key="
|
|
539
|
+
v-for="row in card.previewRefs"
|
|
540
|
+
:key="row.key"
|
|
373
541
|
class="composable-tracker__ref-chip"
|
|
374
542
|
:class="{
|
|
375
|
-
'composable-tracker__ref-chip--reactive':
|
|
376
|
-
'composable-tracker__ref-chip--computed':
|
|
377
|
-
'composable-tracker__ref-chip--shared':
|
|
543
|
+
'composable-tracker__ref-chip--reactive': row.ref.type === 'reactive',
|
|
544
|
+
'composable-tracker__ref-chip--computed': row.ref.type === 'computed',
|
|
545
|
+
'composable-tracker__ref-chip--shared': row.shared,
|
|
378
546
|
}"
|
|
379
|
-
:title="
|
|
547
|
+
:title="row.shared ? 'shared global state' : ''"
|
|
380
548
|
>
|
|
381
|
-
<span class="composable-tracker__ref-chip-key">{{
|
|
382
|
-
<span class="composable-tracker__ref-chip-val">{{ formatVal(
|
|
383
|
-
<span v-if="
|
|
384
|
-
</span>
|
|
385
|
-
<span v-if="Object.keys(entry.refs).length > 3" class="muted text-sm">
|
|
386
|
-
+{{ Object.keys(entry.refs).length - 3 }} more
|
|
549
|
+
<span class="composable-tracker__ref-chip-key">{{ row.key }}</span>
|
|
550
|
+
<span class="composable-tracker__ref-chip-val">{{ formatVal(row.ref.value) }}</span>
|
|
551
|
+
<span v-if="row.shared" class="composable-tracker__ref-chip-shared-dot" title="global"></span>
|
|
387
552
|
</span>
|
|
553
|
+
<span v-if="card.refCount > 3" class="muted text-sm">+{{ card.refCount - 3 }} more</span>
|
|
388
554
|
</div>
|
|
389
555
|
|
|
390
|
-
<div v-if="expanded === entry.id" class="composable-tracker__detail" @click.stop>
|
|
391
|
-
<div v-if="entry.leak" class="composable-tracker__leak-banner">{{ entry.leakReason }}</div>
|
|
556
|
+
<div v-if="expanded === card.entry.id" class="composable-tracker__detail" @click.stop>
|
|
557
|
+
<div v-if="card.entry.leak" class="composable-tracker__leak-banner">{{ card.entry.leakReason }}</div>
|
|
392
558
|
|
|
393
559
|
<!-- Global state warning -->
|
|
394
|
-
<div v-if="entry.sharedKeys?.length" class="composable-tracker__global-banner">
|
|
560
|
+
<div v-if="card.entry.sharedKeys?.length" class="composable-tracker__global-banner">
|
|
395
561
|
<span class="composable-tracker__global-dot"></span>
|
|
396
562
|
<span>
|
|
397
563
|
<strong>global state</strong>
|
|
398
|
-
— {{ entry.sharedKeys.join(', ') }}
|
|
399
|
-
{{ entry.sharedKeys.length === 1 ? 'is' : 'are' }}
|
|
400
|
-
shared across all instances of {{ entry.name }}
|
|
564
|
+
— {{ card.entry.sharedKeys.join(', ') }}
|
|
565
|
+
{{ card.entry.sharedKeys.length === 1 ? 'is' : 'are' }}
|
|
566
|
+
shared across all instances of {{ card.entry.name }}
|
|
401
567
|
</span>
|
|
402
568
|
</div>
|
|
403
569
|
|
|
404
570
|
<div class="composable-tracker__section-label tracker-section-label">reactive state</div>
|
|
405
|
-
<div v-if="!
|
|
406
|
-
|
|
407
|
-
</div>
|
|
408
|
-
<div v-for="[k, v] in Object.entries(entry.refs)" :key="k" class="composable-tracker__ref-row">
|
|
571
|
+
<div v-if="!card.refCount" class="composable-tracker__compact-muted muted text-sm">no tracked state returned</div>
|
|
572
|
+
<div v-for="row in card.detailRefs" :key="row.key" class="composable-tracker__ref-row">
|
|
409
573
|
<span
|
|
410
574
|
class="composable-tracker__ref-key composable-tracker__ref-key--clickable mono text-sm"
|
|
411
|
-
:title="
|
|
412
|
-
|
|
575
|
+
:title="
|
|
576
|
+
card.entry.sharedKeyGroups?.[row.key]
|
|
577
|
+
? `click to see instances sharing this exact '${row.key}' state`
|
|
578
|
+
: `click to see all instances exposing '${row.key}'`
|
|
579
|
+
"
|
|
580
|
+
@click.stop="openLookup(card.entry, row.key)"
|
|
413
581
|
>
|
|
414
|
-
{{
|
|
582
|
+
{{ row.key }}
|
|
415
583
|
</span>
|
|
416
584
|
<span
|
|
417
585
|
class="composable-tracker__ref-val mono text-sm"
|
|
418
586
|
:class="{
|
|
419
|
-
'composable-tracker__ref-val--full':
|
|
420
|
-
'composable-tracker__ref-val--collapsed':
|
|
587
|
+
'composable-tracker__ref-val--full': row.isLong && row.expanded,
|
|
588
|
+
'composable-tracker__ref-val--collapsed': row.isLong && !row.expanded,
|
|
421
589
|
}"
|
|
422
590
|
>
|
|
423
|
-
{{
|
|
591
|
+
{{ row.displayValue }}
|
|
424
592
|
</span>
|
|
425
593
|
<div class="composable-tracker__ref-row-actions">
|
|
426
594
|
<button
|
|
427
|
-
v-if="
|
|
595
|
+
v-if="row.isLong"
|
|
428
596
|
class="composable-tracker__expand-btn"
|
|
429
|
-
:title="
|
|
430
|
-
@click.stop="toggleRefExpand(entry.id,
|
|
597
|
+
:title="row.expanded ? 'Collapse' : 'Expand'"
|
|
598
|
+
@click.stop="toggleRefExpand(card.entry.id, row.key)"
|
|
431
599
|
>
|
|
432
|
-
{{
|
|
600
|
+
{{ row.expanded ? '▲' : '▼' }}
|
|
433
601
|
</button>
|
|
434
|
-
<span class="badge text-xs" :class="
|
|
435
|
-
<span v-if="
|
|
602
|
+
<span class="badge text-xs" :class="row.typeClass">{{ row.ref.type }}</span>
|
|
603
|
+
<span v-if="row.shared" class="badge badge-amber text-xs">global</span>
|
|
436
604
|
<button
|
|
437
|
-
v-if="
|
|
605
|
+
v-if="row.ref.type === 'ref'"
|
|
438
606
|
class="composable-tracker__edit-btn"
|
|
439
607
|
title="Edit value"
|
|
440
|
-
@click.stop="openEdit(entry.id,
|
|
608
|
+
@click.stop="openEdit(card.entry.id, row.key, row.ref.value)"
|
|
441
609
|
>
|
|
442
610
|
edit
|
|
443
611
|
</button>
|
|
444
612
|
</div>
|
|
445
613
|
</div>
|
|
446
614
|
|
|
447
|
-
<template v-if="
|
|
615
|
+
<template v-if="card.historyRows.length">
|
|
448
616
|
<div class="composable-tracker__section-label composable-tracker__section-label--spaced tracker-section-label">
|
|
449
617
|
change history
|
|
450
|
-
<span class="composable-tracker__section-label-meta muted">({{ entry.history.length }} events)</span>
|
|
618
|
+
<span class="composable-tracker__section-label-meta muted">({{ card.entry.history.length }} events)</span>
|
|
451
619
|
</div>
|
|
452
620
|
<div class="composable-tracker__history-list">
|
|
453
|
-
<div
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
class="composable-tracker__history-
|
|
457
|
-
>
|
|
458
|
-
<span class="composable-tracker__history-time mono muted">+{{ (evt.t / 1000).toFixed(2) }}s</span>
|
|
459
|
-
<span class="composable-tracker__history-key mono">{{ evt.key }}</span>
|
|
460
|
-
<span class="composable-tracker__history-val mono">{{ formatVal(evt.value) }}</span>
|
|
621
|
+
<div v-for="historyRow in card.historyRows" :key="historyRow.id" class="composable-tracker__history-row">
|
|
622
|
+
<span class="composable-tracker__history-time mono muted">{{ historyRow.timeLabel }}</span>
|
|
623
|
+
<span class="composable-tracker__history-key mono">{{ historyRow.key }}</span>
|
|
624
|
+
<span class="composable-tracker__history-val mono">{{ historyRow.value }}</span>
|
|
461
625
|
</div>
|
|
462
|
-
<div v-if="
|
|
463
|
-
… {{
|
|
626
|
+
<div v-if="card.historyHiddenCount > 0" class="composable-tracker__compact-muted muted text-sm">
|
|
627
|
+
… {{ card.historyHiddenCount }} earlier events
|
|
464
628
|
</div>
|
|
465
629
|
</div>
|
|
466
630
|
</template>
|
|
@@ -468,7 +632,7 @@ function applyEdit() {
|
|
|
468
632
|
<div class="composable-tracker__section-label composable-tracker__section-label--spaced tracker-section-label">
|
|
469
633
|
lifecycle
|
|
470
634
|
</div>
|
|
471
|
-
<div v-for="row in
|
|
635
|
+
<div v-for="row in card.lifecycle" :key="row.label" class="composable-tracker__lifecycle-row">
|
|
472
636
|
<span
|
|
473
637
|
class="composable-tracker__lifecycle-dot"
|
|
474
638
|
:class="row.ok ? 'composable-tracker__lifecycle-dot--ok' : 'composable-tracker__lifecycle-dot--error'"
|
|
@@ -487,12 +651,12 @@ function applyEdit() {
|
|
|
487
651
|
</div>
|
|
488
652
|
<div class="composable-tracker__lifecycle-row">
|
|
489
653
|
<span class="composable-tracker__context-label muted text-sm">component</span>
|
|
490
|
-
<span class="composable-tracker__context-value mono text-sm">{{ basename(entry.componentFile) }}</span>
|
|
654
|
+
<span class="composable-tracker__context-value mono text-sm">{{ basename(card.entry.componentFile) }}</span>
|
|
491
655
|
</div>
|
|
492
656
|
<div class="composable-tracker__lifecycle-row">
|
|
493
657
|
<span class="composable-tracker__context-label muted text-sm">uid</span>
|
|
494
658
|
<span class="composable-tracker__context-value composable-tracker__context-value--muted mono text-sm muted">
|
|
495
|
-
{{ entry.componentUid }}
|
|
659
|
+
{{ card.entry.componentUid }}
|
|
496
660
|
</span>
|
|
497
661
|
</div>
|
|
498
662
|
<div class="composable-tracker__lifecycle-row">
|
|
@@ -500,8 +664,8 @@ function applyEdit() {
|
|
|
500
664
|
<span
|
|
501
665
|
class="composable-tracker__context-value composable-tracker__context-value--row composable-tracker__context-value--muted mono text-sm muted"
|
|
502
666
|
>
|
|
503
|
-
{{ entry.file }}:{{ entry.line }}
|
|
504
|
-
<button class="composable-tracker__jump-btn" title="Open in editor" @click.stop="openInEditor(entry.file)">
|
|
667
|
+
{{ card.entry.file }}:{{ card.entry.line }}
|
|
668
|
+
<button class="composable-tracker__jump-btn" title="Open in editor" @click.stop="openInEditor(card.entry.file)">
|
|
505
669
|
open ↗
|
|
506
670
|
</button>
|
|
507
671
|
</span>
|
|
@@ -509,20 +673,27 @@ function applyEdit() {
|
|
|
509
673
|
<div class="composable-tracker__lifecycle-row">
|
|
510
674
|
<span class="composable-tracker__context-label muted text-sm">route</span>
|
|
511
675
|
<span class="composable-tracker__context-value composable-tracker__context-value--muted mono text-sm muted">
|
|
512
|
-
{{ entry.route }}
|
|
676
|
+
{{ card.entry.route }}
|
|
513
677
|
</span>
|
|
514
678
|
</div>
|
|
515
679
|
<div class="composable-tracker__lifecycle-row">
|
|
516
680
|
<span class="composable-tracker__context-label muted text-sm">watchers</span>
|
|
517
|
-
<span class="composable-tracker__context-value mono text-sm">{{ entry.watcherCount }}</span>
|
|
681
|
+
<span class="composable-tracker__context-value mono text-sm">{{ card.entry.watcherCount }}</span>
|
|
518
682
|
</div>
|
|
519
683
|
<div class="composable-tracker__lifecycle-row">
|
|
520
684
|
<span class="composable-tracker__context-label muted text-sm">intervals</span>
|
|
521
|
-
<span class="composable-tracker__context-value mono text-sm">{{ entry.intervalCount }}</span>
|
|
685
|
+
<span class="composable-tracker__context-value mono text-sm">{{ card.entry.intervalCount }}</span>
|
|
522
686
|
</div>
|
|
523
687
|
</div>
|
|
524
688
|
</div>
|
|
525
689
|
|
|
690
|
+
<div
|
|
691
|
+
v-if="virtualizedCardsEnabled && bottomListPadding > 0"
|
|
692
|
+
class="composable-tracker__virtual-spacer"
|
|
693
|
+
:style="{ height: `${bottomListPadding}px` }"
|
|
694
|
+
aria-hidden="true"
|
|
695
|
+
/>
|
|
696
|
+
|
|
526
697
|
<div v-if="!filtered.length" class="composable-tracker__empty muted text-sm">
|
|
527
698
|
{{ connected ? 'No composables recorded yet.' : 'Waiting for connection to the Nuxt app…' }}
|
|
528
699
|
</div>
|
|
@@ -530,14 +701,14 @@ function applyEdit() {
|
|
|
530
701
|
|
|
531
702
|
<!-- ── Reverse lookup panel ──────────────────────────────────────── -->
|
|
532
703
|
<Transition name="slide">
|
|
533
|
-
<div v-if="
|
|
704
|
+
<div v-if="lookupTarget" class="composable-tracker__lookup-panel">
|
|
534
705
|
<div class="composable-tracker__lookup-header">
|
|
535
|
-
<span class="mono text-sm">{{
|
|
706
|
+
<span class="mono text-sm">{{ lookupTitle }}</span>
|
|
536
707
|
<span class="muted text-sm">— {{ lookupResults.length }} instance{{ lookupResults.length !== 1 ? 's' : '' }}</span>
|
|
537
|
-
<button class="composable-tracker__clear-btn composable-tracker__lookup-close" @click="
|
|
708
|
+
<button class="composable-tracker__clear-btn composable-tracker__lookup-close" @click="lookupTarget = null">✕</button>
|
|
538
709
|
</div>
|
|
539
710
|
<div v-if="!lookupResults.length" class="composable-tracker__lookup-empty muted text-sm">
|
|
540
|
-
No
|
|
711
|
+
No instances matched this lookup.
|
|
541
712
|
</div>
|
|
542
713
|
<div v-for="r in lookupResults" :key="r.id" class="composable-tracker__lookup-row">
|
|
543
714
|
<span class="mono text-sm">{{ r.name }}</span>
|
|
@@ -609,6 +780,11 @@ function applyEdit() {
|
|
|
609
780
|
min-height: 0;
|
|
610
781
|
}
|
|
611
782
|
|
|
783
|
+
.composable-tracker__virtual-spacer {
|
|
784
|
+
width: 100%;
|
|
785
|
+
flex-shrink: 0;
|
|
786
|
+
}
|
|
787
|
+
|
|
612
788
|
.composable-tracker__card {
|
|
613
789
|
background: var(--bg3);
|
|
614
790
|
border: var(--tracker-border-width) solid var(--border);
|