nuxtseo-shared 0.1.6 → 0.1.7
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/layer-devtools/components/DevtoolsCopyButton.vue +20 -0
- package/dist/layer-devtools/components/DevtoolsDocs.vue +1 -1
- package/dist/layer-devtools/components/DevtoolsKeyValue.vue +109 -0
- package/dist/layer-devtools/components/DevtoolsLayout.vue +9 -3
- package/dist/layer-devtools/components/DevtoolsMetric.vue +96 -0
- package/dist/layer-devtools/composables/clipboard.ts +21 -0
- package/package.json +5 -3
- package/dist/layer-devtools/composables/init.ts +0 -15
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useCopy } from '../composables/clipboard'
|
|
3
|
+
|
|
4
|
+
const { text } = defineProps<{
|
|
5
|
+
text: string
|
|
6
|
+
}>()
|
|
7
|
+
|
|
8
|
+
const { copy, copied } = useCopy()
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<UTooltip :text="copied ? 'Copied!' : 'Copy'">
|
|
13
|
+
<UButton
|
|
14
|
+
:icon="copied ? 'carbon:checkmark' : 'carbon:copy'"
|
|
15
|
+
:aria-label="copied ? 'Copied' : 'Copy to clipboard'"
|
|
16
|
+
:class="copied ? 'text-[var(--seo-green)]' : ''"
|
|
17
|
+
@click="copy(text)"
|
|
18
|
+
/>
|
|
19
|
+
</UTooltip>
|
|
20
|
+
</template>
|
|
@@ -6,6 +6,6 @@ const { url } = defineProps<{
|
|
|
6
6
|
|
|
7
7
|
<template>
|
|
8
8
|
<div class="h-full max-h-full overflow-hidden">
|
|
9
|
-
<iframe :src="url" class="w-full h-full border-none" style="min-height: calc(100vh - 100px);" />
|
|
9
|
+
<iframe :src="url" :title="`Documentation - ${url}`" class="w-full h-full border-none" style="min-height: calc(100vh - 100px);" />
|
|
10
10
|
</div>
|
|
11
11
|
</template>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
export interface KeyValueItem {
|
|
3
|
+
key: string
|
|
4
|
+
value: string | number | boolean | undefined
|
|
5
|
+
copyable?: boolean
|
|
6
|
+
mono?: boolean
|
|
7
|
+
link?: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { items, striped = false } = defineProps<{
|
|
11
|
+
items: KeyValueItem[]
|
|
12
|
+
striped?: boolean
|
|
13
|
+
}>()
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<div class="divide-y divide-[var(--color-border-subtle)]">
|
|
18
|
+
<div
|
|
19
|
+
v-for="item in items"
|
|
20
|
+
:key="item.key"
|
|
21
|
+
class="devtools-kv-row group"
|
|
22
|
+
:class="{ 'devtools-kv-striped': striped }"
|
|
23
|
+
>
|
|
24
|
+
<span class="devtools-kv-key">{{ item.key }}</span>
|
|
25
|
+
<div class="devtools-kv-value-wrap">
|
|
26
|
+
<a
|
|
27
|
+
v-if="item.link"
|
|
28
|
+
:href="item.link"
|
|
29
|
+
target="_blank"
|
|
30
|
+
rel="noopener"
|
|
31
|
+
class="link-external text-sm"
|
|
32
|
+
>
|
|
33
|
+
{{ item.value }}
|
|
34
|
+
</a>
|
|
35
|
+
<span
|
|
36
|
+
v-else
|
|
37
|
+
class="devtools-kv-value"
|
|
38
|
+
:class="{
|
|
39
|
+
'font-mono': item.mono !== false,
|
|
40
|
+
'devtools-kv-true': item.value === true,
|
|
41
|
+
'devtools-kv-false': item.value === false,
|
|
42
|
+
'devtools-kv-empty': item.value === undefined || item.value === '',
|
|
43
|
+
}"
|
|
44
|
+
>
|
|
45
|
+
{{ item.value === undefined || item.value === '' ? '(empty)' : item.value }}
|
|
46
|
+
</span>
|
|
47
|
+
<DevtoolsCopyButton
|
|
48
|
+
v-if="item.copyable && item.value !== undefined && item.value !== ''"
|
|
49
|
+
:text="String(item.value)"
|
|
50
|
+
class="opacity-0 group-hover:opacity-100 transition-opacity"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style scoped>
|
|
58
|
+
.devtools-kv-row {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: space-between;
|
|
62
|
+
gap: 1rem;
|
|
63
|
+
padding: 0.625rem 1.25rem;
|
|
64
|
+
transition: background-color 150ms ease;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.devtools-kv-row:hover {
|
|
68
|
+
background: var(--color-surface-sunken);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.devtools-kv-striped:nth-child(even) {
|
|
72
|
+
background: oklch(from var(--color-surface-sunken) l c h / 0.5);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.devtools-kv-key {
|
|
76
|
+
font-size: 0.8125rem;
|
|
77
|
+
font-family: var(--font-mono);
|
|
78
|
+
color: var(--color-text-muted);
|
|
79
|
+
flex-shrink: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.devtools-kv-value-wrap {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 0.5rem;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.devtools-kv-value {
|
|
90
|
+
font-size: 0.8125rem;
|
|
91
|
+
text-align: right;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
text-overflow: ellipsis;
|
|
94
|
+
white-space: nowrap;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.devtools-kv-true {
|
|
98
|
+
color: var(--seo-green);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.devtools-kv-false {
|
|
102
|
+
color: oklch(65% 0.15 25);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.devtools-kv-empty {
|
|
106
|
+
color: var(--color-text-subtle);
|
|
107
|
+
font-style: italic;
|
|
108
|
+
}
|
|
109
|
+
</style>
|
|
@@ -72,6 +72,8 @@ const isRouteNav = computed(() => navItems.some(item => item.to))
|
|
|
72
72
|
<a
|
|
73
73
|
href="https://nuxtseo.com"
|
|
74
74
|
target="_blank"
|
|
75
|
+
rel="noopener"
|
|
76
|
+
aria-label="Nuxt SEO"
|
|
75
77
|
class="flex items-center opacity-90 hover:opacity-100 transition-opacity"
|
|
76
78
|
>
|
|
77
79
|
<NuxtSeoLogo class="h-6 sm:h-7" />
|
|
@@ -80,7 +82,7 @@ const isRouteNav = computed(() => navItems.some(item => item.to))
|
|
|
80
82
|
<div class="devtools-divider" />
|
|
81
83
|
|
|
82
84
|
<div class="flex items-center gap-2">
|
|
83
|
-
<div class="devtools-brand-icon">
|
|
85
|
+
<div class="devtools-brand-icon" aria-hidden="true">
|
|
84
86
|
<UIcon :name="icon" class="text-base sm:text-lg" />
|
|
85
87
|
</div>
|
|
86
88
|
<h1 class="text-sm sm:text-base font-semibold tracking-tight text-[var(--color-text)]">
|
|
@@ -151,19 +153,21 @@ const isRouteNav = computed(() => navItems.some(item => item.to))
|
|
|
151
153
|
<!-- Preview source toggle -->
|
|
152
154
|
<div v-if="hasProductionUrl" class="devtools-preview-toggle">
|
|
153
155
|
<button
|
|
156
|
+
type="button"
|
|
154
157
|
class="devtools-preview-btn"
|
|
155
158
|
:class="{ active: previewSource === 'local' }"
|
|
156
159
|
@click="previewSource = 'local'"
|
|
157
160
|
>
|
|
158
|
-
<UIcon name="carbon:laptop" class="w-3.5 h-3.5" />
|
|
161
|
+
<UIcon name="carbon:laptop" class="w-3.5 h-3.5" aria-hidden="true" />
|
|
159
162
|
<span class="hidden sm:inline">Local</span>
|
|
160
163
|
</button>
|
|
161
164
|
<button
|
|
165
|
+
type="button"
|
|
162
166
|
class="devtools-preview-btn"
|
|
163
167
|
:class="{ active: previewSource === 'production' }"
|
|
164
168
|
@click="previewSource = 'production'"
|
|
165
169
|
>
|
|
166
|
-
<UIcon name="carbon:cloud" class="w-3.5 h-3.5" />
|
|
170
|
+
<UIcon name="carbon:cloud" class="w-3.5 h-3.5" aria-hidden="true" />
|
|
167
171
|
<span class="hidden sm:inline">Production</span>
|
|
168
172
|
</button>
|
|
169
173
|
</div>
|
|
@@ -183,6 +187,7 @@ const isRouteNav = computed(() => navItems.some(item => item.to))
|
|
|
183
187
|
<UTooltip text="Refresh">
|
|
184
188
|
<UButton
|
|
185
189
|
icon="carbon:reset"
|
|
190
|
+
aria-label="Refresh"
|
|
186
191
|
class="devtools-nav-action"
|
|
187
192
|
@click="emit('refresh')"
|
|
188
193
|
/>
|
|
@@ -191,6 +196,7 @@ const isRouteNav = computed(() => navItems.some(item => item.to))
|
|
|
191
196
|
<UTooltip text="GitHub">
|
|
192
197
|
<UButton
|
|
193
198
|
icon="simple-icons:github"
|
|
199
|
+
aria-label="GitHub"
|
|
194
200
|
:to="githubUrl"
|
|
195
201
|
target="_blank"
|
|
196
202
|
class="devtools-nav-action hidden sm:flex"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const {
|
|
3
|
+
label,
|
|
4
|
+
value,
|
|
5
|
+
icon,
|
|
6
|
+
variant = 'default',
|
|
7
|
+
} = defineProps<{
|
|
8
|
+
label?: string
|
|
9
|
+
value: string | number
|
|
10
|
+
icon?: string
|
|
11
|
+
variant?: 'default' | 'success' | 'warning' | 'danger' | 'info'
|
|
12
|
+
}>()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div class="devtools-metric" :class="`devtools-metric-${variant}`">
|
|
17
|
+
<UIcon v-if="icon" :name="icon" class="devtools-metric-icon" aria-hidden="true" />
|
|
18
|
+
<span class="devtools-metric-value">{{ value }}</span>
|
|
19
|
+
<span v-if="label" class="devtools-metric-label">{{ label }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<style scoped>
|
|
24
|
+
.devtools-metric {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
gap: 0.375rem;
|
|
28
|
+
padding: 0.25rem 0.625rem;
|
|
29
|
+
font-size: 0.75rem;
|
|
30
|
+
font-weight: 500;
|
|
31
|
+
border-radius: var(--radius-sm);
|
|
32
|
+
font-variant-numeric: tabular-nums;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.devtools-metric-icon {
|
|
36
|
+
font-size: 0.875rem;
|
|
37
|
+
flex-shrink: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.devtools-metric-value {
|
|
41
|
+
font-family: var(--font-mono);
|
|
42
|
+
font-weight: 600;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.devtools-metric-label {
|
|
46
|
+
color: inherit;
|
|
47
|
+
opacity: 0.7;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Variants */
|
|
51
|
+
.devtools-metric-default {
|
|
52
|
+
background: var(--color-surface-sunken);
|
|
53
|
+
border: 1px solid var(--color-border-subtle);
|
|
54
|
+
color: var(--color-text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.devtools-metric-success {
|
|
58
|
+
background: oklch(75% 0.15 145 / 0.12);
|
|
59
|
+
color: oklch(50% 0.15 145);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dark .devtools-metric-success {
|
|
63
|
+
background: oklch(50% 0.15 145 / 0.15);
|
|
64
|
+
color: oklch(75% 0.18 145);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.devtools-metric-warning {
|
|
68
|
+
background: oklch(80% 0.12 85 / 0.12);
|
|
69
|
+
color: oklch(55% 0.15 85);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.dark .devtools-metric-warning {
|
|
73
|
+
background: oklch(55% 0.12 85 / 0.15);
|
|
74
|
+
color: oklch(75% 0.15 85);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.devtools-metric-danger {
|
|
78
|
+
background: oklch(65% 0.12 25 / 0.1);
|
|
79
|
+
color: oklch(55% 0.15 25);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.dark .devtools-metric-danger {
|
|
83
|
+
background: oklch(45% 0.1 25 / 0.15);
|
|
84
|
+
color: oklch(70% 0.12 25);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.devtools-metric-info {
|
|
88
|
+
background: oklch(85% 0.08 200 / 0.1);
|
|
89
|
+
color: oklch(50% 0.12 200);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.dark .devtools-metric-info {
|
|
93
|
+
background: oklch(35% 0.08 200 / 0.15);
|
|
94
|
+
color: oklch(70% 0.1 200);
|
|
95
|
+
}
|
|
96
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useClipboard } from '@vueuse/core'
|
|
2
|
+
import { ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
export function useCopy(timeout = 2000) {
|
|
5
|
+
const { copy, copied } = useClipboard({ legacy: true })
|
|
6
|
+
const justCopied = ref(false)
|
|
7
|
+
|
|
8
|
+
watch(copied, (val) => {
|
|
9
|
+
if (val) {
|
|
10
|
+
justCopied.value = true
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
justCopied.value = false
|
|
13
|
+
}, timeout)
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
copy,
|
|
19
|
+
copied: justCopied,
|
|
20
|
+
}
|
|
21
|
+
}
|
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.7",
|
|
5
5
|
"description": "Shared utilities for Nuxt SEO modules.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
"types": "./dist/pro.d.mts",
|
|
39
39
|
"import": "./dist/pro.mjs"
|
|
40
40
|
},
|
|
41
|
-
"./
|
|
41
|
+
"./package.json": "./package.json",
|
|
42
|
+
"./layer-devtools": "./dist/layer-devtools",
|
|
43
|
+
"./layer-devtools/*": "./dist/layer-devtools/*"
|
|
42
44
|
},
|
|
43
45
|
"main": "./dist/index.mjs",
|
|
44
46
|
"types": "./dist/index.d.mts",
|
|
@@ -78,7 +80,7 @@
|
|
|
78
80
|
"@nuxtjs/i18n": "^10.2.3",
|
|
79
81
|
"@shikijs/langs": "^4.0.2",
|
|
80
82
|
"@shikijs/themes": "^4.0.2",
|
|
81
|
-
"@vueuse/core": "^
|
|
83
|
+
"@vueuse/core": "^13.9.0",
|
|
82
84
|
"nuxt-site-config": "^4.0.0",
|
|
83
85
|
"obuild": "^0.4.32",
|
|
84
86
|
"shiki": "^4.0.2",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { computed } from 'vue'
|
|
2
|
-
import { colorMode } from './rpc'
|
|
3
|
-
|
|
4
|
-
export function useDevtoolsInit(title: string) {
|
|
5
|
-
const isDark = computed(() => colorMode.value === 'dark')
|
|
6
|
-
|
|
7
|
-
useHead({
|
|
8
|
-
title: `Nuxt ${title}`,
|
|
9
|
-
htmlAttrs: {
|
|
10
|
-
class: () => isDark.value ? 'dark' : '',
|
|
11
|
-
},
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
return { isDark }
|
|
15
|
-
}
|