nuxtseo-layer-devtools 0.3.8 → 0.4.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/components/DevtoolsLayout.vue +30 -7
- package/components/DevtoolsModuleSplash.vue +189 -97
- package/nuxt.config.ts +5 -0
- package/package.json +1 -1
|
@@ -106,7 +106,7 @@ function disconnectStandalone() {
|
|
|
106
106
|
type="button"
|
|
107
107
|
aria-label="Nuxt SEO Modules"
|
|
108
108
|
class="flex items-center opacity-90 hover:opacity-100 transition-opacity cursor-pointer"
|
|
109
|
-
@click="showModuleSplash =
|
|
109
|
+
@click="showModuleSplash = !showModuleSplash"
|
|
110
110
|
>
|
|
111
111
|
<NuxtSeoLogo class="h-6 sm:h-7" />
|
|
112
112
|
</button>
|
|
@@ -114,12 +114,19 @@ function disconnectStandalone() {
|
|
|
114
114
|
<div class="devtools-divider" />
|
|
115
115
|
|
|
116
116
|
<div class="flex items-center gap-2">
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
<button
|
|
118
|
+
type="button"
|
|
119
|
+
class="devtools-module-switcher"
|
|
120
|
+
@click="showModuleSplash = !showModuleSplash"
|
|
121
|
+
>
|
|
122
|
+
<div class="devtools-brand-icon" aria-hidden="true">
|
|
123
|
+
<UIcon :name="icon" class="text-base sm:text-lg" />
|
|
124
|
+
</div>
|
|
125
|
+
<span class="text-sm sm:text-base font-semibold tracking-tight text-[var(--color-text)]">
|
|
126
|
+
{{ title }}
|
|
127
|
+
</span>
|
|
128
|
+
<UIcon name="carbon:chevron-down" class="w-3 h-3 opacity-50 transition-transform" :class="showModuleSplash ? 'rotate-180' : ''" />
|
|
129
|
+
</button>
|
|
123
130
|
<UBadge
|
|
124
131
|
v-if="version"
|
|
125
132
|
class="font-mono text-[10px] sm:text-xs hidden sm:inline-flex"
|
|
@@ -282,6 +289,22 @@ function disconnectStandalone() {
|
|
|
282
289
|
</template>
|
|
283
290
|
|
|
284
291
|
<style scoped>
|
|
292
|
+
.devtools-module-switcher {
|
|
293
|
+
display: flex;
|
|
294
|
+
align-items: center;
|
|
295
|
+
gap: 0.5rem;
|
|
296
|
+
padding: 0.25rem 0.5rem 0.25rem 0.25rem;
|
|
297
|
+
border-radius: var(--radius-md);
|
|
298
|
+
border: 1px solid transparent;
|
|
299
|
+
cursor: pointer;
|
|
300
|
+
transition: background 100ms, border-color 100ms;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.devtools-module-switcher:hover {
|
|
304
|
+
background: var(--color-surface-elevated);
|
|
305
|
+
border-color: var(--color-border);
|
|
306
|
+
}
|
|
307
|
+
|
|
285
308
|
.mode-dropdown-wrapper {
|
|
286
309
|
position: relative;
|
|
287
310
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onClickOutside } from '@vueuse/core'
|
|
3
|
-
import { ref } from 'vue'
|
|
3
|
+
import { computed, ref } from 'vue'
|
|
4
4
|
import { moduleCatalog, showModuleSplash, switchToModule } from '../composables/modules'
|
|
5
5
|
import { isConnected } from '../composables/state'
|
|
6
6
|
|
|
@@ -13,6 +13,9 @@ onClickOutside(panelRef, () => {
|
|
|
13
13
|
showModuleSplash.value = false
|
|
14
14
|
})
|
|
15
15
|
|
|
16
|
+
const coreModules = computed(() => moduleCatalog.value.filter(m => !m.pro))
|
|
17
|
+
const proModules = computed(() => moduleCatalog.value.filter(m => m.pro))
|
|
18
|
+
|
|
16
19
|
function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
17
20
|
if (!mod.installed || mod.name === props.currentModule)
|
|
18
21
|
return
|
|
@@ -25,59 +28,92 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
25
28
|
<Transition name="splash">
|
|
26
29
|
<div v-if="showModuleSplash" class="splash-overlay">
|
|
27
30
|
<div ref="panelRef" class="splash-panel">
|
|
31
|
+
<!-- Header -->
|
|
28
32
|
<div class="splash-header">
|
|
29
|
-
<NuxtSeoLogo class="h-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
</
|
|
33
|
+
<NuxtSeoLogo class="h-5" />
|
|
34
|
+
<button type="button" class="splash-header-close" @click="showModuleSplash = false">
|
|
35
|
+
<UIcon name="carbon:close" class="w-4 h-4" />
|
|
36
|
+
</button>
|
|
33
37
|
</div>
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
PRO
|
|
58
|
-
</UBadge>
|
|
59
|
-
</div>
|
|
60
|
-
<div class="splash-module-desc">
|
|
61
|
-
{{ mod.description }}
|
|
39
|
+
<!-- Modules Section -->
|
|
40
|
+
<div class="splash-section">
|
|
41
|
+
<div class="splash-section-label">
|
|
42
|
+
<span class="splash-section-dot" />
|
|
43
|
+
Modules
|
|
44
|
+
</div>
|
|
45
|
+
<div class="splash-grid">
|
|
46
|
+
<button
|
|
47
|
+
v-for="mod of coreModules"
|
|
48
|
+
:key="mod.name"
|
|
49
|
+
type="button"
|
|
50
|
+
class="splash-module"
|
|
51
|
+
:class="{
|
|
52
|
+
'is-current': mod.name === currentModule,
|
|
53
|
+
'is-unavailable': !mod.installed,
|
|
54
|
+
'is-switchable': mod.installed && mod.name !== currentModule && isConnected,
|
|
55
|
+
}"
|
|
56
|
+
:disabled="!mod.installed || mod.name === currentModule"
|
|
57
|
+
@click="handleModuleClick(mod)"
|
|
58
|
+
>
|
|
59
|
+
<div class="splash-module-icon">
|
|
60
|
+
<UIcon :name="mod.icon" class="text-base" />
|
|
62
61
|
</div>
|
|
63
|
-
|
|
64
|
-
<div class="splash-module-status">
|
|
62
|
+
<span class="splash-module-title">{{ mod.title }}</span>
|
|
65
63
|
<span v-if="mod.name === currentModule" class="splash-current-badge">Current</span>
|
|
66
|
-
<UIcon v-else-if="mod.installed && isConnected" name="carbon:arrow-right" class="text-sm opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
67
64
|
<span v-else-if="!mod.installed" class="splash-not-installed">Not installed</span>
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<!-- Pro Section -->
|
|
70
|
+
<div v-if="proModules.length" class="splash-section splash-pro-area">
|
|
71
|
+
<div class="splash-section-label">
|
|
72
|
+
<span class="splash-section-dot splash-section-dot--pro" />
|
|
73
|
+
Pro
|
|
74
|
+
</div>
|
|
75
|
+
<div class="splash-grid">
|
|
76
|
+
<button
|
|
77
|
+
v-for="mod of proModules"
|
|
78
|
+
:key="mod.name"
|
|
79
|
+
type="button"
|
|
80
|
+
class="splash-module"
|
|
81
|
+
:class="{
|
|
82
|
+
'is-current': mod.name === currentModule,
|
|
83
|
+
'is-unavailable': !mod.installed,
|
|
84
|
+
'is-switchable': mod.installed && mod.name !== currentModule && isConnected,
|
|
85
|
+
}"
|
|
86
|
+
:disabled="!mod.installed || mod.name === currentModule"
|
|
87
|
+
@click="handleModuleClick(mod)"
|
|
88
|
+
>
|
|
89
|
+
<div class="splash-module-icon">
|
|
90
|
+
<UIcon :name="mod.icon" class="text-base" />
|
|
91
|
+
</div>
|
|
92
|
+
<span class="splash-module-title">{{ mod.title }}</span>
|
|
93
|
+
<UBadge size="xs" color="neutral" variant="outline" class="splash-pro-badge">
|
|
94
|
+
PRO
|
|
95
|
+
</UBadge>
|
|
96
|
+
</button>
|
|
97
|
+
</div>
|
|
98
|
+
<!-- Pro status / CTA -->
|
|
99
|
+
<div class="splash-pro-status">
|
|
100
|
+
<div class="splash-pro-status-inner">
|
|
101
|
+
<UIcon name="carbon:locked" class="w-3.5 h-3.5 opacity-50" />
|
|
102
|
+
<span>Unlock Pro modules with a license key</span>
|
|
68
103
|
</div>
|
|
69
|
-
|
|
104
|
+
<a href="https://nuxtseo.com/pro" target="_blank" rel="noopener" class="splash-pro-cta">
|
|
105
|
+
Learn more
|
|
106
|
+
<UIcon name="carbon:arrow-right" class="w-3 h-3" />
|
|
107
|
+
</a>
|
|
108
|
+
</div>
|
|
70
109
|
</div>
|
|
71
110
|
|
|
111
|
+
<!-- Footer -->
|
|
72
112
|
<div class="splash-footer">
|
|
73
113
|
<a href="https://nuxtseo.com" target="_blank" rel="noopener" class="splash-link">
|
|
74
114
|
<UIcon name="carbon:earth" class="w-3.5 h-3.5" />
|
|
75
115
|
nuxtseo.com
|
|
76
116
|
</a>
|
|
77
|
-
<button type="button" class="splash-close" @click="showModuleSplash = false">
|
|
78
|
-
Close
|
|
79
|
-
<UIcon name="carbon:close" class="w-3.5 h-3.5" />
|
|
80
|
-
</button>
|
|
81
117
|
</div>
|
|
82
118
|
</div>
|
|
83
119
|
</div>
|
|
@@ -98,42 +134,89 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
98
134
|
}
|
|
99
135
|
|
|
100
136
|
.splash-panel {
|
|
101
|
-
width: min(
|
|
102
|
-
max-height: calc(100vh - 2rem);
|
|
103
|
-
overflow-y: auto;
|
|
137
|
+
width: min(480px, calc(100vw - 2rem));
|
|
104
138
|
border-radius: var(--radius-lg);
|
|
105
139
|
border: 1px solid var(--color-border);
|
|
106
140
|
background: var(--color-surface);
|
|
107
141
|
box-shadow: 0 24px 48px oklch(0% 0 0 / 0.2);
|
|
142
|
+
overflow: hidden;
|
|
108
143
|
}
|
|
109
144
|
|
|
110
145
|
.dark .splash-panel {
|
|
111
146
|
box-shadow: 0 24px 48px oklch(0% 0 0 / 0.5);
|
|
112
147
|
}
|
|
113
148
|
|
|
149
|
+
/* Header */
|
|
114
150
|
.splash-header {
|
|
115
|
-
|
|
116
|
-
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: space-between;
|
|
154
|
+
padding: 0.75rem 1rem;
|
|
155
|
+
border-bottom: 1px solid var(--color-border);
|
|
117
156
|
}
|
|
118
157
|
|
|
119
|
-
.splash-
|
|
120
|
-
|
|
121
|
-
|
|
158
|
+
.splash-header-close {
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
justify-content: center;
|
|
162
|
+
width: 1.5rem;
|
|
163
|
+
height: 1.5rem;
|
|
164
|
+
border-radius: var(--radius-sm);
|
|
122
165
|
color: var(--color-text-muted);
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
transition: background 100ms, color 100ms;
|
|
123
168
|
}
|
|
124
169
|
|
|
125
|
-
.splash-
|
|
170
|
+
.splash-header-close:hover {
|
|
171
|
+
background: var(--color-surface-sunken);
|
|
172
|
+
color: var(--color-text);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Sections */
|
|
176
|
+
.splash-section {
|
|
177
|
+
padding: 0.5rem 0.75rem 0.625rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.splash-section + .splash-section {
|
|
181
|
+
border-top: 1px solid var(--color-border);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.splash-section-label {
|
|
126
185
|
display: flex;
|
|
127
|
-
|
|
128
|
-
gap:
|
|
129
|
-
|
|
186
|
+
align-items: center;
|
|
187
|
+
gap: 0.375rem;
|
|
188
|
+
font-size: 0.625rem;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
text-transform: uppercase;
|
|
191
|
+
letter-spacing: 0.05em;
|
|
192
|
+
color: var(--color-text-muted);
|
|
193
|
+
padding: 0.25rem 0.375rem 0.375rem;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.splash-section-dot {
|
|
197
|
+
width: 0.3125rem;
|
|
198
|
+
height: 0.3125rem;
|
|
199
|
+
border-radius: 50%;
|
|
200
|
+
background: var(--color-text-subtle);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.splash-section-dot--pro {
|
|
204
|
+
background: var(--seo-green);
|
|
130
205
|
}
|
|
131
206
|
|
|
207
|
+
/* Grid */
|
|
208
|
+
.splash-grid {
|
|
209
|
+
display: grid;
|
|
210
|
+
grid-template-columns: repeat(2, 1fr);
|
|
211
|
+
gap: 1px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* Module items */
|
|
132
215
|
.splash-module {
|
|
133
216
|
display: flex;
|
|
134
217
|
align-items: center;
|
|
135
|
-
gap: 0.
|
|
136
|
-
padding: 0.
|
|
218
|
+
gap: 0.5rem;
|
|
219
|
+
padding: 0.375rem 0.5rem;
|
|
137
220
|
border-radius: var(--radius-md);
|
|
138
221
|
text-align: left;
|
|
139
222
|
cursor: default;
|
|
@@ -150,6 +233,7 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
150
233
|
|
|
151
234
|
.splash-module.is-switchable:hover .splash-module-icon {
|
|
152
235
|
color: var(--seo-green);
|
|
236
|
+
background: oklch(from var(--seo-green) l c h / 0.12);
|
|
153
237
|
}
|
|
154
238
|
|
|
155
239
|
.splash-module.is-current {
|
|
@@ -157,20 +241,20 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
157
241
|
}
|
|
158
242
|
|
|
159
243
|
.splash-module.is-unavailable {
|
|
160
|
-
opacity: 0.
|
|
244
|
+
opacity: 0.5;
|
|
161
245
|
}
|
|
162
246
|
|
|
163
247
|
.splash-module-icon {
|
|
164
248
|
display: flex;
|
|
165
249
|
align-items: center;
|
|
166
250
|
justify-content: center;
|
|
167
|
-
width:
|
|
168
|
-
height:
|
|
251
|
+
width: 1.625rem;
|
|
252
|
+
height: 1.625rem;
|
|
169
253
|
flex-shrink: 0;
|
|
170
254
|
border-radius: var(--radius-sm);
|
|
171
255
|
background: var(--color-surface-sunken);
|
|
172
256
|
color: var(--color-text-muted);
|
|
173
|
-
transition: color 100ms;
|
|
257
|
+
transition: color 100ms, background 100ms;
|
|
174
258
|
}
|
|
175
259
|
|
|
176
260
|
.splash-module.is-current .splash-module-icon {
|
|
@@ -178,47 +262,73 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
178
262
|
color: var(--seo-green);
|
|
179
263
|
}
|
|
180
264
|
|
|
181
|
-
.splash-module-
|
|
265
|
+
.splash-module-title {
|
|
266
|
+
font-size: 0.75rem;
|
|
267
|
+
font-weight: 500;
|
|
268
|
+
color: var(--color-text);
|
|
182
269
|
flex: 1;
|
|
183
270
|
min-width: 0;
|
|
184
271
|
}
|
|
185
272
|
|
|
186
|
-
.splash-
|
|
187
|
-
|
|
188
|
-
align-items: center;
|
|
189
|
-
font-size: 0.8125rem;
|
|
273
|
+
.splash-current-badge {
|
|
274
|
+
font-size: 0.5625rem;
|
|
190
275
|
font-weight: 600;
|
|
191
|
-
|
|
276
|
+
text-transform: uppercase;
|
|
277
|
+
letter-spacing: 0.05em;
|
|
278
|
+
color: var(--seo-green);
|
|
279
|
+
flex-shrink: 0;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.splash-not-installed {
|
|
283
|
+
font-size: 0.625rem;
|
|
284
|
+
color: var(--color-text-subtle);
|
|
285
|
+
flex-shrink: 0;
|
|
192
286
|
}
|
|
193
287
|
|
|
194
|
-
.splash-
|
|
288
|
+
.splash-pro-badge {
|
|
289
|
+
font-size: 9px !important;
|
|
290
|
+
flex-shrink: 0;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* Pro status / CTA */
|
|
294
|
+
.splash-pro-status {
|
|
295
|
+
display: flex;
|
|
296
|
+
align-items: center;
|
|
297
|
+
justify-content: space-between;
|
|
298
|
+
margin: 0.375rem 0.25rem 0;
|
|
299
|
+
padding: 0.4rem 0.625rem;
|
|
300
|
+
border-radius: var(--radius-md);
|
|
301
|
+
background: var(--color-surface-sunken);
|
|
195
302
|
font-size: 0.6875rem;
|
|
196
303
|
color: var(--color-text-muted);
|
|
197
|
-
margin-top: 1px;
|
|
198
304
|
}
|
|
199
305
|
|
|
200
|
-
.splash-
|
|
201
|
-
|
|
306
|
+
.splash-pro-status-inner {
|
|
307
|
+
display: flex;
|
|
308
|
+
align-items: center;
|
|
309
|
+
gap: 0.375rem;
|
|
202
310
|
}
|
|
203
311
|
|
|
204
|
-
.splash-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
312
|
+
.splash-pro-cta {
|
|
313
|
+
display: flex;
|
|
314
|
+
align-items: center;
|
|
315
|
+
gap: 0.25rem;
|
|
316
|
+
font-size: 0.6875rem;
|
|
317
|
+
font-weight: 500;
|
|
209
318
|
color: var(--seo-green);
|
|
319
|
+
text-decoration: none;
|
|
320
|
+
transition: opacity 100ms;
|
|
210
321
|
}
|
|
211
322
|
|
|
212
|
-
.splash-
|
|
213
|
-
|
|
214
|
-
color: var(--color-text-subtle);
|
|
323
|
+
.splash-pro-cta:hover {
|
|
324
|
+
opacity: 0.8;
|
|
215
325
|
}
|
|
216
326
|
|
|
327
|
+
/* Footer */
|
|
217
328
|
.splash-footer {
|
|
218
329
|
display: flex;
|
|
219
330
|
align-items: center;
|
|
220
|
-
|
|
221
|
-
padding: 0.75rem 1.25rem;
|
|
331
|
+
padding: 0.5rem 1rem;
|
|
222
332
|
border-top: 1px solid var(--color-border);
|
|
223
333
|
}
|
|
224
334
|
|
|
@@ -226,7 +336,7 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
226
336
|
display: flex;
|
|
227
337
|
align-items: center;
|
|
228
338
|
gap: 0.375rem;
|
|
229
|
-
font-size: 0.
|
|
339
|
+
font-size: 0.6875rem;
|
|
230
340
|
color: var(--color-text-muted);
|
|
231
341
|
text-decoration: none;
|
|
232
342
|
transition: color 100ms;
|
|
@@ -236,24 +346,6 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
|
|
|
236
346
|
color: var(--seo-green);
|
|
237
347
|
}
|
|
238
348
|
|
|
239
|
-
.splash-close {
|
|
240
|
-
display: flex;
|
|
241
|
-
align-items: center;
|
|
242
|
-
gap: 0.375rem;
|
|
243
|
-
font-size: 0.75rem;
|
|
244
|
-
font-weight: 500;
|
|
245
|
-
color: var(--color-text-muted);
|
|
246
|
-
padding: 0.25rem 0.625rem;
|
|
247
|
-
border-radius: var(--radius-sm);
|
|
248
|
-
cursor: pointer;
|
|
249
|
-
transition: background 100ms, color 100ms;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.splash-close:hover {
|
|
253
|
-
background: var(--color-surface-sunken);
|
|
254
|
-
color: var(--color-text);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
349
|
/* Transitions */
|
|
258
350
|
.splash-enter-active {
|
|
259
351
|
transition: opacity 200ms ease;
|
package/nuxt.config.ts
CHANGED