nuxtseo-layer-devtools 5.0.2 → 5.1.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.
@@ -15,8 +15,7 @@ onClickOutside(panelRef, () => {
15
15
  showModuleSplash.value = false
16
16
  })
17
17
 
18
- const coreModules = computed(() => moduleCatalog.value.filter(m => !m.pro))
19
- const proModules = computed(() => moduleCatalog.value.filter(m => m.pro))
18
+ const allModules = computed(() => moduleCatalog.value)
20
19
 
21
20
  const selectedForInstall = ref(new Set<string>())
22
21
 
@@ -56,9 +55,6 @@ const tabs = computed<TabsItem[]>(() => {
56
55
  const items: TabsItem[] = [
57
56
  { label: 'Modules', value: 'modules', icon: 'i-carbon-grid' },
58
57
  ]
59
- if (proModules.value.length) {
60
- items.push({ label: 'Pro', value: 'pro', icon: 'i-carbon-locked' })
61
- }
62
58
  if (evaluated.value && summary.value.total > 0) {
63
59
  items.push({
64
60
  label: 'Setup',
@@ -116,7 +112,7 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
116
112
  <div v-show="activeTab === 'modules'" class="splash-tab-content">
117
113
  <div class="splash-grid">
118
114
  <button
119
- v-for="mod of coreModules"
115
+ v-for="mod of allModules"
120
116
  :key="mod.name"
121
117
  type="button"
122
118
  class="splash-module"
@@ -157,43 +153,6 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
157
153
  </Transition>
158
154
  </div>
159
155
 
160
- <!-- Pro tab -->
161
- <div v-show="activeTab === 'pro'" class="splash-tab-content">
162
- <div class="splash-grid">
163
- <button
164
- v-for="mod of proModules"
165
- :key="mod.name"
166
- type="button"
167
- class="splash-module"
168
- :class="{
169
- 'is-current': mod.name === currentModule,
170
- 'is-unavailable': !mod.installed,
171
- 'is-switchable': mod.installed && mod.name !== currentModule && isConnected,
172
- }"
173
- :disabled="!mod.installed || mod.name === currentModule"
174
- @click="handleModuleClick(mod)"
175
- >
176
- <div class="splash-module-icon">
177
- <UIcon :name="mod.icon" class="text-base" />
178
- </div>
179
- <span class="splash-module-title">{{ mod.title }}</span>
180
- <UBadge size="xs" color="neutral" variant="outline" class="splash-pro-badge">
181
- PRO
182
- </UBadge>
183
- </button>
184
- </div>
185
- <div class="splash-pro-status">
186
- <div class="splash-pro-status-inner">
187
- <UIcon name="carbon:locked" class="w-3.5 h-3.5 opacity-50" />
188
- <span>Unlock Pro modules with a license key</span>
189
- </div>
190
- <a href="https://nuxtseo.com/pro" target="_blank" rel="noopener" class="splash-pro-cta">
191
- Learn more
192
- <UIcon name="carbon:arrow-right" class="w-3 h-3" />
193
- </a>
194
- </div>
195
- </div>
196
-
197
156
  <!-- Setup tab -->
198
157
  <div v-show="activeTab === 'setup'" class="splash-tab-content">
199
158
  <div v-if="evaluated && summary.total > 0" class="splash-health-header">
@@ -209,6 +168,21 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
209
168
  <DevtoolsSetupChecklist :current-module="currentModule" />
210
169
  </div>
211
170
 
171
+ <!-- Pro ad -->
172
+ <a href="https://nuxtseo.com/pro" target="_blank" rel="noopener" class="splash-pro-ad">
173
+ <div class="splash-pro-ad-content">
174
+ <UIcon name="i-carbon-chart-line-data" class="w-4 h-4 text-violet-500" />
175
+ <div>
176
+ <span class="splash-pro-ad-title">Nuxt SEO Pro</span>
177
+ <span class="splash-pro-ad-desc">GSC analytics, indexing diagnostics, competitor tracking &amp; MCP server</span>
178
+ </div>
179
+ </div>
180
+ <span class="splash-pro-ad-cta">
181
+ Learn more
182
+ <UIcon name="carbon:arrow-right" class="w-3 h-3" />
183
+ </span>
184
+ </a>
185
+
212
186
  <!-- Footer -->
213
187
  <div class="splash-footer">
214
188
  <a href="https://nuxtseo.com" target="_blank" rel="noopener" class="splash-link">
@@ -392,48 +366,6 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
392
366
  flex-shrink: 0;
393
367
  }
394
368
 
395
- .splash-pro-badge {
396
- font-size: 9px !important;
397
- flex-shrink: 0;
398
- color: oklch(65% 0.25 290) !important;
399
- border-color: oklch(65% 0.25 290 / 0.3) !important;
400
- }
401
-
402
- /* Pro status / CTA */
403
- .splash-pro-status {
404
- display: flex;
405
- align-items: center;
406
- justify-content: space-between;
407
- margin: 0.375rem 0 0;
408
- padding: 0.4rem 0.625rem;
409
- border-radius: var(--radius-md);
410
- background: oklch(65% 0.25 290 / 0.06);
411
- border: 1px solid oklch(65% 0.25 290 / 0.12);
412
- font-size: 0.6875rem;
413
- color: var(--color-text-muted);
414
- }
415
-
416
- .splash-pro-status-inner {
417
- display: flex;
418
- align-items: center;
419
- gap: 0.375rem;
420
- }
421
-
422
- .splash-pro-cta {
423
- display: flex;
424
- align-items: center;
425
- gap: 0.25rem;
426
- font-size: 0.6875rem;
427
- font-weight: 500;
428
- color: oklch(65% 0.25 290);
429
- text-decoration: none;
430
- transition: opacity 100ms;
431
- }
432
-
433
- .splash-pro-cta:hover {
434
- opacity: 0.8;
435
- }
436
-
437
369
  /* Setup Health header */
438
370
  .splash-health-header {
439
371
  display: flex;
@@ -543,6 +475,58 @@ function handleModuleClick(mod: typeof moduleCatalog.value[0]) {
543
475
  max-height: 4rem;
544
476
  }
545
477
 
478
+ /* Pro ad */
479
+ .splash-pro-ad {
480
+ display: flex;
481
+ align-items: center;
482
+ justify-content: space-between;
483
+ gap: 0.5rem;
484
+ margin: 0.375rem 0.75rem 0;
485
+ padding: 0.5rem 0.625rem;
486
+ border-radius: var(--radius-md);
487
+ background: oklch(65% 0.25 290 / 0.06);
488
+ border: 1px solid oklch(65% 0.25 290 / 0.12);
489
+ text-decoration: none;
490
+ transition: border-color 100ms, background 100ms;
491
+ }
492
+
493
+ .splash-pro-ad:hover {
494
+ background: oklch(65% 0.25 290 / 0.1);
495
+ border-color: oklch(65% 0.25 290 / 0.2);
496
+ }
497
+
498
+ .splash-pro-ad-content {
499
+ display: flex;
500
+ align-items: flex-start;
501
+ gap: 0.5rem;
502
+ min-width: 0;
503
+ }
504
+
505
+ .splash-pro-ad-title {
506
+ display: block;
507
+ font-size: 0.6875rem;
508
+ font-weight: 600;
509
+ color: var(--color-text);
510
+ }
511
+
512
+ .splash-pro-ad-desc {
513
+ display: block;
514
+ font-size: 0.625rem;
515
+ color: var(--color-text-muted);
516
+ line-height: 1.4;
517
+ }
518
+
519
+ .splash-pro-ad-cta {
520
+ display: flex;
521
+ align-items: center;
522
+ gap: 0.25rem;
523
+ flex-shrink: 0;
524
+ font-size: 0.625rem;
525
+ font-weight: 500;
526
+ color: oklch(65% 0.25 290);
527
+ white-space: nowrap;
528
+ }
529
+
546
530
  /* Footer */
547
531
  .splash-footer {
548
532
  display: flex;
@@ -41,7 +41,7 @@ export interface ChecklistSummary {
41
41
 
42
42
  // Debug endpoint paths for each module
43
43
  const DEBUG_ENDPOINTS: Partial<Record<NuxtSEOModule['slug'], string>> = {
44
- 'site-config': '/__site-config__/debug',
44
+ 'site-config': '/__site-config__/debug.json',
45
45
  'robots': '/__robots__/debug.json',
46
46
  'sitemap': '/__sitemap__/debug.json',
47
47
  'og-image': '/__nuxt-og-image/debug.json',
@@ -21,7 +21,6 @@ export interface SeoModuleCatalogEntry {
21
21
  route?: string
22
22
  npm: string
23
23
  repo: string
24
- pro?: boolean
25
24
  playgrounds?: Record<string, string>
26
25
  }
27
26
 
@@ -55,7 +54,6 @@ function moduleToCatalogEntry(mod: NuxtSEOModule): Omit<SeoModuleCatalogEntry, '
55
54
  icon: toIconify(mod.icon),
56
55
  npm: mod.npm,
57
56
  repo: mod.repo,
58
- pro: mod.pro,
59
57
  playgrounds: mod.playgrounds,
60
58
  }
61
59
  }
@@ -7,9 +7,13 @@ export interface ModuleUpdateInfo {
7
7
  hasUpdate: boolean
8
8
  }
9
9
 
10
+ const LEADING_V_RE = /^v/
11
+ const PRERELEASE_RE = /[-+].*$/
12
+
10
13
  function parseSemver(version: string): [number, number, number] {
11
- // eslint-disable-next-line e18e/prefer-static-regex
12
- const parts = version.replace(/^v/, '').split('.').map(Number)
14
+ // Strip leading v, then strip pre-release/build metadata (e.g. 1.2.3-rc.1+build)
15
+ const clean = version.replace(LEADING_V_RE, '').replace(PRERELEASE_RE, '')
16
+ const parts = clean.split('.').map(Number)
13
17
  return [parts[0] || 0, parts[1] || 0, parts[2] || 0]
14
18
  }
15
19
 
@@ -26,26 +30,28 @@ function isNewerVersion(latest: string, current: string): boolean {
26
30
  const updateCache = ref<Record<string, ModuleUpdateInfo>>({})
27
31
 
28
32
  export function useModuleUpdate(npmPackage: string | undefined, currentVersion: string | undefined): { hasUpdate: ComputedRef<boolean>, latestVersion: ComputedRef<string | undefined>, info: ComputedRef<ModuleUpdateInfo | undefined> } {
33
+ const cacheKey = npmPackage && currentVersion ? `${npmPackage}@${currentVersion}` : undefined
34
+
29
35
  const info = computed(() => {
30
- if (!npmPackage || !currentVersion)
36
+ if (!cacheKey)
31
37
  return undefined
32
- return updateCache.value[npmPackage]
38
+ return updateCache.value[cacheKey]
33
39
  })
34
40
 
35
41
  const hasUpdate = computed(() => info.value?.hasUpdate ?? false)
36
42
  const latestVersion = computed(() => info.value?.latestVersion)
37
43
 
38
- if (npmPackage && currentVersion && !updateCache.value[npmPackage]) {
44
+ if (cacheKey && !updateCache.value[cacheKey]) {
39
45
  fetch(`https://registry.npmjs.org/${npmPackage}/latest`)
40
46
  .then(r => r.json())
41
47
  .then((data) => {
42
48
  const latest = data.version as string
43
49
  if (!latest)
44
50
  return
45
- updateCache.value[npmPackage] = {
46
- currentVersion,
51
+ updateCache.value[cacheKey] = {
52
+ currentVersion: currentVersion!,
47
53
  latestVersion: latest,
48
- hasUpdate: isNewerVersion(latest, currentVersion),
54
+ hasUpdate: isNewerVersion(latest, currentVersion!),
49
55
  }
50
56
  })
51
57
  .catch(() => {})
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxtseo-layer-devtools",
3
3
  "type": "module",
4
- "version": "5.0.2",
4
+ "version": "5.1.1",
5
5
  "description": "Shared Nuxt layer for Nuxt SEO devtools clients.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -40,17 +40,17 @@
40
40
  "dependencies": {
41
41
  "@nuxt/devtools-kit": "4.0.0-alpha.3",
42
42
  "@nuxt/kit": "^4.4.2",
43
- "@nuxt/ui": "^4.6.0",
43
+ "@nuxt/ui": "^4.6.1",
44
44
  "@shikijs/langs": "^4.0.2",
45
45
  "@shikijs/themes": "^4.0.2",
46
46
  "@vueuse/nuxt": "^14.2.1",
47
47
  "ofetch": "^1.5.1",
48
48
  "shiki": "^4.0.2",
49
49
  "ufo": "^1.6.3",
50
- "nuxtseo-shared": "5.0.2"
50
+ "nuxtseo-shared": "5.1.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "nuxt": "^4.4.2",
54
- "vue": "^3.5.31"
54
+ "vue": "^3.5.32"
55
55
  }
56
56
  }