nuxtseo-layer-devtools 0.4.5 → 0.5.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/DevtoolsChecklistBadge.vue +65 -0
- package/components/DevtoolsChecklistItem.vue +171 -0
- package/components/DevtoolsLayout.vue +91 -7
- package/components/DevtoolsModuleSplash.vue +270 -51
- package/components/DevtoolsPlaygrounds.vue +99 -0
- package/components/DevtoolsSetupChecklist.vue +60 -0
- package/components/DevtoolsTroubleshooting.vue +332 -0
- package/composables/checklist.ts +486 -0
- package/composables/modules.ts +48 -13
- package/composables/package-manager.ts +41 -0
- package/composables/update-check.ts +39 -0
- package/error.vue +1 -0
- package/package.json +3 -2
- package/skills/devtools-layer-skilld/SKILL.md +17 -8
- package/skills/devtools-layer-skilld/reference.md +70 -7
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { requiredPending = 0, recommendedPending = 0 } = defineProps<{
|
|
3
|
+
requiredPending?: number
|
|
4
|
+
recommendedPending?: number
|
|
5
|
+
}>()
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<span
|
|
10
|
+
v-if="requiredPending > 0 || recommendedPending > 0"
|
|
11
|
+
class="checklist-badge"
|
|
12
|
+
:class="requiredPending > 0 ? 'checklist-badge--required' : 'checklist-badge--recommended'"
|
|
13
|
+
>
|
|
14
|
+
{{ requiredPending + recommendedPending }}
|
|
15
|
+
</span>
|
|
16
|
+
<span v-else class="checklist-badge checklist-badge--complete">
|
|
17
|
+
<UIcon name="carbon:checkmark" class="w-2.5 h-2.5" />
|
|
18
|
+
</span>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<style scoped>
|
|
22
|
+
.checklist-badge {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
min-width: 1rem;
|
|
27
|
+
height: 1rem;
|
|
28
|
+
padding: 0 0.25rem;
|
|
29
|
+
border-radius: var(--radius-full, 9999px);
|
|
30
|
+
font-size: 0.5625rem;
|
|
31
|
+
font-weight: 700;
|
|
32
|
+
line-height: 1;
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.checklist-badge--required {
|
|
37
|
+
background: oklch(65% 0.18 25 / 0.15);
|
|
38
|
+
color: oklch(55% 0.18 25);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.dark .checklist-badge--required {
|
|
42
|
+
background: oklch(45% 0.14 25 / 0.2);
|
|
43
|
+
color: oklch(72% 0.14 25);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.checklist-badge--recommended {
|
|
47
|
+
background: oklch(80% 0.12 85 / 0.15);
|
|
48
|
+
color: oklch(50% 0.15 85);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.dark .checklist-badge--recommended {
|
|
52
|
+
background: oklch(50% 0.12 85 / 0.2);
|
|
53
|
+
color: oklch(75% 0.12 85);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.checklist-badge--complete {
|
|
57
|
+
background: oklch(75% 0.15 145 / 0.12);
|
|
58
|
+
color: oklch(50% 0.15 145);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.dark .checklist-badge--complete {
|
|
62
|
+
background: oklch(50% 0.15 145 / 0.15);
|
|
63
|
+
color: oklch(75% 0.18 145);
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ChecklistItemResult } from '../composables/checklist'
|
|
3
|
+
|
|
4
|
+
const { item } = defineProps<{
|
|
5
|
+
item: ChecklistItemResult
|
|
6
|
+
}>()
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div class="checklist-item" :class="item.passed ? 'is-passed' : `is-pending-${item.level}`">
|
|
11
|
+
<div class="checklist-item-status">
|
|
12
|
+
<UIcon
|
|
13
|
+
:name="item.passed ? 'carbon:checkmark-filled' : item.level === 'required' ? 'carbon:warning-alt-filled' : 'carbon:circle-dash'"
|
|
14
|
+
class="checklist-item-icon"
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="checklist-item-content">
|
|
18
|
+
<div class="checklist-item-header">
|
|
19
|
+
<span class="checklist-item-label">{{ item.label }}</span>
|
|
20
|
+
<UBadge
|
|
21
|
+
v-if="!item.passed"
|
|
22
|
+
size="xs"
|
|
23
|
+
:color="item.level === 'required' ? 'error' : 'warning'"
|
|
24
|
+
variant="subtle"
|
|
25
|
+
class="checklist-item-level"
|
|
26
|
+
>
|
|
27
|
+
{{ item.level === 'required' ? 'Required' : 'Tip' }}
|
|
28
|
+
</UBadge>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="checklist-item-description">
|
|
31
|
+
{{ item.description }}
|
|
32
|
+
</div>
|
|
33
|
+
<div v-if="item.detail" class="checklist-item-detail">
|
|
34
|
+
{{ item.detail }}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<a
|
|
38
|
+
:href="item.docsUrl"
|
|
39
|
+
target="_blank"
|
|
40
|
+
rel="noopener"
|
|
41
|
+
class="checklist-item-docs"
|
|
42
|
+
:class="item.passed ? 'is-subtle' : ''"
|
|
43
|
+
>
|
|
44
|
+
<UIcon name="carbon:arrow-up-right" class="w-3 h-3" />
|
|
45
|
+
</a>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<style scoped>
|
|
50
|
+
.checklist-item {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: flex-start;
|
|
53
|
+
gap: 0.5rem;
|
|
54
|
+
padding: 0.4375rem 0.5rem;
|
|
55
|
+
border-radius: var(--radius-md);
|
|
56
|
+
transition: background 100ms;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.checklist-item:hover {
|
|
60
|
+
background: var(--color-surface-elevated);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.checklist-item.is-passed {
|
|
64
|
+
opacity: 0.55;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.checklist-item.is-passed:hover {
|
|
68
|
+
opacity: 0.8;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.checklist-item-status {
|
|
72
|
+
flex-shrink: 0;
|
|
73
|
+
padding-top: 0.125rem;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.checklist-item-icon {
|
|
77
|
+
font-size: 0.8125rem;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.checklist-item.is-passed .checklist-item-icon {
|
|
81
|
+
color: oklch(50% 0.15 145);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dark .checklist-item.is-passed .checklist-item-icon {
|
|
85
|
+
color: oklch(70% 0.18 145);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.checklist-item.is-pending-required .checklist-item-icon {
|
|
89
|
+
color: oklch(55% 0.2 25);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.dark .checklist-item.is-pending-required .checklist-item-icon {
|
|
93
|
+
color: oklch(70% 0.16 25);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.checklist-item.is-pending-recommended .checklist-item-icon {
|
|
97
|
+
color: oklch(52% 0.15 85);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.dark .checklist-item.is-pending-recommended .checklist-item-icon {
|
|
101
|
+
color: oklch(72% 0.12 85);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.checklist-item-content {
|
|
105
|
+
flex: 1;
|
|
106
|
+
min-width: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.checklist-item-header {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 0.375rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.checklist-item-label {
|
|
116
|
+
font-size: 0.6875rem;
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
color: var(--color-text);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.checklist-item-level {
|
|
122
|
+
font-size: 0.5rem !important;
|
|
123
|
+
padding: 0 0.25rem !important;
|
|
124
|
+
line-height: 1.4 !important;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.checklist-item-description {
|
|
128
|
+
font-size: 0.625rem;
|
|
129
|
+
color: var(--color-text-muted);
|
|
130
|
+
margin-top: 0.0625rem;
|
|
131
|
+
line-height: 1.35;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.checklist-item-detail {
|
|
135
|
+
display: inline-block;
|
|
136
|
+
font-size: 0.5625rem;
|
|
137
|
+
font-family: var(--font-mono, monospace);
|
|
138
|
+
color: var(--color-text-subtle);
|
|
139
|
+
margin-top: 0.1875rem;
|
|
140
|
+
padding: 0.0625rem 0.3125rem;
|
|
141
|
+
background: var(--color-surface-sunken);
|
|
142
|
+
border-radius: var(--radius-sm);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.checklist-item-docs {
|
|
146
|
+
display: flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
flex-shrink: 0;
|
|
150
|
+
width: 1.25rem;
|
|
151
|
+
height: 1.25rem;
|
|
152
|
+
margin-top: 0.0625rem;
|
|
153
|
+
border-radius: var(--radius-sm);
|
|
154
|
+
color: var(--color-text-muted);
|
|
155
|
+
text-decoration: none;
|
|
156
|
+
transition: color 100ms, background 100ms;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.checklist-item-docs:hover {
|
|
160
|
+
color: var(--seo-green);
|
|
161
|
+
background: oklch(from var(--seo-green) l c h / 0.08);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.checklist-item-docs.is-subtle {
|
|
165
|
+
opacity: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.checklist-item:hover .checklist-item-docs.is-subtle {
|
|
169
|
+
opacity: 1;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onClickOutside } from '@vueuse/core'
|
|
3
3
|
import { computed, ref } from 'vue'
|
|
4
|
-
import {
|
|
4
|
+
import { useSetupChecklist } from '../composables/checklist'
|
|
5
|
+
import { fetchInstalledModules, findModuleByName, showModuleSplash } from '../composables/modules'
|
|
5
6
|
import { colorMode } from '../composables/rpc'
|
|
6
7
|
import { hasProductionUrl, isConnected, isProductionMode, isStandalone, path, previewSource, productionUrl, standaloneUrl } from '../composables/state'
|
|
8
|
+
import { useModuleUpdate } from '../composables/update-check'
|
|
7
9
|
|
|
8
10
|
export interface DevtoolsNavItem {
|
|
9
11
|
value: string
|
|
@@ -35,6 +37,10 @@ const emit = defineEmits<{
|
|
|
35
37
|
refresh: []
|
|
36
38
|
}>()
|
|
37
39
|
|
|
40
|
+
const moduleInfo = computed(() => moduleName ? findModuleByName(moduleName) : undefined)
|
|
41
|
+
const npmPackage = computed(() => moduleInfo.value?.npm)
|
|
42
|
+
const { hasUpdate, latestVersion } = useModuleUpdate(npmPackage.value, version)
|
|
43
|
+
|
|
38
44
|
// Fetch installed modules for the splash screen
|
|
39
45
|
fetchInstalledModules()
|
|
40
46
|
|
|
@@ -88,6 +94,13 @@ const standaloneHostname = computed(() => {
|
|
|
88
94
|
|
|
89
95
|
const showStandaloneSetup = computed(() => !isConnected.value && !isStandalone.value)
|
|
90
96
|
|
|
97
|
+
const { evaluated, getModuleResultByName } = useSetupChecklist()
|
|
98
|
+
const moduleChecklistResult = computed(() => {
|
|
99
|
+
if (!evaluated.value || !moduleName)
|
|
100
|
+
return undefined
|
|
101
|
+
return getModuleResultByName(moduleName)
|
|
102
|
+
})
|
|
103
|
+
|
|
91
104
|
function disconnectStandalone() {
|
|
92
105
|
standaloneUrl.value = ''
|
|
93
106
|
}
|
|
@@ -125,14 +138,28 @@ function disconnectStandalone() {
|
|
|
125
138
|
<span class="text-sm sm:text-base font-semibold tracking-tight text-[var(--color-text)]">
|
|
126
139
|
{{ title }}
|
|
127
140
|
</span>
|
|
141
|
+
<DevtoolsChecklistBadge
|
|
142
|
+
v-if="moduleChecklistResult?.totalPending"
|
|
143
|
+
:required-pending="moduleChecklistResult.requiredPending"
|
|
144
|
+
:recommended-pending="moduleChecklistResult.recommendedPending"
|
|
145
|
+
/>
|
|
128
146
|
<UIcon name="carbon:chevron-down" class="w-3 h-3 opacity-50 transition-transform" :class="showModuleSplash ? 'rotate-180' : ''" />
|
|
129
147
|
</button>
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
<UTooltip v-if="version" :text="hasUpdate ? `Update available: v${latestVersion}` : `v${version}`">
|
|
149
|
+
<a
|
|
150
|
+
:href="hasUpdate && npmPackage ? `https://npmjs.com/package/${npmPackage}` : undefined"
|
|
151
|
+
:target="hasUpdate ? '_blank' : undefined"
|
|
152
|
+
rel="noopener"
|
|
153
|
+
class="version-badge-wrapper"
|
|
154
|
+
>
|
|
155
|
+
<UBadge
|
|
156
|
+
class="font-mono text-[10px] sm:text-xs hidden sm:inline-flex"
|
|
157
|
+
>
|
|
158
|
+
v{{ version }}
|
|
159
|
+
</UBadge>
|
|
160
|
+
<span v-if="hasUpdate" class="update-dot" />
|
|
161
|
+
</a>
|
|
162
|
+
</UTooltip>
|
|
136
163
|
<!-- Mode dropdown: embedded with production URL -->
|
|
137
164
|
<div v-if="hasProductionUrl && !isStandalone" ref="modeDropdownRef" class="mode-dropdown-wrapper">
|
|
138
165
|
<button type="button" class="devtools-mode-btn" @click="modeDropdownOpen = !modeDropdownOpen">
|
|
@@ -272,6 +299,20 @@ function disconnectStandalone() {
|
|
|
272
299
|
</div>
|
|
273
300
|
</div>
|
|
274
301
|
|
|
302
|
+
<!-- Setup checklist alert -->
|
|
303
|
+
<DevtoolsAlert
|
|
304
|
+
v-if="moduleChecklistResult?.requiredPending"
|
|
305
|
+
variant="warning"
|
|
306
|
+
>
|
|
307
|
+
{{ moduleChecklistResult.requiredPending }} required setup {{ moduleChecklistResult.requiredPending === 1 ? 'step' : 'steps' }} remaining
|
|
308
|
+
<template #action>
|
|
309
|
+
<button type="button" class="checklist-alert-action" @click="showModuleSplash = true">
|
|
310
|
+
View setup
|
|
311
|
+
<UIcon name="carbon:arrow-right" class="w-3 h-3" />
|
|
312
|
+
</button>
|
|
313
|
+
</template>
|
|
314
|
+
</DevtoolsAlert>
|
|
315
|
+
|
|
275
316
|
<!-- Main Content -->
|
|
276
317
|
<div class="devtools-main">
|
|
277
318
|
<main class="devtools-main-content">
|
|
@@ -279,6 +320,9 @@ function disconnectStandalone() {
|
|
|
279
320
|
<DevtoolsLoading v-show="!showStandaloneSetup && loading" />
|
|
280
321
|
<div v-show="!showStandaloneSetup && !loading">
|
|
281
322
|
<slot />
|
|
323
|
+
<div v-if="activeTab === 'debug' && moduleName" class="devtools-troubleshooting-section">
|
|
324
|
+
<DevtoolsTroubleshooting :module-name="moduleName" :version="version" />
|
|
325
|
+
</div>
|
|
282
326
|
</div>
|
|
283
327
|
</main>
|
|
284
328
|
</div>
|
|
@@ -289,6 +333,25 @@ function disconnectStandalone() {
|
|
|
289
333
|
</template>
|
|
290
334
|
|
|
291
335
|
<style scoped>
|
|
336
|
+
.version-badge-wrapper {
|
|
337
|
+
position: relative;
|
|
338
|
+
display: inline-flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
text-decoration: none;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.update-dot {
|
|
344
|
+
position: absolute;
|
|
345
|
+
top: -2px;
|
|
346
|
+
right: -2px;
|
|
347
|
+
width: 7px;
|
|
348
|
+
height: 7px;
|
|
349
|
+
border-radius: 50%;
|
|
350
|
+
background: var(--seo-green);
|
|
351
|
+
border: 1.5px solid var(--color-surface);
|
|
352
|
+
pointer-events: none;
|
|
353
|
+
}
|
|
354
|
+
|
|
292
355
|
.devtools-module-switcher {
|
|
293
356
|
display: flex;
|
|
294
357
|
align-items: center;
|
|
@@ -418,4 +481,25 @@ function disconnectStandalone() {
|
|
|
418
481
|
.standalone-path-input:focus {
|
|
419
482
|
border-color: var(--seo-green);
|
|
420
483
|
}
|
|
484
|
+
|
|
485
|
+
.devtools-troubleshooting-section {
|
|
486
|
+
padding: 1.5rem 1rem 1rem;
|
|
487
|
+
max-width: 48rem;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.checklist-alert-action {
|
|
491
|
+
display: flex;
|
|
492
|
+
align-items: center;
|
|
493
|
+
gap: 0.25rem;
|
|
494
|
+
font-size: 0.75rem;
|
|
495
|
+
font-weight: 600;
|
|
496
|
+
color: inherit;
|
|
497
|
+
opacity: 0.8;
|
|
498
|
+
cursor: pointer;
|
|
499
|
+
transition: opacity 100ms;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.checklist-alert-action:hover {
|
|
503
|
+
opacity: 1;
|
|
504
|
+
}
|
|
421
505
|
</style>
|