nuxt-schema-org 6.1.3 → 6.2.1
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/dist/devtools/components/schema-org/SchemaValidator.vue +326 -0
- package/dist/devtools/lib/schema-org/rpc.ts +47 -0
- package/dist/devtools/lib/schema-org/state.ts +15 -0
- package/dist/devtools/lib/schema-org/util/schema-validation.ts +470 -0
- package/dist/devtools/nuxt.config.ts +7 -0
- package/dist/devtools/pages/schema-org/debug.vue +23 -0
- package/dist/devtools/pages/schema-org/docs.vue +3 -0
- package/dist/devtools/pages/schema-org/index.vue +8 -0
- package/dist/devtools/pages/schema-org/raw.vue +25 -0
- package/dist/devtools/pages/schema-org.vue +56 -0
- package/dist/module.cjs +28 -7
- package/dist/module.d.cts +1 -1
- package/dist/module.d.mts +1 -1
- package/dist/module.d.ts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +28 -7
- package/dist/schema.cjs +2147 -9
- package/dist/schema.d.cts +185 -1
- package/dist/schema.d.mts +185 -1
- package/dist/schema.d.ts +185 -1
- package/dist/schema.mjs +2035 -1
- package/dist/shared/nuxt-schema-org.BQ-jW5j5.d.cts +2770 -0
- package/dist/shared/nuxt-schema-org.BQ-jW5j5.d.mts +2770 -0
- package/dist/shared/nuxt-schema-org.BQ-jW5j5.d.ts +2770 -0
- package/dist/vendor/schema-org-v2/LICENSE +21 -0
- package/dist/vendor/schema-org-v2/chunks/index.mjs +23 -0
- package/dist/vendor/schema-org-v2/chunks/index10.mjs +63 -0
- package/dist/vendor/schema-org-v2/chunks/index11.mjs +35 -0
- package/dist/vendor/schema-org-v2/chunks/index12.mjs +48 -0
- package/dist/vendor/schema-org-v2/chunks/index13.mjs +36 -0
- package/dist/vendor/schema-org-v2/chunks/index14.mjs +25 -0
- package/dist/vendor/schema-org-v2/chunks/index15.mjs +37 -0
- package/dist/vendor/schema-org-v2/chunks/index16.mjs +27 -0
- package/dist/vendor/schema-org-v2/chunks/index17.mjs +34 -0
- package/dist/vendor/schema-org-v2/chunks/index18.mjs +32 -0
- package/dist/vendor/schema-org-v2/chunks/index19.mjs +31 -0
- package/dist/vendor/schema-org-v2/chunks/index2.mjs +12 -0
- package/dist/vendor/schema-org-v2/chunks/index20.mjs +31 -0
- package/dist/vendor/schema-org-v2/chunks/index21.mjs +30 -0
- package/dist/vendor/schema-org-v2/chunks/index22.mjs +30 -0
- package/dist/vendor/schema-org-v2/chunks/index23.mjs +82 -0
- package/dist/vendor/schema-org-v2/chunks/index24.mjs +14 -0
- package/dist/vendor/schema-org-v2/chunks/index25.mjs +33 -0
- package/dist/vendor/schema-org-v2/chunks/index26.mjs +31 -0
- package/dist/vendor/schema-org-v2/chunks/index27.mjs +29 -0
- package/dist/vendor/schema-org-v2/chunks/index28.mjs +12 -0
- package/dist/vendor/schema-org-v2/chunks/index29.mjs +46 -0
- package/dist/vendor/schema-org-v2/chunks/index3.mjs +317 -0
- package/dist/vendor/schema-org-v2/chunks/index30.mjs +53 -0
- package/dist/vendor/schema-org-v2/chunks/index31.mjs +41 -0
- package/dist/vendor/schema-org-v2/chunks/index32.mjs +26 -0
- package/dist/vendor/schema-org-v2/chunks/index33.mjs +47 -0
- package/dist/vendor/schema-org-v2/chunks/index34.mjs +29 -0
- package/dist/vendor/schema-org-v2/chunks/index35.mjs +34 -0
- package/dist/vendor/schema-org-v2/chunks/index36.mjs +33 -0
- package/dist/vendor/schema-org-v2/chunks/index37.mjs +34 -0
- package/dist/vendor/schema-org-v2/chunks/index38.mjs +51 -0
- package/dist/vendor/schema-org-v2/chunks/index39.mjs +17 -0
- package/dist/vendor/schema-org-v2/chunks/index4.mjs +54 -0
- package/dist/vendor/schema-org-v2/chunks/index40.mjs +29 -0
- package/dist/vendor/schema-org-v2/chunks/index5.mjs +31 -0
- package/dist/vendor/schema-org-v2/chunks/index6.mjs +29 -0
- package/dist/vendor/schema-org-v2/chunks/index7.mjs +35 -0
- package/dist/vendor/schema-org-v2/chunks/index8.mjs +18 -0
- package/dist/vendor/schema-org-v2/chunks/index9.mjs +20 -0
- package/dist/vendor/schema-org-v2/index.d.mts +32 -0
- package/dist/vendor/schema-org-v2/index.d.ts +32 -0
- package/dist/vendor/schema-org-v2/index.mjs +46 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.BR4WcSqZ.d.ts +21 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.Ba7D0Hp1.mjs +19 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.CAKsqzbX.d.ts +1022 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.CFcsqFfN.d.mts +1824 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.CFcsqFfN.d.ts +1824 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.CHbRCiep.mjs +52 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.Dryb3EoR.mjs +884 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.F44ipjVJ.mjs +27 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.UT1u1UYq.d.mts +1022 -0
- package/dist/vendor/schema-org-v2/shared/schema-org.oFHFm6my.d.mts +21 -0
- package/dist/vendor/schema-org-v2/vendor.json +4 -0
- package/dist/vendor/schema-org-v2/vue.d.mts +88 -0
- package/dist/vendor/schema-org-v2/vue.d.ts +88 -0
- package/dist/vendor/schema-org-v2/vue.mjs +262 -0
- package/dist/vendor/schema-org-v3/LICENSE +21 -0
- package/dist/vendor/schema-org-v3/index.d.mts +34 -0
- package/dist/vendor/schema-org-v3/index.d.ts +34 -0
- package/dist/vendor/schema-org-v3/index.mjs +4 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.D9o9UOh2.d.mts +11 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.DNQroCjX.d.mts +2770 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.DNQroCjX.d.ts +2770 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.DYFTMLZ0.mjs +2035 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.DjDPlqPB.d.mts +150 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.mKZYVZ3E.d.ts +150 -0
- package/dist/vendor/schema-org-v3/shared/schema-org.rS-TANmN.d.ts +11 -0
- package/dist/vendor/schema-org-v3/vendor.json +4 -0
- package/dist/vendor/schema-org-v3/vue.d.mts +100 -0
- package/dist/vendor/schema-org-v3/vue.d.ts +100 -0
- package/dist/vendor/schema-org-v3/vue.mjs +302 -0
- package/package.json +12 -12
- package/dist/devtools/200.html +0 -1
- package/dist/devtools/404.html +0 -1
- package/dist/devtools/_nuxt/B1DdtD2d.js +0 -30
- package/dist/devtools/_nuxt/BOpHBWj1.js +0 -1
- package/dist/devtools/_nuxt/BdQEHo9i.js +0 -1
- package/dist/devtools/_nuxt/BvDCh0XJ.js +0 -1
- package/dist/devtools/_nuxt/C6vgB62o.js +0 -1
- package/dist/devtools/_nuxt/CCjZBs6a.js +0 -3
- package/dist/devtools/_nuxt/CHHO6nw6.js +0 -1
- package/dist/devtools/_nuxt/CQr7N5ou.js +0 -6
- package/dist/devtools/_nuxt/CRABcd13.js +0 -1
- package/dist/devtools/_nuxt/Cy67RPmZ.js +0 -1
- package/dist/devtools/_nuxt/D-Oj-loM.js +0 -154
- package/dist/devtools/_nuxt/D6u4txBo.js +0 -1
- package/dist/devtools/_nuxt/DKZUJ--l.js +0 -1
- package/dist/devtools/_nuxt/DLXftbE1.js +0 -1
- package/dist/devtools/_nuxt/DevtoolsSnippet.CuW0O0Zu.css +0 -1
- package/dist/devtools/_nuxt/QcnxyHc5.js +0 -1
- package/dist/devtools/_nuxt/builds/latest.json +0 -1
- package/dist/devtools/_nuxt/builds/meta/b29085bd-1521-40f3-8c81-bdc818184bef.json +0 -1
- package/dist/devtools/_nuxt/entry.DprwPCib.css +0 -2
- package/dist/devtools/_nuxt/fira-code.Bc8wnsZt.woff2 +0 -0
- package/dist/devtools/_nuxt/hubot-sans.DLGyhQVu.woff2 +0 -0
- package/dist/devtools/_nuxt/pages.BpqfEWCe.css +0 -1
- package/dist/devtools/debug/index.html +0 -1
- package/dist/devtools/docs/index.html +0 -1
- package/dist/devtools/index.html +0 -1
- package/dist/devtools/nodes/index.html +0 -1
- package/dist/devtools/raw/index.html +0 -1
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
analyzeNodeProperties,
|
|
4
|
+
formatPropertyValue,
|
|
5
|
+
getNestedProperty,
|
|
6
|
+
getNodeDescription,
|
|
7
|
+
getNodeType,
|
|
8
|
+
getSchemaIcon,
|
|
9
|
+
googleRichResultsRequirements,
|
|
10
|
+
isRichResultType,
|
|
11
|
+
nodeToSchemaOrgLink,
|
|
12
|
+
validateGraph,
|
|
13
|
+
} from '../../lib/schema-org/util/schema-validation'
|
|
14
|
+
|
|
15
|
+
const { graph } = defineProps<{
|
|
16
|
+
graph: string
|
|
17
|
+
}>()
|
|
18
|
+
|
|
19
|
+
const parsed = computed(() => {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(graph)
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const validation = computed(() => {
|
|
29
|
+
if (!parsed.value)
|
|
30
|
+
return null
|
|
31
|
+
return validateGraph(parsed.value)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const overallStatus = computed(() => {
|
|
35
|
+
if (!validation.value)
|
|
36
|
+
return null
|
|
37
|
+
|
|
38
|
+
const { summary } = validation.value
|
|
39
|
+
|
|
40
|
+
if (summary.totalNodes === 0) {
|
|
41
|
+
return {
|
|
42
|
+
variant: 'warning' as const,
|
|
43
|
+
icon: 'carbon:warning',
|
|
44
|
+
message: 'No structured data nodes detected on this page.',
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (summary.totalErrors > 0) {
|
|
49
|
+
return {
|
|
50
|
+
variant: 'error' as const,
|
|
51
|
+
icon: 'carbon:close-filled',
|
|
52
|
+
message: `Found ${summary.totalErrors} missing required property${summary.totalErrors > 1 ? 'ies' : 'y'} across ${summary.totalNodes} node${summary.totalNodes > 1 ? 's' : ''}`,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (summary.totalWarnings > 0) {
|
|
57
|
+
return {
|
|
58
|
+
variant: 'warning' as const,
|
|
59
|
+
icon: 'carbon:warning',
|
|
60
|
+
message: `${summary.totalWarnings} missing recommended property${summary.totalWarnings > 1 ? 'ies' : 'y'}, but all required fields present`,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
variant: 'success' as const,
|
|
66
|
+
icon: 'carbon:checkmark-filled',
|
|
67
|
+
message: `All ${summary.totalNodes} node${summary.totalNodes > 1 ? 's' : ''} validated successfully.`,
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const displayNodes = computed(() => {
|
|
72
|
+
if (!validation.value)
|
|
73
|
+
return []
|
|
74
|
+
return validation.value.nodes.filter((node) => {
|
|
75
|
+
return getNodeType(node) !== 'Unknown'
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Per-card view mode: 'validate' or 'json'
|
|
80
|
+
const cardViewModes = reactive(new Map<number, 'validate' | 'json'>())
|
|
81
|
+
|
|
82
|
+
function getCardView(index: number): 'validate' | 'json' {
|
|
83
|
+
return cardViewModes.get(index) ?? 'validate'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function nodeCardClass(node: any) {
|
|
87
|
+
const type = getNodeType(node)
|
|
88
|
+
const requirements = googleRichResultsRequirements[type]
|
|
89
|
+
const analysis = analyzeNodeProperties(node)
|
|
90
|
+
|
|
91
|
+
if (requirements?.recommended?.length && analysis.missingRecommended.length === 0 && analysis.missingRequired.length === 0)
|
|
92
|
+
return 'node-card-success'
|
|
93
|
+
if (isRichResultType(type) && (analysis.missingRecommended.length > 0 || analysis.missingRequired.length > 0))
|
|
94
|
+
return 'node-card-active'
|
|
95
|
+
return ''
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const foundationOnlyTypes = new Set(['WebSite', 'WebPage', 'ImageObject'])
|
|
99
|
+
const hasOnlyFoundationSchemas = computed(() => {
|
|
100
|
+
return displayNodes.value.length > 0 && displayNodes.value.every(n => foundationOnlyTypes.has(getNodeType(n)))
|
|
101
|
+
})
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<div class="space-y-4 stagger-children">
|
|
106
|
+
<!-- Overall Status -->
|
|
107
|
+
<DevtoolsAlert
|
|
108
|
+
v-if="overallStatus"
|
|
109
|
+
:variant="overallStatus.variant"
|
|
110
|
+
:icon="overallStatus.icon"
|
|
111
|
+
>
|
|
112
|
+
<span class="font-medium">{{ overallStatus.message }}</span>
|
|
113
|
+
<template v-if="validation" #action>
|
|
114
|
+
<div class="flex items-center gap-2">
|
|
115
|
+
<UBadge>
|
|
116
|
+
{{ validation.summary.totalNodes }} nodes
|
|
117
|
+
</UBadge>
|
|
118
|
+
<UBadge v-if="validation.summary.richResultNodes > 0" color="primary">
|
|
119
|
+
{{ validation.summary.richResultNodes }} rich result eligible
|
|
120
|
+
</UBadge>
|
|
121
|
+
</div>
|
|
122
|
+
</template>
|
|
123
|
+
</DevtoolsAlert>
|
|
124
|
+
|
|
125
|
+
<!-- Foundation-only message -->
|
|
126
|
+
<DevtoolsEmptyState
|
|
127
|
+
v-if="hasOnlyFoundationSchemas"
|
|
128
|
+
title="Only foundation schemas detected"
|
|
129
|
+
description="Only WebSite, WebPage, and ImageObject found. Consider adding rich result schemas for better search visibility."
|
|
130
|
+
icon="carbon:information"
|
|
131
|
+
/>
|
|
132
|
+
|
|
133
|
+
<!-- Node Cards -->
|
|
134
|
+
<div v-for="(node, index) in displayNodes" :key="index" class="node-card" :class="nodeCardClass(node)">
|
|
135
|
+
<!-- Header -->
|
|
136
|
+
<div class="flex items-start justify-between mb-3">
|
|
137
|
+
<div class="flex items-center gap-2 min-w-0">
|
|
138
|
+
<UIcon
|
|
139
|
+
:name="getSchemaIcon(getNodeType(node))"
|
|
140
|
+
:class="isRichResultType(getNodeType(node)) ? 'text-[var(--seo-green)]' : 'text-[var(--color-text-muted)]'"
|
|
141
|
+
class="shrink-0"
|
|
142
|
+
/>
|
|
143
|
+
<span class="font-semibold text-sm">{{ getNodeType(node) }}</span>
|
|
144
|
+
<UBadge v-if="isRichResultType(getNodeType(node))" color="primary">
|
|
145
|
+
Rich Result
|
|
146
|
+
</UBadge>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="flex items-center shrink-0">
|
|
149
|
+
<!-- View mode toggle -->
|
|
150
|
+
<UButton
|
|
151
|
+
icon="carbon:checkmark-outline"
|
|
152
|
+
size="xs"
|
|
153
|
+
:variant="getCardView(index) === 'validate' ? 'subtle' : 'ghost'"
|
|
154
|
+
:color="getCardView(index) === 'validate' ? 'primary' : 'neutral'"
|
|
155
|
+
title="Validation view"
|
|
156
|
+
@click="cardViewModes.set(index, 'validate')"
|
|
157
|
+
/>
|
|
158
|
+
<UButton
|
|
159
|
+
icon="carbon:code"
|
|
160
|
+
size="xs"
|
|
161
|
+
:variant="getCardView(index) === 'json' ? 'subtle' : 'ghost'"
|
|
162
|
+
:color="getCardView(index) === 'json' ? 'primary' : 'neutral'"
|
|
163
|
+
title="JSON view"
|
|
164
|
+
@click="cardViewModes.set(index, 'json')"
|
|
165
|
+
/>
|
|
166
|
+
<!-- Docs link -->
|
|
167
|
+
<UButton
|
|
168
|
+
v-if="googleRichResultsRequirements[getNodeType(node)]"
|
|
169
|
+
icon="carbon:launch"
|
|
170
|
+
size="xs"
|
|
171
|
+
variant="ghost"
|
|
172
|
+
color="neutral"
|
|
173
|
+
:to="googleRichResultsRequirements[getNodeType(node)].documentationUrl"
|
|
174
|
+
target="_blank"
|
|
175
|
+
title="Google Rich Results docs"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<!-- Description / ID -->
|
|
181
|
+
<p v-if="getNodeDescription(node)" class="text-xs text-[var(--color-text-muted)] mb-3 truncate">
|
|
182
|
+
{{ getNodeDescription(node) }}
|
|
183
|
+
</p>
|
|
184
|
+
|
|
185
|
+
<!-- ===== Validate View ===== -->
|
|
186
|
+
<template v-if="getCardView(index) === 'validate'">
|
|
187
|
+
<!-- Validation Status -->
|
|
188
|
+
<DevtoolsAlert v-if="analyzeNodeProperties(node).missingRequired.length > 0" variant="error" class="mb-3">
|
|
189
|
+
<span class="font-medium">Missing required:</span>
|
|
190
|
+
<span class="ml-1 opacity-80">{{ analyzeNodeProperties(node).missingRequired.join(', ') }}</span>
|
|
191
|
+
</DevtoolsAlert>
|
|
192
|
+
|
|
193
|
+
<DevtoolsAlert
|
|
194
|
+
v-else-if="googleRichResultsRequirements[getNodeType(node)]?.recommended?.length > 0 && analyzeNodeProperties(node).missingRecommended.length === 0"
|
|
195
|
+
variant="success"
|
|
196
|
+
class="mb-3"
|
|
197
|
+
>
|
|
198
|
+
<span class="font-medium">Excellent!</span>
|
|
199
|
+
<span class="ml-1 opacity-80">All recommended properties present</span>
|
|
200
|
+
</DevtoolsAlert>
|
|
201
|
+
|
|
202
|
+
<DevtoolsAlert
|
|
203
|
+
v-else-if="analyzeNodeProperties(node).missingRecommended.length > 0 && analyzeNodeProperties(node).missingRecommended.length <= 5"
|
|
204
|
+
variant="warning"
|
|
205
|
+
class="mb-3"
|
|
206
|
+
>
|
|
207
|
+
<span class="font-medium">Missing recommended:</span>
|
|
208
|
+
<span class="ml-1 opacity-80">{{ analyzeNodeProperties(node).missingRecommended.slice(0, 5).join(', ') }}</span>
|
|
209
|
+
</DevtoolsAlert>
|
|
210
|
+
|
|
211
|
+
<!-- Property Checklists -->
|
|
212
|
+
<div class="space-y-3">
|
|
213
|
+
<!-- Required Properties -->
|
|
214
|
+
<div v-if="googleRichResultsRequirements[getNodeType(node)]?.required?.length > 0">
|
|
215
|
+
<h5 class="text-xs font-medium mb-2">
|
|
216
|
+
Required Properties
|
|
217
|
+
</h5>
|
|
218
|
+
<div class="space-y-1.5">
|
|
219
|
+
<div
|
|
220
|
+
v-for="prop in googleRichResultsRequirements[getNodeType(node)].required"
|
|
221
|
+
:key="prop"
|
|
222
|
+
class="flex items-center gap-2 text-xs"
|
|
223
|
+
>
|
|
224
|
+
<UIcon
|
|
225
|
+
:name="getNestedProperty(node, prop) ? 'carbon:checkmark-filled' : 'carbon:close-filled'"
|
|
226
|
+
:class="getNestedProperty(node, prop) ? 'text-green-500' : 'text-red-500'"
|
|
227
|
+
/>
|
|
228
|
+
<span class="font-mono text-[var(--seo-green)]">{{ prop }}:</span>
|
|
229
|
+
<span v-if="getNestedProperty(node, prop)" class="text-[var(--color-text-subtle)] truncate">
|
|
230
|
+
{{ formatPropertyValue(getNestedProperty(node, prop)) }}
|
|
231
|
+
</span>
|
|
232
|
+
<span v-else class="text-red-500">missing</span>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Recommended Properties -->
|
|
238
|
+
<DevtoolsSection
|
|
239
|
+
v-if="googleRichResultsRequirements[getNodeType(node)]?.recommended?.length > 0"
|
|
240
|
+
:text="`Recommended Properties (${Object.keys(analyzeNodeProperties(node).presentProperties).filter(p => googleRichResultsRequirements[getNodeType(node)]?.recommended.includes(p)).length}/${googleRichResultsRequirements[getNodeType(node)]?.recommended.length})`"
|
|
241
|
+
:open="false"
|
|
242
|
+
>
|
|
243
|
+
<div class="space-y-1.5">
|
|
244
|
+
<div
|
|
245
|
+
v-for="prop in googleRichResultsRequirements[getNodeType(node)].recommended"
|
|
246
|
+
:key="prop"
|
|
247
|
+
class="flex items-center gap-2 text-xs"
|
|
248
|
+
>
|
|
249
|
+
<UIcon
|
|
250
|
+
:name="getNestedProperty(node, prop) ? 'carbon:checkmark' : 'carbon:subtract'"
|
|
251
|
+
:class="getNestedProperty(node, prop) ? 'text-green-500' : 'text-[var(--color-text-subtle)]'"
|
|
252
|
+
/>
|
|
253
|
+
<span class="font-mono text-[var(--seo-green)] opacity-70">{{ prop }}:</span>
|
|
254
|
+
<span v-if="getNestedProperty(node, prop)" class="text-[var(--color-text-subtle)] truncate">
|
|
255
|
+
{{ formatPropertyValue(getNestedProperty(node, prop)) }}
|
|
256
|
+
</span>
|
|
257
|
+
<span v-else class="text-[var(--color-text-subtle)] opacity-50">not set</span>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</DevtoolsSection>
|
|
261
|
+
</div>
|
|
262
|
+
</template>
|
|
263
|
+
|
|
264
|
+
<!-- ===== JSON View ===== -->
|
|
265
|
+
<template v-else>
|
|
266
|
+
<div class="json-view">
|
|
267
|
+
<DevtoolsSnippet
|
|
268
|
+
:code="JSON.stringify(node, null, 2)"
|
|
269
|
+
lang="json"
|
|
270
|
+
:label="getNodeType(node)"
|
|
271
|
+
/>
|
|
272
|
+
<!-- Schema.org / Rich Results links -->
|
|
273
|
+
<div class="flex items-center gap-1 mt-2">
|
|
274
|
+
<UButton
|
|
275
|
+
:to="nodeToSchemaOrgLink(getNodeType(node)).schemaOrg"
|
|
276
|
+
target="_blank"
|
|
277
|
+
size="xs"
|
|
278
|
+
variant="ghost"
|
|
279
|
+
color="neutral"
|
|
280
|
+
trailing-icon="carbon:launch"
|
|
281
|
+
label="Schema.org"
|
|
282
|
+
/>
|
|
283
|
+
<UButton
|
|
284
|
+
v-if="nodeToSchemaOrgLink(getNodeType(node)).googlePage"
|
|
285
|
+
:to="nodeToSchemaOrgLink(getNodeType(node)).googlePage!"
|
|
286
|
+
target="_blank"
|
|
287
|
+
size="xs"
|
|
288
|
+
variant="ghost"
|
|
289
|
+
color="neutral"
|
|
290
|
+
trailing-icon="carbon:launch"
|
|
291
|
+
label="Rich Results"
|
|
292
|
+
/>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
</template>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</template>
|
|
299
|
+
|
|
300
|
+
<style scoped>
|
|
301
|
+
.node-card {
|
|
302
|
+
background: var(--color-surface-elevated);
|
|
303
|
+
border: 1px solid var(--color-border);
|
|
304
|
+
border-radius: var(--radius-lg);
|
|
305
|
+
padding: 1rem;
|
|
306
|
+
transition: border-color 200ms ease;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.node-card:hover {
|
|
310
|
+
border-color: var(--color-neutral-300);
|
|
311
|
+
}
|
|
312
|
+
.dark .node-card:hover {
|
|
313
|
+
border-color: var(--color-neutral-700);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.node-card-success {
|
|
317
|
+
border-color: oklch(65% 0.2 145 / 0.3);
|
|
318
|
+
}
|
|
319
|
+
.node-card-success:hover {
|
|
320
|
+
border-color: oklch(65% 0.2 145 / 0.5);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.node-card-active {
|
|
324
|
+
border-color: oklch(65% 0.15 250 / 0.2);
|
|
325
|
+
}
|
|
326
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { onDevtoolsClientConnected, useDevtoolsClient } from '@nuxt/devtools-kit/iframe-client'
|
|
2
|
+
import { useDevtoolsConnection } from 'nuxtseo-layer-devtools/composables/rpc'
|
|
3
|
+
import { refreshTime } from 'nuxtseo-layer-devtools/composables/state'
|
|
4
|
+
import { ref, watch } from 'vue'
|
|
5
|
+
|
|
6
|
+
// Mirror the layer's workaround: initialize the devtools client ref before any
|
|
7
|
+
// onDevtoolsClientConnected call, or the devtools-kit alpha getter on
|
|
8
|
+
// window.__NUXT_DEVTOOLS__ reads an uninitialized clientRef and throws
|
|
9
|
+
// "Cannot read properties of undefined (reading 'value')", crashing the panel.
|
|
10
|
+
useDevtoolsClient()
|
|
11
|
+
|
|
12
|
+
// The host's rendered `<script data-nuxt-schema-org>` JSON-LD is the source of
|
|
13
|
+
// truth for the graph. Reading it straight from the host document is resilient
|
|
14
|
+
// across unhead majors: v3 dropped the v2 `resolveTags()` we used to call on the
|
|
15
|
+
// injected head instance, which silently left this panel empty.
|
|
16
|
+
export const schemaOrgGraph = ref<string>('loading')
|
|
17
|
+
|
|
18
|
+
let hostDocument: Document | undefined
|
|
19
|
+
|
|
20
|
+
function fetchGraph(): void {
|
|
21
|
+
if (!hostDocument)
|
|
22
|
+
return
|
|
23
|
+
const script = hostDocument.querySelector('script[data-nuxt-schema-org]')
|
|
24
|
+
// Fall back to an empty-object string (not '') so the layout's loading guard
|
|
25
|
+
// clears and the validator shows its "no nodes" state instead of spinning.
|
|
26
|
+
schemaOrgGraph.value = script?.textContent || '{}'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The layer's useDevtoolsConnection only surfaces app provides, not the host DOM,
|
|
30
|
+
// so open our own devtools-kit client handle to reach the host document via the
|
|
31
|
+
// mounted Vue app's container element.
|
|
32
|
+
onDevtoolsClientConnected((client) => {
|
|
33
|
+
const nuxt: any = client.host?.nuxt
|
|
34
|
+
hostDocument = nuxt?.vueApp?._container?.ownerDocument
|
|
35
|
+
|| nuxt?.vueApp?._instance?.vnode?.el?.ownerDocument
|
|
36
|
+
fetchGraph()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
useDevtoolsConnection({
|
|
40
|
+
// Layer refreshes data on route change; re-read the graph once nav settles.
|
|
41
|
+
onRouteChange() {
|
|
42
|
+
setTimeout(fetchGraph, 100)
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// The Refresh button bumps the shared clock; re-read the rendered graph with it.
|
|
47
|
+
watch(refreshTime, () => fetchGraph())
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { appFetch } from 'nuxtseo-layer-devtools/composables/rpc'
|
|
2
|
+
import { refreshTime } from 'nuxtseo-layer-devtools/composables/state'
|
|
3
|
+
import { useAsyncData } from '#imports'
|
|
4
|
+
|
|
5
|
+
export function fetchGlobalDebug() {
|
|
6
|
+
return useAsyncData<{
|
|
7
|
+
nitroOrigin: string
|
|
8
|
+
runtimeConfig: any
|
|
9
|
+
}>(() => {
|
|
10
|
+
// @ts-expect-error untyped
|
|
11
|
+
return appFetch.value('/__schema-org__/debug.json')
|
|
12
|
+
}, {
|
|
13
|
+
watch: [refreshTime],
|
|
14
|
+
})
|
|
15
|
+
}
|