nuxtseo-shared 0.1.2 → 0.1.4
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/content.d.mts +8 -3
- package/dist/content.mjs +2 -1
- package/dist/layer-devtools/app.config.ts +27 -0
- package/dist/layer-devtools/assets/css/global.css +377 -0
- package/dist/layer-devtools/components/DevtoolsError.vue +78 -0
- package/dist/layer-devtools/components/DevtoolsLoading.vue +8 -0
- package/dist/layer-devtools/components/DevtoolsProductionError.vue +41 -0
- package/dist/layer-devtools/components/OCodeBlock.vue +23 -0
- package/dist/{runtime/app → layer-devtools}/components/OSectionBlock.vue +13 -22
- package/dist/layer-devtools/composables/rpc.ts +39 -0
- package/dist/layer-devtools/composables/shiki.ts +46 -0
- package/dist/layer-devtools/composables/state.ts +33 -0
- package/dist/layer-devtools/nuxt.config.ts +26 -0
- package/dist/module.d.mts +14 -0
- package/dist/module.mjs +33 -0
- package/dist/pro.d.mts +0 -1
- package/dist/telemetry.d.mts +11 -0
- package/dist/telemetry.mjs +49 -0
- package/package.json +7 -17
- package/dist/runtime/app/components/OCodeBlock.vue +0 -28
- package/dist/runtime/app/composables/rpc.d.mts +0 -16
- package/dist/runtime/app/composables/rpc.mjs +0 -28
- package/dist/runtime/app/composables/shiki.d.mts +0 -8
- package/dist/runtime/app/composables/shiki.mjs +0 -31
- package/dist/runtime/app/index.d.mts +0 -2
- package/dist/runtime/app/index.mjs +0 -2
- /package/dist/{runtime/app → layer-devtools}/components/NuxtSeoLogo.vue +0 -0
package/dist/content.d.mts
CHANGED
|
@@ -25,7 +25,7 @@ declare function withEditor<T extends ZodTypeAny>(schema: T, config: ContentEdit
|
|
|
25
25
|
* Only use for fields that genuinely don't work in a form (freeform JSON, deeply nested arrays).
|
|
26
26
|
*/
|
|
27
27
|
declare function withEditorHidden<T extends ZodTypeAny>(schema: T): T;
|
|
28
|
-
interface DefineContentSchemaConfig<TSchema extends ZodTypeAny> {
|
|
28
|
+
interface DefineContentSchemaConfig<TSchema extends ZodTypeAny, TDefineOptions extends ContentSchemaOptions = ContentSchemaOptions> {
|
|
29
29
|
/**
|
|
30
30
|
* The field name used in frontmatter (e.g. 'robots', 'sitemap', 'ogImage').
|
|
31
31
|
*/
|
|
@@ -43,6 +43,11 @@ interface DefineContentSchemaConfig<TSchema extends ZodTypeAny> {
|
|
|
43
43
|
* Documentation URL for migration guidance.
|
|
44
44
|
*/
|
|
45
45
|
docsUrl?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Hook called when `defineSchema()` is invoked. Use for validation or
|
|
48
|
+
* registering module-specific side effects (e.g. sitemap filter/onUrl).
|
|
49
|
+
*/
|
|
50
|
+
onDefineSchema?: (options: TDefineOptions) => void;
|
|
46
51
|
}
|
|
47
52
|
/**
|
|
48
53
|
* Factory for creating a module's `define*Schema()` and deprecated `as*Collection()` exports.
|
|
@@ -66,8 +71,8 @@ interface DefineContentSchemaConfig<TSchema extends ZodTypeAny> {
|
|
|
66
71
|
*
|
|
67
72
|
* export { defineSchema as defineRobotsSchema, asCollection as asRobotsCollection, schema }
|
|
68
73
|
*/
|
|
69
|
-
declare function createContentSchemaFactory<TSchema extends ZodTypeAny>(config: DefineContentSchemaConfig<TSchema>, defaultZ: ZodInstance): {
|
|
70
|
-
defineSchema: (options?:
|
|
74
|
+
declare function createContentSchemaFactory<TSchema extends ZodTypeAny, TDefineOptions extends ContentSchemaOptions = ContentSchemaOptions>(config: DefineContentSchemaConfig<TSchema, TDefineOptions>, defaultZ: ZodInstance): {
|
|
75
|
+
defineSchema: (options?: TDefineOptions) => TSchema;
|
|
71
76
|
asCollection: <T>(collection: any) => T;
|
|
72
77
|
schema: z.ZodObject<{
|
|
73
78
|
[x: string]: TSchema;
|
package/dist/content.mjs
CHANGED
|
@@ -37,10 +37,11 @@ function withEditorHidden(schema) {
|
|
|
37
37
|
* export { defineSchema as defineRobotsSchema, asCollection as asRobotsCollection, schema }
|
|
38
38
|
*/
|
|
39
39
|
function createContentSchemaFactory(config, defaultZ) {
|
|
40
|
-
const { fieldName, buildSchema, label, docsUrl } = config;
|
|
40
|
+
const { fieldName, buildSchema, label, docsUrl, onDefineSchema } = config;
|
|
41
41
|
const defaultSchema = buildSchema(defaultZ);
|
|
42
42
|
const schemaObject = defaultZ.object({ [fieldName]: defaultSchema });
|
|
43
43
|
function defineSchema(options) {
|
|
44
|
+
if (options && onDefineSchema) onDefineSchema(options);
|
|
44
45
|
const _z = options?.z ?? defaultZ;
|
|
45
46
|
if (_z === defaultZ) return defaultSchema;
|
|
46
47
|
return buildSchema(_z);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default defineAppConfig({
|
|
2
|
+
ui: {
|
|
3
|
+
colors: {
|
|
4
|
+
primary: 'green',
|
|
5
|
+
neutral: 'neutral',
|
|
6
|
+
},
|
|
7
|
+
button: {
|
|
8
|
+
defaultVariants: {
|
|
9
|
+
color: 'neutral',
|
|
10
|
+
variant: 'ghost',
|
|
11
|
+
size: 'sm',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
badge: {
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
color: 'neutral',
|
|
17
|
+
variant: 'subtle',
|
|
18
|
+
size: 'xs',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
tooltip: {
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
delayDuration: 0,
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "@nuxt/ui";
|
|
3
|
+
|
|
4
|
+
@theme {
|
|
5
|
+
--font-sans: 'Hubot Sans', ui-sans-serif, system-ui, sans-serif;
|
|
6
|
+
--font-mono: 'Fira Code', ui-monospace, monospace;
|
|
7
|
+
|
|
8
|
+
/* Neutral palette, true gray with minimal chroma */
|
|
9
|
+
--color-neutral-50: oklch(98% 0.002 260);
|
|
10
|
+
--color-neutral-100: oklch(96% 0.003 260);
|
|
11
|
+
--color-neutral-200: oklch(91% 0.005 260);
|
|
12
|
+
--color-neutral-300: oklch(85% 0.008 260);
|
|
13
|
+
--color-neutral-400: oklch(65% 0.015 260);
|
|
14
|
+
--color-neutral-500: oklch(50% 0.018 260);
|
|
15
|
+
--color-neutral-600: oklch(40% 0.015 260);
|
|
16
|
+
--color-neutral-700: oklch(30% 0.012 260);
|
|
17
|
+
--color-neutral-800: oklch(20% 0.01 260);
|
|
18
|
+
--color-neutral-900: oklch(14% 0.008 260);
|
|
19
|
+
--color-neutral-950: oklch(9% 0.005 260);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:root {
|
|
23
|
+
/* Brand colors - Nuxt SEO green */
|
|
24
|
+
--seo-green: oklch(65% 0.2 145);
|
|
25
|
+
|
|
26
|
+
/* Semantic colors */
|
|
27
|
+
--color-surface: var(--color-neutral-50);
|
|
28
|
+
--color-surface-elevated: white;
|
|
29
|
+
--color-surface-sunken: var(--color-neutral-100);
|
|
30
|
+
--color-border: var(--color-neutral-200);
|
|
31
|
+
--color-border-subtle: var(--color-neutral-100);
|
|
32
|
+
--color-text: var(--color-neutral-900);
|
|
33
|
+
--color-text-muted: var(--color-neutral-500);
|
|
34
|
+
--color-text-subtle: var(--color-neutral-400);
|
|
35
|
+
|
|
36
|
+
--radius-sm: 6px;
|
|
37
|
+
--radius-md: 10px;
|
|
38
|
+
--radius-lg: 14px;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dark {
|
|
42
|
+
--color-surface: var(--color-neutral-950);
|
|
43
|
+
--color-surface-elevated: var(--color-neutral-900);
|
|
44
|
+
--color-surface-sunken: oklch(7% 0.004 260);
|
|
45
|
+
--color-border: var(--color-neutral-800);
|
|
46
|
+
--color-border-subtle: oklch(16% 0.008 260);
|
|
47
|
+
--color-text: var(--color-neutral-100);
|
|
48
|
+
--color-text-muted: var(--color-neutral-400);
|
|
49
|
+
--color-text-subtle: var(--color-neutral-500);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Base styles */
|
|
53
|
+
body {
|
|
54
|
+
-webkit-font-smoothing: antialiased;
|
|
55
|
+
-moz-osx-font-smoothing: grayscale;
|
|
56
|
+
font-family: var(--font-sans);
|
|
57
|
+
font-feature-settings: 'liga' 1, 'calt' 1;
|
|
58
|
+
background: var(--color-surface);
|
|
59
|
+
color: var(--color-text);
|
|
60
|
+
line-height: 1.6;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Typography */
|
|
64
|
+
p, h1, h2, h3, h4, h5, h6 {
|
|
65
|
+
overflow-wrap: break-word;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
p {
|
|
69
|
+
text-wrap: pretty;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
h1, h2, h3, h4, h5, h6 {
|
|
73
|
+
text-wrap: balance;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
letter-spacing: -0.01em;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Scrollbar styling */
|
|
79
|
+
* {
|
|
80
|
+
scrollbar-width: thin;
|
|
81
|
+
scrollbar-color: oklch(50% 0.015 260 / 0.25) transparent;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
::-webkit-scrollbar {
|
|
85
|
+
width: 8px;
|
|
86
|
+
height: 8px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
::-webkit-scrollbar-track {
|
|
90
|
+
background: transparent;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
::-webkit-scrollbar-thumb {
|
|
94
|
+
background-color: oklch(50% 0.015 260 / 0.25);
|
|
95
|
+
border-radius: 4px;
|
|
96
|
+
border: 2px solid transparent;
|
|
97
|
+
background-clip: content-box;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
::-webkit-scrollbar-thumb:hover {
|
|
101
|
+
background-color: oklch(50% 0.015 260 / 0.4);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.dark ::-webkit-scrollbar-thumb {
|
|
105
|
+
background-color: oklch(55% 0.015 260 / 0.2);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.dark ::-webkit-scrollbar-thumb:hover {
|
|
109
|
+
background-color: oklch(55% 0.015 260 / 0.35);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Gradient background effect */
|
|
113
|
+
.gradient-bg {
|
|
114
|
+
position: fixed;
|
|
115
|
+
inset: 0;
|
|
116
|
+
z-index: -1;
|
|
117
|
+
pointer-events: none;
|
|
118
|
+
opacity: 0.6;
|
|
119
|
+
background:
|
|
120
|
+
radial-gradient(ellipse 80% 50% at 50% -20%, oklch(85% 0.12 145 / 0.15), transparent),
|
|
121
|
+
radial-gradient(ellipse 60% 40% at 100% 0%, oklch(80% 0.1 200 / 0.08), transparent);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.dark .gradient-bg {
|
|
125
|
+
opacity: 0.4;
|
|
126
|
+
background:
|
|
127
|
+
radial-gradient(ellipse 80% 50% at 50% -20%, oklch(40% 0.1 145 / 0.2), transparent),
|
|
128
|
+
radial-gradient(ellipse 60% 40% at 100% 0%, oklch(35% 0.08 200 / 0.15), transparent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Animation utilities */
|
|
132
|
+
@keyframes fade-up {
|
|
133
|
+
from {
|
|
134
|
+
opacity: 0;
|
|
135
|
+
transform: translateY(8px);
|
|
136
|
+
}
|
|
137
|
+
to {
|
|
138
|
+
opacity: 1;
|
|
139
|
+
transform: translateY(0);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@keyframes scale-in {
|
|
144
|
+
from {
|
|
145
|
+
opacity: 0;
|
|
146
|
+
transform: scale(0.95);
|
|
147
|
+
}
|
|
148
|
+
to {
|
|
149
|
+
opacity: 1;
|
|
150
|
+
transform: scale(1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@keyframes spin {
|
|
155
|
+
from { transform: rotate(0deg); }
|
|
156
|
+
to { transform: rotate(360deg); }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.animate-fade-up {
|
|
160
|
+
animation: fade-up 400ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.animate-scale-in {
|
|
164
|
+
animation: scale-in 300ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.animate-spin {
|
|
168
|
+
animation: spin 1s linear infinite;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Stagger children animations */
|
|
172
|
+
.stagger-children > * {
|
|
173
|
+
animation: fade-up 400ms cubic-bezier(0.22, 1, 0.36, 1) backwards;
|
|
174
|
+
}
|
|
175
|
+
.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
|
|
176
|
+
.stagger-children > *:nth-child(2) { animation-delay: 50ms; }
|
|
177
|
+
.stagger-children > *:nth-child(3) { animation-delay: 100ms; }
|
|
178
|
+
.stagger-children > *:nth-child(4) { animation-delay: 150ms; }
|
|
179
|
+
.stagger-children > *:nth-child(5) { animation-delay: 200ms; }
|
|
180
|
+
.stagger-children > *:nth-child(6) { animation-delay: 250ms; }
|
|
181
|
+
|
|
182
|
+
/* Focus styles */
|
|
183
|
+
:focus-visible {
|
|
184
|
+
outline: 2px solid var(--seo-green);
|
|
185
|
+
outline-offset: 2px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Base backgrounds */
|
|
189
|
+
.bg-base {
|
|
190
|
+
background: var(--color-surface);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Card styling */
|
|
194
|
+
.card {
|
|
195
|
+
background: var(--color-surface-elevated);
|
|
196
|
+
border: 1px solid var(--color-border);
|
|
197
|
+
border-radius: var(--radius-lg);
|
|
198
|
+
transition: border-color 200ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 200ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.card:hover {
|
|
202
|
+
border-color: var(--color-neutral-300);
|
|
203
|
+
box-shadow: 0 4px 20px oklch(0% 0 0 / 0.06);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.dark .card:hover {
|
|
207
|
+
border-color: var(--color-neutral-700);
|
|
208
|
+
box-shadow: 0 4px 20px oklch(0% 0 0 / 0.3);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Glass effect */
|
|
212
|
+
.glass {
|
|
213
|
+
background: oklch(100% 0 0 / 0.8);
|
|
214
|
+
backdrop-filter: blur(16px) saturate(180%);
|
|
215
|
+
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.dark .glass {
|
|
219
|
+
background: oklch(9% 0.005 260 / 0.85);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Code block styling */
|
|
223
|
+
.code-block {
|
|
224
|
+
background: var(--color-surface-sunken);
|
|
225
|
+
border: 1px solid var(--color-border-subtle);
|
|
226
|
+
border-radius: var(--radius-md);
|
|
227
|
+
overflow-x: auto;
|
|
228
|
+
font-family: var(--font-mono);
|
|
229
|
+
font-size: 13px;
|
|
230
|
+
line-height: 1.7;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.code-block .shiki {
|
|
234
|
+
background: transparent !important;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.dark .shiki,
|
|
238
|
+
.dark .shiki span {
|
|
239
|
+
color: var(--shiki-dark) !important;
|
|
240
|
+
background-color: transparent !important;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.code-block-lines .shiki code .line::before {
|
|
244
|
+
display: none;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Status indicators */
|
|
248
|
+
.status-enabled {
|
|
249
|
+
display: inline-flex;
|
|
250
|
+
align-items: center;
|
|
251
|
+
gap: 0.375rem;
|
|
252
|
+
padding: 0.25rem 0.625rem;
|
|
253
|
+
font-size: 0.75rem;
|
|
254
|
+
font-weight: 500;
|
|
255
|
+
border-radius: var(--radius-sm);
|
|
256
|
+
background: oklch(75% 0.15 145 / 0.12);
|
|
257
|
+
color: oklch(50% 0.15 145);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.dark .status-enabled {
|
|
261
|
+
background: oklch(50% 0.15 145 / 0.15);
|
|
262
|
+
color: oklch(75% 0.18 145);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.status-disabled {
|
|
266
|
+
display: inline-flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
gap: 0.375rem;
|
|
269
|
+
padding: 0.25rem 0.625rem;
|
|
270
|
+
font-size: 0.75rem;
|
|
271
|
+
font-weight: 500;
|
|
272
|
+
border-radius: var(--radius-sm);
|
|
273
|
+
background: oklch(65% 0.12 25 / 0.1);
|
|
274
|
+
color: oklch(55% 0.15 25);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.dark .status-disabled {
|
|
278
|
+
background: oklch(45% 0.1 25 / 0.15);
|
|
279
|
+
color: oklch(70% 0.12 25);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* Link with external indicator */
|
|
283
|
+
.link-external {
|
|
284
|
+
display: inline-flex;
|
|
285
|
+
align-items: center;
|
|
286
|
+
gap: 0.25rem;
|
|
287
|
+
color: var(--seo-green);
|
|
288
|
+
text-decoration: none;
|
|
289
|
+
font-weight: 500;
|
|
290
|
+
transition: all 150ms ease;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.link-external:hover {
|
|
294
|
+
text-decoration: underline;
|
|
295
|
+
text-underline-offset: 2px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.link-external::after {
|
|
299
|
+
content: '\2197';
|
|
300
|
+
font-size: 0.75em;
|
|
301
|
+
opacity: 0.6;
|
|
302
|
+
transition: transform 150ms ease;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.link-external:hover::after {
|
|
306
|
+
transform: translate(1px, -1px);
|
|
307
|
+
opacity: 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* Hint/tip callout */
|
|
311
|
+
.hint-callout {
|
|
312
|
+
display: flex;
|
|
313
|
+
gap: 0.75rem;
|
|
314
|
+
padding: 0.875rem 1rem;
|
|
315
|
+
border-radius: var(--radius-md);
|
|
316
|
+
background: oklch(85% 0.08 200 / 0.08);
|
|
317
|
+
border: 1px solid oklch(70% 0.08 200 / 0.15);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.dark .hint-callout {
|
|
321
|
+
background: oklch(30% 0.05 200 / 0.15);
|
|
322
|
+
border-color: oklch(45% 0.06 200 / 0.2);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.hint-callout-icon {
|
|
326
|
+
flex-shrink: 0;
|
|
327
|
+
color: oklch(55% 0.12 200);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.dark .hint-callout-icon {
|
|
331
|
+
color: oklch(70% 0.1 200);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* Panel grid pattern background with radial fade */
|
|
335
|
+
.panel-grids {
|
|
336
|
+
background-color: var(--color-surface-sunken);
|
|
337
|
+
position: relative;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.panel-grids::before {
|
|
341
|
+
content: '';
|
|
342
|
+
position: absolute;
|
|
343
|
+
inset: 0;
|
|
344
|
+
z-index: 0;
|
|
345
|
+
background-image:
|
|
346
|
+
linear-gradient(var(--color-border-subtle) 1px, transparent 1px),
|
|
347
|
+
linear-gradient(90deg, var(--color-border-subtle) 1px, transparent 1px);
|
|
348
|
+
background-size: 24px 24px;
|
|
349
|
+
background-position: -1px -1px;
|
|
350
|
+
mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, black 20%, transparent 70%);
|
|
351
|
+
-webkit-mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, black 20%, transparent 70%);
|
|
352
|
+
pointer-events: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.panel-grids > * {
|
|
356
|
+
position: relative;
|
|
357
|
+
z-index: 1;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* Reduced motion */
|
|
361
|
+
@media (prefers-reduced-motion: reduce) {
|
|
362
|
+
.animate-fade-up,
|
|
363
|
+
.animate-scale-in,
|
|
364
|
+
.animate-spin,
|
|
365
|
+
.stagger-children > * {
|
|
366
|
+
animation: none;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/* Subtle text selection */
|
|
371
|
+
::selection {
|
|
372
|
+
background: oklch(65% 0.2 145 / 0.25);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.dark ::selection {
|
|
376
|
+
background: oklch(65% 0.2 145 / 0.35);
|
|
377
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { icon = 'carbon:warning', title = 'Something went wrong' } = defineProps<{
|
|
3
|
+
icon?: string
|
|
4
|
+
title?: string
|
|
5
|
+
error?: string | Error | null
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div class="devtools-error">
|
|
11
|
+
<div class="devtools-error-icon-wrap">
|
|
12
|
+
<UIcon :name="icon" class="devtools-error-icon" />
|
|
13
|
+
</div>
|
|
14
|
+
<p class="devtools-error-title">
|
|
15
|
+
{{ title }}
|
|
16
|
+
</p>
|
|
17
|
+
<p v-if="error" class="devtools-error-message">
|
|
18
|
+
{{ typeof error === 'string' ? error : error.message }}
|
|
19
|
+
</p>
|
|
20
|
+
<div v-if="$slots.default" class="devtools-error-actions">
|
|
21
|
+
<slot />
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<style scoped>
|
|
27
|
+
.devtools-error {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
gap: 0.75rem;
|
|
33
|
+
padding: 3rem 1.5rem;
|
|
34
|
+
text-align: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.devtools-error-icon-wrap {
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
width: 3rem;
|
|
42
|
+
height: 3rem;
|
|
43
|
+
border-radius: var(--radius-lg);
|
|
44
|
+
background: oklch(65% 0.15 25 / 0.1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.dark .devtools-error-icon-wrap {
|
|
48
|
+
background: oklch(45% 0.1 25 / 0.15);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.devtools-error-icon {
|
|
52
|
+
font-size: 1.5rem;
|
|
53
|
+
color: oklch(60% 0.18 25);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dark .devtools-error-icon {
|
|
57
|
+
color: oklch(70% 0.15 25);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.devtools-error-title {
|
|
61
|
+
font-size: 0.875rem;
|
|
62
|
+
font-weight: 600;
|
|
63
|
+
color: var(--color-text);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.devtools-error-message {
|
|
67
|
+
font-size: 0.75rem;
|
|
68
|
+
color: var(--color-text-muted);
|
|
69
|
+
max-width: 20rem;
|
|
70
|
+
line-height: 1.5;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.devtools-error-actions {
|
|
74
|
+
display: flex;
|
|
75
|
+
gap: 0.5rem;
|
|
76
|
+
margin-top: 0.25rem;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { previewSource, productionUrl } from '../composables/state'
|
|
3
|
+
|
|
4
|
+
const { error } = defineProps<{
|
|
5
|
+
error?: string | Error | null
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<DevtoolsError
|
|
11
|
+
icon="carbon:cloud-offline"
|
|
12
|
+
title="Production site unreachable"
|
|
13
|
+
:error="error"
|
|
14
|
+
>
|
|
15
|
+
<template v-if="!error">
|
|
16
|
+
<p class="text-xs text-[var(--color-text-muted)] max-w-xs leading-relaxed">
|
|
17
|
+
Could not connect to <code class="prod-url">{{ productionUrl }}</code>.
|
|
18
|
+
Check that the site is deployed and accessible.
|
|
19
|
+
</p>
|
|
20
|
+
</template>
|
|
21
|
+
<UButton
|
|
22
|
+
variant="soft"
|
|
23
|
+
size="xs"
|
|
24
|
+
icon="carbon:laptop"
|
|
25
|
+
@click="previewSource = 'local'"
|
|
26
|
+
>
|
|
27
|
+
Switch to local
|
|
28
|
+
</UButton>
|
|
29
|
+
</DevtoolsError>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<style scoped>
|
|
33
|
+
.prod-url {
|
|
34
|
+
font-family: var(--font-mono);
|
|
35
|
+
font-size: 0.6875rem;
|
|
36
|
+
padding: 0.125rem 0.375rem;
|
|
37
|
+
border-radius: var(--radius-sm);
|
|
38
|
+
background: var(--color-surface-sunken);
|
|
39
|
+
border: 1px solid var(--color-border-subtle);
|
|
40
|
+
}
|
|
41
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useRenderCodeHighlight } from '../composables/shiki'
|
|
3
|
+
|
|
4
|
+
const { code, lang, lines = false, transformRendered } = defineProps<{
|
|
5
|
+
code: string
|
|
6
|
+
lang: 'json' | 'xml' | 'js'
|
|
7
|
+
lines?: boolean
|
|
8
|
+
transformRendered?: (code: string) => string
|
|
9
|
+
}>()
|
|
10
|
+
|
|
11
|
+
const rendered = computed(() => {
|
|
12
|
+
const highlight = useRenderCodeHighlight(code, lang)
|
|
13
|
+
return transformRendered ? transformRendered(highlight.value || '') : highlight.value
|
|
14
|
+
})
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<template>
|
|
18
|
+
<pre
|
|
19
|
+
class="code-block p-5"
|
|
20
|
+
:class="lines ? 'code-block-lines' : ''"
|
|
21
|
+
v-html="rendered"
|
|
22
|
+
/>
|
|
23
|
+
</template>
|
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
containerClass: '',
|
|
17
|
-
open: true,
|
|
18
|
-
padding: true,
|
|
19
|
-
collapse: true,
|
|
20
|
-
},
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
const open = useVModel(props, 'open')
|
|
2
|
+
const { icon, text, description, containerClass = '', collapse = true, padding = true, headerClass } = defineProps<{
|
|
3
|
+
icon?: string
|
|
4
|
+
text?: string
|
|
5
|
+
description?: string
|
|
6
|
+
containerClass?: string
|
|
7
|
+
headerClass?: string
|
|
8
|
+
collapse?: boolean
|
|
9
|
+
open?: boolean
|
|
10
|
+
padding?: boolean | string
|
|
11
|
+
}>()
|
|
12
|
+
|
|
13
|
+
const open = defineModel('open', { default: true })
|
|
14
|
+
|
|
24
15
|
function onToggle(e: any) {
|
|
25
16
|
open.value = e.target.open
|
|
26
17
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { NuxtDevtoolsClient } from '@nuxt/devtools-kit/types'
|
|
2
|
+
import type { $Fetch } from 'nitropack/types'
|
|
3
|
+
import type { Ref } from 'vue'
|
|
4
|
+
import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client'
|
|
5
|
+
import { ref, watchEffect } from 'vue'
|
|
6
|
+
|
|
7
|
+
export const appFetch: Ref<$Fetch | undefined> = ref()
|
|
8
|
+
export const devtools: Ref<NuxtDevtoolsClient | undefined> = ref()
|
|
9
|
+
export const colorMode: Ref<'dark' | 'light'> = ref('dark')
|
|
10
|
+
|
|
11
|
+
export interface DevtoolsConnectionOptions {
|
|
12
|
+
onConnected?: (client: any) => void
|
|
13
|
+
onRouteChange?: (route: any) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the base devtools connection.
|
|
18
|
+
* Call this in your module's devtools client setup.
|
|
19
|
+
* Returns a cleanup function.
|
|
20
|
+
*/
|
|
21
|
+
export function useDevtoolsConnection(options: DevtoolsConnectionOptions = {}): void {
|
|
22
|
+
onDevtoolsClientConnected(async (client) => {
|
|
23
|
+
// @ts-expect-error untyped
|
|
24
|
+
appFetch.value = client.host.app.$fetch
|
|
25
|
+
watchEffect(() => {
|
|
26
|
+
colorMode.value = client.host.app.colorMode.value
|
|
27
|
+
})
|
|
28
|
+
devtools.value = client.devtools
|
|
29
|
+
options.onConnected?.(client)
|
|
30
|
+
|
|
31
|
+
if (options.onRouteChange) {
|
|
32
|
+
const $route = client.host.nuxt.vueApp.config.globalProperties?.$route
|
|
33
|
+
options.onRouteChange($route)
|
|
34
|
+
client.host.nuxt.$router.afterEach((route: any) => {
|
|
35
|
+
options.onRouteChange!(route)
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { HighlighterCore, LanguageRegistration } from 'shiki'
|
|
2
|
+
import type { ComputedRef, MaybeRef, Ref } from 'vue'
|
|
3
|
+
import { createHighlighterCore } from 'shiki/core'
|
|
4
|
+
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'
|
|
5
|
+
import { computed, ref, toValue } from 'vue'
|
|
6
|
+
import { colorMode } from './rpc'
|
|
7
|
+
|
|
8
|
+
export const shiki: Ref<HighlighterCore | undefined> = ref()
|
|
9
|
+
|
|
10
|
+
export interface LoadShikiOptions {
|
|
11
|
+
extraLangs?: (LanguageRegistration | Promise<LanguageRegistration>)[]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function loadShiki(options: LoadShikiOptions = {}): Promise<HighlighterCore> {
|
|
15
|
+
const langs: any[] = [
|
|
16
|
+
import('@shikijs/langs/xml'),
|
|
17
|
+
import('@shikijs/langs/json'),
|
|
18
|
+
import('@shikijs/langs/js'),
|
|
19
|
+
]
|
|
20
|
+
if (options.extraLangs) {
|
|
21
|
+
langs.push(...options.extraLangs)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
shiki.value = await createHighlighterCore({
|
|
25
|
+
themes: [
|
|
26
|
+
import('@shikijs/themes/vitesse-light'),
|
|
27
|
+
import('@shikijs/themes/vitesse-dark'),
|
|
28
|
+
],
|
|
29
|
+
langs,
|
|
30
|
+
engine: createJavaScriptRegexEngine(),
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return shiki.value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function useRenderCodeHighlight(code: MaybeRef<string>, lang: string): ComputedRef<string> {
|
|
37
|
+
return computed(() => {
|
|
38
|
+
if (!shiki.value)
|
|
39
|
+
return ''
|
|
40
|
+
const theme = colorMode.value === 'dark' ? 'vitesse-dark' : 'vitesse-light'
|
|
41
|
+
return shiki.value.codeToHtml(toValue(code) || '', {
|
|
42
|
+
lang,
|
|
43
|
+
theme,
|
|
44
|
+
}) || ''
|
|
45
|
+
})
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useDebounceFn, useLocalStorage } from '@vueuse/core'
|
|
2
|
+
import { hasProtocol, withBase } from 'ufo'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
export const refreshTime = ref(Date.now())
|
|
6
|
+
|
|
7
|
+
export const hostname = window.location.host
|
|
8
|
+
export const path = ref('/')
|
|
9
|
+
export const query = ref()
|
|
10
|
+
export const base = ref('/')
|
|
11
|
+
|
|
12
|
+
export const host = computed(() => withBase(base.value, `${window.location.protocol}//${hostname}`))
|
|
13
|
+
|
|
14
|
+
export const refreshSources = useDebounceFn(() => {
|
|
15
|
+
refreshTime.value = Date.now()
|
|
16
|
+
}, 200)
|
|
17
|
+
|
|
18
|
+
export const slowRefreshSources = useDebounceFn(() => {
|
|
19
|
+
refreshTime.value = Date.now()
|
|
20
|
+
}, 1000)
|
|
21
|
+
|
|
22
|
+
// Production preview state
|
|
23
|
+
export const previewSource = useLocalStorage<'local' | 'production'>('nuxt-seo:preview-source', 'local')
|
|
24
|
+
export const productionUrl = ref<string>('')
|
|
25
|
+
|
|
26
|
+
export const hasProductionUrl = computed(() => {
|
|
27
|
+
const url = productionUrl.value
|
|
28
|
+
if (!url || !hasProtocol(url))
|
|
29
|
+
return false
|
|
30
|
+
return !url.includes('localhost') && !url.includes('127.0.0.1')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export const isProductionMode = computed(() => previewSource.value === 'production' && hasProductionUrl.value)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createResolver } from '@nuxt/kit'
|
|
2
|
+
|
|
3
|
+
const { resolve } = createResolver(import.meta.url)
|
|
4
|
+
|
|
5
|
+
export default defineNuxtConfig({
|
|
6
|
+
ssr: false,
|
|
7
|
+
|
|
8
|
+
modules: [
|
|
9
|
+
'@nuxt/fonts',
|
|
10
|
+
'@nuxt/ui',
|
|
11
|
+
],
|
|
12
|
+
|
|
13
|
+
css: [resolve('./assets/css/global.css')],
|
|
14
|
+
|
|
15
|
+
fonts: {
|
|
16
|
+
families: [
|
|
17
|
+
{ name: 'Hubot Sans' },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
devtools: {
|
|
22
|
+
enabled: false,
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
compatibilityDate: '2026-03-13',
|
|
26
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ModuleRegistration } from "./pro.mjs";
|
|
2
|
+
import * as _nuxt_schema0 from "@nuxt/schema";
|
|
3
|
+
|
|
4
|
+
//#region src/module.d.ts
|
|
5
|
+
declare const _default: _nuxt_schema0.NuxtModule<_nuxt_schema0.ModuleOptions, _nuxt_schema0.ModuleOptions, false>;
|
|
6
|
+
/**
|
|
7
|
+
* Register a Nuxt SEO module with the shared instance.
|
|
8
|
+
* Pro modules are detected by name and trigger license verification at build time.
|
|
9
|
+
*
|
|
10
|
+
* Call this from your module's setup().
|
|
11
|
+
*/
|
|
12
|
+
declare function registerNuxtSeoModule(registration: ModuleRegistration): void;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { _default as default, registerNuxtSeoModule };
|
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { hookNuxtSeoProLicense } from "./pro.mjs";
|
|
2
|
+
import { hookNuxtSeoTelemetry } from "./telemetry.mjs";
|
|
3
|
+
import { defineNuxtModule, useNuxt } from "@nuxt/kit";
|
|
4
|
+
//#region src/module.ts
|
|
5
|
+
var module_default = defineNuxtModule({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "nuxtseo-shared",
|
|
8
|
+
configKey: "nuxtSeoShared",
|
|
9
|
+
compatibility: { nuxt: ">=3.16.0" }
|
|
10
|
+
},
|
|
11
|
+
setup() {
|
|
12
|
+
hookNuxtSeoProLicense();
|
|
13
|
+
hookNuxtSeoTelemetry();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const PRO_MODULES = new Set(["nuxt-skew-protection", "nuxt-ai-ready"]);
|
|
17
|
+
/**
|
|
18
|
+
* Register a Nuxt SEO module with the shared instance.
|
|
19
|
+
* Pro modules are detected by name and trigger license verification at build time.
|
|
20
|
+
*
|
|
21
|
+
* Call this from your module's setup().
|
|
22
|
+
*/
|
|
23
|
+
function registerNuxtSeoModule(registration) {
|
|
24
|
+
const nuxt = useNuxt();
|
|
25
|
+
nuxt._nuxtSeoModules = nuxt._nuxtSeoModules || [];
|
|
26
|
+
nuxt._nuxtSeoModules.push(registration);
|
|
27
|
+
if (PRO_MODULES.has(registration.name)) {
|
|
28
|
+
nuxt._nuxtSeoProModules = nuxt._nuxtSeoProModules || [];
|
|
29
|
+
nuxt._nuxtSeoProModules.push(registration);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { module_default as default, registerNuxtSeoModule };
|
package/dist/pro.d.mts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/telemetry.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Hook into build to send telemetry. Call once from the shared module.
|
|
4
|
+
* Reads from the existing _nuxtSeoModules registration list.
|
|
5
|
+
* Only fires in CI, never in dev/test, fully fire-and-forget.
|
|
6
|
+
*
|
|
7
|
+
* Disable with NUXT_SEO_TELEMETRY_DISABLED=1
|
|
8
|
+
*/
|
|
9
|
+
declare function hookNuxtSeoTelemetry(): void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { hookNuxtSeoTelemetry };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useNuxt } from "@nuxt/kit";
|
|
2
|
+
import { isCI, isTest, provider } from "std-env";
|
|
3
|
+
import { $fetch } from "ofetch";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { detectPackageManager } from "pkg-types";
|
|
6
|
+
//#region src/telemetry.ts
|
|
7
|
+
const TELEMETRY_ENDPOINT = "https://nuxtseo.com/api/telemetry/collect";
|
|
8
|
+
/**
|
|
9
|
+
* Hook into build to send telemetry. Call once from the shared module.
|
|
10
|
+
* Reads from the existing _nuxtSeoModules registration list.
|
|
11
|
+
* Only fires in CI, never in dev/test, fully fire-and-forget.
|
|
12
|
+
*
|
|
13
|
+
* Disable with NUXT_SEO_TELEMETRY_DISABLED=1
|
|
14
|
+
*/
|
|
15
|
+
function hookNuxtSeoTelemetry() {
|
|
16
|
+
const nuxt = useNuxt();
|
|
17
|
+
if (!isCI || isTest || process.env.VITEST || process.env.NUXT_SEO_TELEMETRY_DISABLED) return;
|
|
18
|
+
if (nuxt._isNuxtSeoTelemetryHooked) return;
|
|
19
|
+
nuxt._isNuxtSeoTelemetryHooked = true;
|
|
20
|
+
nuxt.hooks.hook("build:done", async () => {
|
|
21
|
+
const modules = nuxt._nuxtSeoModules || [];
|
|
22
|
+
if (modules.length === 0) return;
|
|
23
|
+
const projectHash = createHash("sha256").update(nuxt.options.rootDir).digest("hex").slice(0, 16);
|
|
24
|
+
const config = {};
|
|
25
|
+
for (const mod of modules) {
|
|
26
|
+
if (!mod.features) continue;
|
|
27
|
+
const safe = {};
|
|
28
|
+
for (const [k, v] of Object.entries(mod.features)) if (typeof v === "boolean" || typeof v === "number") safe[k] = v;
|
|
29
|
+
if (Object.keys(safe).length > 0) config[mod.name] = safe;
|
|
30
|
+
}
|
|
31
|
+
$fetch(TELEMETRY_ENDPOINT, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
body: {
|
|
34
|
+
projectHash,
|
|
35
|
+
modules: modules.map((m) => m.name),
|
|
36
|
+
moduleVersions: Object.fromEntries(modules.filter((m) => m.version).map((m) => [m.name, m.version])),
|
|
37
|
+
config: Object.keys(config).length > 0 ? config : void 0,
|
|
38
|
+
nuxtVersion: nuxt._version,
|
|
39
|
+
nodeVersion: process.version,
|
|
40
|
+
os: process.platform,
|
|
41
|
+
packageManager: await detectPackageManager(nuxt.options.rootDir).then((r) => r?.name).catch(() => void 0),
|
|
42
|
+
ci: provider || "true"
|
|
43
|
+
},
|
|
44
|
+
timeout: 5e3
|
|
45
|
+
}).catch(() => {});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { hookNuxtSeoTelemetry };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxtseo-shared",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"description": "Shared utilities for Nuxt SEO modules.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"types": "./dist/index.d.mts",
|
|
15
15
|
"import": "./dist/index.mjs"
|
|
16
16
|
},
|
|
17
|
+
"./module": {
|
|
18
|
+
"types": "./dist/module.d.mts",
|
|
19
|
+
"import": "./dist/module.mjs"
|
|
20
|
+
},
|
|
17
21
|
"./content": {
|
|
18
22
|
"types": "./dist/content.d.mts",
|
|
19
23
|
"import": "./dist/content.mjs"
|
|
@@ -34,21 +38,7 @@
|
|
|
34
38
|
"types": "./dist/pro.d.mts",
|
|
35
39
|
"import": "./dist/pro.mjs"
|
|
36
40
|
},
|
|
37
|
-
"./
|
|
38
|
-
"types": "./dist/runtime/app/index.d.mts",
|
|
39
|
-
"import": "./dist/runtime/app/index.mjs"
|
|
40
|
-
},
|
|
41
|
-
"./runtime/app/composables/rpc": {
|
|
42
|
-
"types": "./dist/runtime/app/composables/rpc.d.mts",
|
|
43
|
-
"import": "./dist/runtime/app/composables/rpc.mjs"
|
|
44
|
-
},
|
|
45
|
-
"./runtime/app/composables/shiki": {
|
|
46
|
-
"types": "./dist/runtime/app/composables/shiki.d.mts",
|
|
47
|
-
"import": "./dist/runtime/app/composables/shiki.mjs"
|
|
48
|
-
},
|
|
49
|
-
"./runtime/app/components/OCodeBlock": "./dist/runtime/app/components/OCodeBlock.vue",
|
|
50
|
-
"./runtime/app/components/OSectionBlock": "./dist/runtime/app/components/OSectionBlock.vue",
|
|
51
|
-
"./runtime/app/components/NuxtSeoLogo": "./dist/runtime/app/components/NuxtSeoLogo.vue"
|
|
41
|
+
"./layer-devtools": "./dist/layer-devtools"
|
|
52
42
|
},
|
|
53
43
|
"main": "./dist/index.mjs",
|
|
54
44
|
"types": "./dist/index.d.mts",
|
|
@@ -96,7 +86,7 @@
|
|
|
96
86
|
"vue": "^3.5.30"
|
|
97
87
|
},
|
|
98
88
|
"scripts": {
|
|
99
|
-
"build": "obuild",
|
|
89
|
+
"build": "obuild && cp -r src/layer-devtools dist/layer-devtools",
|
|
100
90
|
"stub": "obuild --stub"
|
|
101
91
|
}
|
|
102
92
|
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed } from 'vue'
|
|
3
|
-
import { useRenderCodeHighlight } from '../composables/shiki'
|
|
4
|
-
|
|
5
|
-
const props = withDefaults(
|
|
6
|
-
defineProps<{
|
|
7
|
-
code: string
|
|
8
|
-
lang: 'json' | 'xml' | 'js'
|
|
9
|
-
lines?: boolean
|
|
10
|
-
transformRendered?: (code: string) => string
|
|
11
|
-
}>(),
|
|
12
|
-
{
|
|
13
|
-
lines: false,
|
|
14
|
-
},
|
|
15
|
-
)
|
|
16
|
-
const rendered = computed(() => {
|
|
17
|
-
const code = useRenderCodeHighlight(props.code, props.lang)
|
|
18
|
-
return props.transformRendered ? props.transformRendered(code.value || '') : code.value
|
|
19
|
-
})
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
|
-
<template>
|
|
23
|
-
<pre
|
|
24
|
-
class="code-block p-5"
|
|
25
|
-
:class="lines ? 'code-block-lines' : ''"
|
|
26
|
-
v-html="rendered"
|
|
27
|
-
/>
|
|
28
|
-
</template>
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { NuxtDevtoolsClient } from "@nuxt/devtools-kit/types";
|
|
2
|
-
import type { $Fetch } from "nitropack/types";
|
|
3
|
-
import type { Ref } from "vue";
|
|
4
|
-
export declare const appFetch: Ref<$Fetch | undefined>;
|
|
5
|
-
export declare const devtools: Ref<NuxtDevtoolsClient | undefined>;
|
|
6
|
-
export declare const colorMode: Ref<"dark" | "light">;
|
|
7
|
-
export interface DevtoolsConnectionOptions {
|
|
8
|
-
onConnected?: (client: any) => void;
|
|
9
|
-
onRouteChange?: (route: any) => void;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Initialize the base devtools connection.
|
|
13
|
-
* Call this in your module's devtools client setup.
|
|
14
|
-
* Returns a cleanup function.
|
|
15
|
-
*/
|
|
16
|
-
export declare function useDevtoolsConnection(options?: DevtoolsConnectionOptions): void;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { onDevtoolsClientConnected } from "@nuxt/devtools-kit/iframe-client";
|
|
2
|
-
import { ref, watchEffect } from "vue";
|
|
3
|
-
export const appFetch = ref();
|
|
4
|
-
export const devtools = ref();
|
|
5
|
-
export const colorMode = ref("dark");
|
|
6
|
-
/**
|
|
7
|
-
* Initialize the base devtools connection.
|
|
8
|
-
* Call this in your module's devtools client setup.
|
|
9
|
-
* Returns a cleanup function.
|
|
10
|
-
*/
|
|
11
|
-
export function useDevtoolsConnection(options = {}) {
|
|
12
|
-
onDevtoolsClientConnected(async (client) => {
|
|
13
|
-
// @ts-expect-error untyped
|
|
14
|
-
appFetch.value = client.host.app.$fetch;
|
|
15
|
-
watchEffect(() => {
|
|
16
|
-
colorMode.value = client.host.app.colorMode.value;
|
|
17
|
-
});
|
|
18
|
-
devtools.value = client.devtools;
|
|
19
|
-
options.onConnected?.(client);
|
|
20
|
-
if (options.onRouteChange) {
|
|
21
|
-
const $route = client.host.nuxt.vueApp.config.globalProperties?.$route;
|
|
22
|
-
options.onRouteChange($route);
|
|
23
|
-
client.host.nuxt.$router.afterEach((route) => {
|
|
24
|
-
options.onRouteChange(route);
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { HighlighterCore, LanguageRegistration } from "shiki";
|
|
2
|
-
import type { ComputedRef, MaybeRef, Ref } from "vue";
|
|
3
|
-
export declare const shiki: Ref<HighlighterCore | undefined>;
|
|
4
|
-
export interface LoadShikiOptions {
|
|
5
|
-
extraLangs?: (LanguageRegistration | Promise<LanguageRegistration>)[];
|
|
6
|
-
}
|
|
7
|
-
export declare function loadShiki(options?: LoadShikiOptions): Promise<HighlighterCore>;
|
|
8
|
-
export declare function useRenderCodeHighlight(code: MaybeRef<string>, lang: string): ComputedRef<string>;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { createHighlighterCore } from "shiki/core";
|
|
2
|
-
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
3
|
-
import { computed, ref, toValue } from "vue";
|
|
4
|
-
import { colorMode } from "./rpc.mjs";
|
|
5
|
-
export const shiki = ref();
|
|
6
|
-
export async function loadShiki(options = {}) {
|
|
7
|
-
const langs = [
|
|
8
|
-
import("@shikijs/langs/xml"),
|
|
9
|
-
import("@shikijs/langs/json"),
|
|
10
|
-
import("@shikijs/langs/js")
|
|
11
|
-
];
|
|
12
|
-
if (options.extraLangs) {
|
|
13
|
-
langs.push(...options.extraLangs);
|
|
14
|
-
}
|
|
15
|
-
shiki.value = await createHighlighterCore({
|
|
16
|
-
themes: [import("@shikijs/themes/vitesse-light"), import("@shikijs/themes/vitesse-dark")],
|
|
17
|
-
langs,
|
|
18
|
-
engine: createJavaScriptRegexEngine()
|
|
19
|
-
});
|
|
20
|
-
return shiki.value;
|
|
21
|
-
}
|
|
22
|
-
export function useRenderCodeHighlight(code, lang) {
|
|
23
|
-
return computed(() => {
|
|
24
|
-
if (!shiki.value) return "";
|
|
25
|
-
const theme = colorMode.value === "dark" ? "vitesse-dark" : "vitesse-light";
|
|
26
|
-
return shiki.value.codeToHtml(toValue(code) || "", {
|
|
27
|
-
lang,
|
|
28
|
-
theme
|
|
29
|
-
}) || "";
|
|
30
|
-
});
|
|
31
|
-
}
|
|
File without changes
|