@stackonward/cli 1.3.3 → 1.5.0

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.
Files changed (111) hide show
  1. package/README.md +248 -0
  2. package/dist/index.js +14124 -635
  3. package/dist/index.js.map +1 -1
  4. package/dist/resources/components/nuxt/site-source/app/app.vue +4 -2
  5. package/dist/resources/components/nuxt/site-source/app/assets/css/site-shell.css +50 -50
  6. package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationHeader.vue → app/Header.vue} +3 -3
  7. package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationShell.vue → app/Shell.vue} +2 -2
  8. package/dist/resources/components/nuxt/site-source/app/components/{site/ApplicationSidebar.vue → app/Sidebar.vue} +2 -2
  9. package/dist/resources/components/nuxt/site-source/app/components/{ui/CheckoutStatus.vue → checkout/Status.vue} +8 -3
  10. package/dist/resources/components/nuxt/site-source/app/components/{ui/CheckoutSummary.vue → checkout/Summary.vue} +11 -11
  11. package/dist/resources/components/nuxt/site-source/app/components/{site/CookieConsent.vue → consent/Banner.vue} +3 -3
  12. package/dist/resources/components/nuxt/site-source/app/components/{site/ContentArticlePage.vue → content/Article.vue} +3 -3
  13. package/dist/resources/components/nuxt/site-source/app/components/{site/ContentListPage.vue → content/List.vue} +2 -2
  14. package/dist/resources/components/nuxt/site-source/app/components/{ui/GoogleSignInButton.vue → google/SignInButton.vue} +3 -3
  15. package/dist/resources/components/nuxt/site-source/app/components/{site/LocaleSwitcher.vue → locale/Menu.vue} +2 -2
  16. package/dist/resources/components/nuxt/site-source/app/components/site/{MarketingFooter.vue → Footer.vue} +8 -8
  17. package/dist/resources/components/nuxt/site-source/app/components/site/{MarketingHeader.vue → Header.vue} +27 -27
  18. package/dist/resources/components/nuxt/site-source/app/components/site/Shell.vue +29 -0
  19. package/dist/resources/components/nuxt/site-source/app/components/{site/ThemeSwitcher.vue → theme/Menu.vue} +2 -2
  20. package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseButton.vue → Button.vue} +1 -8
  21. package/dist/resources/components/nuxt/site-source/app/components/ui/InfiniteScroll.vue +2 -2
  22. package/dist/resources/components/nuxt/site-source/app/components/{ui/YoutubeEmbed.vue → youtube/Embed.vue} +10 -2
  23. package/dist/resources/components/nuxt/site-source/app/composables/useConsent.ts +8 -10
  24. package/dist/resources/components/nuxt/site-source/app/composables/useNavigation.ts +9 -14
  25. package/dist/resources/components/nuxt/site-source/app/composables/useSeo.ts +3 -1
  26. package/dist/resources/components/nuxt/site-source/app/composables/useSiteShell.ts +19 -22
  27. package/dist/resources/components/nuxt/site-source/app/consent/types.ts +59 -0
  28. package/dist/resources/components/nuxt/site-source/app/content/types.ts +17 -0
  29. package/dist/resources/components/nuxt/site-source/app/layouts/app.vue +2 -2
  30. package/dist/resources/components/nuxt/site-source/app/layouts/default.vue +2 -2
  31. package/dist/resources/components/nuxt/site-source/app/navigation/types.ts +68 -0
  32. package/dist/resources/components/nuxt/site-source/app/plugins/consent.ts +2 -2
  33. package/dist/resources/components/nuxt/site-source/app/site/config.ts +35 -0
  34. package/dist/resources/components/registry.json +63 -51
  35. package/dist/resources/deploy/ci/github/caller.yml +38 -0
  36. package/dist/resources/deploy/ci/onedev/import.yml +14 -0
  37. package/dist/resources/deploy/nginx/cache.conf +17 -0
  38. package/dist/resources/deploy/nginx/compression.conf +23 -0
  39. package/dist/resources/deploy/nginx/proxy.conf +15 -0
  40. package/dist/resources/deploy/profiles/go-service/compose.yaml +19 -0
  41. package/dist/resources/deploy/profiles/nuxt-node/Dockerfile +17 -0
  42. package/dist/resources/deploy/profiles/static-files/nginx/site.conf.template +55 -0
  43. package/dist/resources/deploy/profiles/static-nginx/Dockerfile +18 -0
  44. package/dist/resources/deploy/profiles/static-nginx/nginx/default.conf.template +28 -0
  45. package/dist/resources/deploy/targets/compose-host/deploy.config.example.json +114 -0
  46. package/dist/resources/deploy/traffic/nginx/stackonward-nginx-traffic +96 -0
  47. package/dist/resources/registry.json +30 -30
  48. package/dist/resources/templates/base/.claude/docs/architecture.md +4 -0
  49. package/dist/resources/templates/base/AGENTS.md +15 -0
  50. package/dist/resources/templates/base/CLAUDE.md +15 -0
  51. package/dist/resources/templates/go-backend/.dockerignore +25 -3
  52. package/dist/resources/templates/go-backend/deploy.config.json +26 -0
  53. package/dist/resources/templates/go-backend/docs/runtime-provenance.json +9 -6
  54. package/dist/resources/templates/go-backend/docs/source-provenance.json +33 -22
  55. package/dist/resources/templates/go-backend/internal/common/enums/register_locale.go +7 -0
  56. package/dist/resources/templates/go-backend/internal/common/enums/register_locale_test.go +30 -0
  57. package/dist/resources/templates/go-backend/internal/common/locale/locale.go +102 -76
  58. package/dist/resources/templates/go-backend/internal/common/locale/locale_test.go +168 -45
  59. package/dist/resources/templates/go-backend/internal/core/commandreplay/model.go +5 -5
  60. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence.go +20 -7
  61. package/dist/resources/templates/go-backend/internal/core/commandreplay/persistence_test.go +51 -22
  62. package/dist/resources/templates/go-backend/internal/core/commandreplay/repository.go +2 -2
  63. package/dist/resources/templates/go-backend/internal/core/commandreplay/service.go +13 -24
  64. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_persistence_test.go +98 -13
  65. package/dist/resources/templates/go-backend/internal/core/commandreplay/service_test.go +61 -36
  66. package/dist/resources/templates/go-backend/internal/core/commandreplay/types.go +6 -6
  67. package/dist/resources/templates/go-backend/internal/core/integrationevent/config.go +24 -0
  68. package/dist/resources/templates/go-backend/internal/core/integrationevent/errors.go +53 -0
  69. package/dist/resources/templates/go-backend/internal/core/integrationevent/model.go +71 -0
  70. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence.go +193 -0
  71. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence_postgres_test.go +178 -0
  72. package/dist/resources/templates/go-backend/internal/core/integrationevent/persistence_test.go +168 -0
  73. package/dist/resources/templates/go-backend/internal/core/integrationevent/provider.go +16 -0
  74. package/dist/resources/templates/go-backend/internal/core/integrationevent/registry.go +69 -0
  75. package/dist/resources/templates/go-backend/internal/core/integrationevent/repository.go +17 -0
  76. package/dist/resources/templates/go-backend/internal/core/integrationevent/service.go +283 -0
  77. package/dist/resources/templates/go-backend/internal/core/integrationevent/service_test.go +433 -0
  78. package/dist/resources/templates/go-backend/internal/core/integrationevent/types.go +67 -0
  79. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.down.sql +6 -0
  80. package/dist/resources/templates/go-backend/migrations/000001_infrastructure.up.sql +176 -16
  81. package/dist/resources/templates/go-backend/tests/infrastructure.sql +109 -6
  82. package/dist/resources/templates/nuxt/.claude/docs/stack.md +5 -1
  83. package/dist/resources/templates/nuxt/.dockerignore +19 -0
  84. package/dist/resources/templates/nuxt/README.md +3 -3
  85. package/dist/resources/templates/nuxt/app/app.config.ts +1 -1
  86. package/dist/resources/templates/nuxt/app/i18n/locales/en.json +1 -1
  87. package/dist/resources/templates/nuxt/app/pages/app/index.vue +1 -1
  88. package/dist/resources/templates/nuxt/app/pages/authors/[slug].vue +2 -2
  89. package/dist/resources/templates/nuxt/app/pages/blog/[slug].vue +4 -3
  90. package/dist/resources/templates/nuxt/app/pages/blog/category/[category].vue +2 -2
  91. package/dist/resources/templates/nuxt/app/pages/blog/index.vue +2 -2
  92. package/dist/resources/templates/nuxt/app/pages/legal/cookies.vue +3 -3
  93. package/dist/resources/templates/nuxt/app/pages/legal/privacy.vue +2 -2
  94. package/dist/resources/templates/nuxt/app/pages/legal/terms.vue +2 -2
  95. package/dist/resources/templates/nuxt/app/pages/ui/index.vue +45 -45
  96. package/dist/resources/templates/nuxt/deploy.config.json +27 -0
  97. package/dist/resources/templates/nuxt/nuxt.config.ts +4 -10
  98. package/dist/resources/templates/nuxt/package.json +8 -8
  99. package/dist/resources/templates/nuxt/test/e2e/rendering.test.ts +1 -0
  100. package/dist/resources/templates/nuxt/test/unit/consent.test.ts +4 -7
  101. package/package.json +5 -3
  102. package/dist/resources/components/nuxt/site-source/app/components/site/MarketingShell.vue +0 -33
  103. package/dist/resources/components/nuxt/site-source/app/types/site-shell.ts +0 -172
  104. /package/dist/resources/components/nuxt/site-source/app/components/{site/ConsentSettingsButton.vue → consent/Settings.vue} +0 -0
  105. /package/dist/resources/components/nuxt/site-source/app/components/{site/StaticPage.vue → content/Document.vue} +0 -0
  106. /package/dist/resources/components/nuxt/site-source/app/components/{site/ToastViewport.vue → feedback/Toast.vue} +0 -0
  107. /package/dist/resources/components/nuxt/site-source/app/components/site/{SiteBrand.vue → Brand.vue} +0 -0
  108. /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseGradientBorder.vue → GradientBorder.vue} +0 -0
  109. /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseModal.vue → Modal.vue} +0 -0
  110. /package/dist/resources/components/nuxt/site-source/app/components/ui/{BasePopover.vue → Popover.vue} +0 -0
  111. /package/dist/resources/components/nuxt/site-source/app/components/ui/{BaseText.vue → Text.vue} +0 -0
@@ -1,11 +1,13 @@
1
1
  <script setup lang="ts">
2
+ import { formatTitle } from "@stackonward/nuxt-site-shell/runtime";
3
+
2
4
  const site = useSiteShell();
3
5
  const { localeProperties, t } = useI18n();
4
6
  const localeHead = useLocaleHead({ seo: true });
5
7
 
6
8
  useHead(() => ({
7
9
  htmlAttrs: localeHead.value.htmlAttrs,
8
- titleTemplate: (title?: string) => (title ? `${title} | ${site.value.name}` : site.value.name),
10
+ titleTemplate: (title?: string) => formatTitle(title, site.value.name),
9
11
  meta: [
10
12
  ...(localeHead.value.meta || []),
11
13
  { name: "description", content: t(site.value.descriptionKey) },
@@ -40,6 +42,6 @@ useHead(() => ({
40
42
  <NuxtPage />
41
43
  </NuxtLayout>
42
44
  </div>
43
- <ToastViewport />
45
+ <FeedbackToast />
44
46
  </UApp>
45
47
  </template>
@@ -268,7 +268,7 @@ summary {
268
268
  white-space: nowrap;
269
269
  }
270
270
 
271
- .marketing-shell {
271
+ .site-shell {
272
272
  min-height: 100dvh;
273
273
  display: flex;
274
274
  flex-direction: column;
@@ -277,12 +277,12 @@ summary {
277
277
  linear-gradient(180deg, rgb(168 101 80 / 2.5%), transparent 520px), var(--site-color-bg);
278
278
  }
279
279
 
280
- .marketing-shell__main {
280
+ .site-shell__main {
281
281
  width: 100%;
282
282
  flex: 1;
283
283
  }
284
284
 
285
- .marketing-header {
285
+ .site-header {
286
286
  position: sticky;
287
287
  top: 0;
288
288
  z-index: 40;
@@ -291,7 +291,7 @@ summary {
291
291
  backdrop-filter: blur(18px);
292
292
  }
293
293
 
294
- .marketing-header__inner {
294
+ .site-header__inner {
295
295
  display: flex;
296
296
  width: min(1180px, calc(100% - 32px));
297
297
  height: var(--site-shell-header-height);
@@ -301,7 +301,7 @@ summary {
301
301
  gap: 24px;
302
302
  }
303
303
 
304
- .marketing-header__nav {
304
+ .site-header__nav {
305
305
  display: flex;
306
306
  min-width: 0;
307
307
  flex: 1;
@@ -310,8 +310,8 @@ summary {
310
310
  gap: 6px;
311
311
  }
312
312
 
313
- .marketing-nav-link,
314
- .marketing-nav-menu > summary {
313
+ .site-nav-link,
314
+ .site-nav-menu > summary {
315
315
  display: inline-flex;
316
316
  min-height: 40px;
317
317
  align-items: center;
@@ -324,26 +324,26 @@ summary {
324
324
  cursor: pointer;
325
325
  }
326
326
 
327
- .marketing-nav-link:hover,
328
- .marketing-nav-menu > summary:hover {
327
+ .site-nav-link:hover,
328
+ .site-nav-menu > summary:hover {
329
329
  border-color: rgb(168 101 80 / 18%);
330
330
  background: rgb(168 101 80 / 7%);
331
331
  color: var(--site-color-text);
332
332
  }
333
333
 
334
- .marketing-nav-menu {
334
+ .site-nav-menu {
335
335
  position: relative;
336
336
  }
337
337
 
338
- .marketing-nav-menu > summary {
338
+ .site-nav-menu > summary {
339
339
  list-style: none;
340
340
  }
341
341
 
342
- .marketing-nav-menu > summary::-webkit-details-marker {
342
+ .site-nav-menu > summary::-webkit-details-marker {
343
343
  display: none;
344
344
  }
345
345
 
346
- .marketing-nav-menu__panel {
346
+ .site-nav-menu__panel {
347
347
  position: absolute;
348
348
  top: calc(100% + 10px);
349
349
  left: 50%;
@@ -358,33 +358,33 @@ summary {
358
358
  transform: translateX(-50%);
359
359
  }
360
360
 
361
- .marketing-nav-menu__item {
361
+ .site-nav-menu__item {
362
362
  display: grid;
363
363
  gap: 4px;
364
364
  border-radius: var(--site-radius-md);
365
365
  padding: 12px;
366
366
  }
367
367
 
368
- .marketing-nav-menu__item:hover {
368
+ .site-nav-menu__item:hover {
369
369
  background: rgb(168 101 80 / 7%);
370
370
  }
371
371
 
372
- .marketing-nav-menu__item small {
372
+ .site-nav-menu__item small {
373
373
  color: var(--site-color-muted);
374
374
  line-height: 1.45;
375
375
  }
376
376
 
377
- .marketing-header__actions {
377
+ .site-header__actions {
378
378
  display: flex;
379
379
  align-items: center;
380
380
  gap: 10px;
381
381
  }
382
382
 
383
- .marketing-header__actions > .base-button--secondary {
383
+ .site-header__actions > .base-button--secondary {
384
384
  background: color-mix(in srgb, var(--site-color-surface) 78%, transparent);
385
385
  }
386
386
 
387
- .marketing-header__preferences,
387
+ .site-header__preferences,
388
388
  .mobile-nav__preferences {
389
389
  display: flex;
390
390
  align-items: center;
@@ -468,7 +468,7 @@ summary {
468
468
  margin-inline-start: auto;
469
469
  }
470
470
 
471
- .marketing-header__menu-button {
471
+ .site-header__menu-button {
472
472
  display: none;
473
473
  }
474
474
 
@@ -476,13 +476,13 @@ summary {
476
476
  display: none;
477
477
  }
478
478
 
479
- .marketing-footer {
479
+ .site-footer {
480
480
  border-top: 1px solid var(--site-color-border);
481
481
  background: linear-gradient(180deg, var(--site-color-surface), var(--site-color-bg-soft));
482
482
  color: var(--site-color-text);
483
483
  }
484
484
 
485
- .marketing-footer__inner {
485
+ .site-footer__inner {
486
486
  display: grid;
487
487
  width: min(1180px, calc(100% - 32px));
488
488
  margin: 0 auto;
@@ -491,39 +491,39 @@ summary {
491
491
  padding: 56px 0;
492
492
  }
493
493
 
494
- .marketing-footer .site-brand__text,
495
- .marketing-footer .site-brand {
494
+ .site-footer .site-brand__text,
495
+ .site-footer .site-brand {
496
496
  color: var(--site-color-text);
497
497
  }
498
498
 
499
- .marketing-footer__brand {
499
+ .site-footer__brand {
500
500
  display: grid;
501
501
  align-content: start;
502
502
  gap: 18px;
503
503
  }
504
504
 
505
- .marketing-footer__brand p {
505
+ .site-footer__brand p {
506
506
  margin: 0;
507
507
  max-width: 360px;
508
508
  color: var(--site-color-muted);
509
509
  line-height: 1.65;
510
510
  }
511
511
 
512
- .marketing-footer__social,
513
- .marketing-footer__legal nav {
512
+ .site-footer__social,
513
+ .site-footer__legal nav {
514
514
  display: flex;
515
515
  flex-wrap: wrap;
516
516
  gap: 12px;
517
517
  }
518
518
 
519
- .marketing-footer__social a,
520
- .marketing-footer__legal a,
519
+ .site-footer__social a,
520
+ .site-footer__legal a,
521
521
  .consent-settings-button {
522
522
  color: var(--site-color-muted);
523
523
  }
524
524
 
525
- .marketing-footer__social a:hover,
526
- .marketing-footer__legal a:hover,
525
+ .site-footer__social a:hover,
526
+ .site-footer__legal a:hover,
527
527
  .consent-settings-button:hover {
528
528
  color: var(--site-color-accent);
529
529
  }
@@ -536,20 +536,20 @@ summary {
536
536
  cursor: pointer;
537
537
  }
538
538
 
539
- .marketing-footer__nav {
539
+ .site-footer__nav {
540
540
  display: grid;
541
541
  grid-template-columns: repeat(3, minmax(0, 1fr));
542
542
  gap: 24px;
543
543
  }
544
544
 
545
- .marketing-footer__group h2 {
545
+ .site-footer__group h2 {
546
546
  margin: 0 0 14px;
547
547
  color: var(--site-color-accent-warm);
548
548
  font-size: 13px;
549
549
  font-weight: 650;
550
550
  }
551
551
 
552
- .marketing-footer__group ul {
552
+ .site-footer__group ul {
553
553
  display: grid;
554
554
  gap: 10px;
555
555
  margin: 0;
@@ -557,16 +557,16 @@ summary {
557
557
  list-style: none;
558
558
  }
559
559
 
560
- .marketing-footer__group a {
560
+ .site-footer__group a {
561
561
  color: var(--site-color-muted);
562
562
  font-size: 14px;
563
563
  }
564
564
 
565
- .marketing-footer__group a:hover {
565
+ .site-footer__group a:hover {
566
566
  color: var(--site-color-text);
567
567
  }
568
568
 
569
- .marketing-footer__legal {
569
+ .site-footer__legal {
570
570
  display: flex;
571
571
  width: min(1180px, calc(100% - 32px));
572
572
  margin: 0 auto;
@@ -1065,30 +1065,30 @@ summary {
1065
1065
  --site-shell-header-height: 64px;
1066
1066
  }
1067
1067
 
1068
- .marketing-header__inner {
1068
+ .site-header__inner {
1069
1069
  width: min(100% - 24px, 1180px);
1070
1070
  gap: 12px;
1071
1071
  }
1072
1072
 
1073
- .marketing-header .site-brand {
1073
+ .site-header .site-brand {
1074
1074
  flex: 1 1 auto;
1075
1075
  }
1076
1076
 
1077
- .marketing-header .site-brand__text {
1077
+ .site-header .site-brand__text {
1078
1078
  min-width: 0;
1079
1079
  }
1080
1080
 
1081
- .marketing-header__nav,
1082
- .marketing-header__preferences,
1083
- .marketing-header__actions > .base-button {
1081
+ .site-header__nav,
1082
+ .site-header__preferences,
1083
+ .site-header__actions > .base-button {
1084
1084
  display: none;
1085
1085
  }
1086
1086
 
1087
- .marketing-header__actions {
1087
+ .site-header__actions {
1088
1088
  flex: 0 0 auto;
1089
1089
  }
1090
1090
 
1091
- .marketing-header__menu-button {
1091
+ .site-header__menu-button {
1092
1092
  display: inline-flex;
1093
1093
  }
1094
1094
 
@@ -1144,18 +1144,18 @@ summary {
1144
1144
  width: 100%;
1145
1145
  }
1146
1146
 
1147
- .marketing-footer__inner,
1148
- .marketing-footer__nav,
1147
+ .site-footer__inner,
1148
+ .site-footer__nav,
1149
1149
  .content-list__grid {
1150
1150
  grid-template-columns: 1fr;
1151
1151
  }
1152
1152
 
1153
- .marketing-footer__inner {
1153
+ .site-footer__inner {
1154
1154
  gap: 34px;
1155
1155
  padding: 44px 0;
1156
1156
  }
1157
1157
 
1158
- .marketing-footer__legal {
1158
+ .site-footer__legal {
1159
1159
  align-items: flex-start;
1160
1160
  flex-direction: column;
1161
1161
  }
@@ -27,10 +27,10 @@ defineEmits<{
27
27
 
28
28
  <div class="application-header__right">
29
29
  <slot name="actions">
30
- <ThemeSwitcher />
31
- <LocaleSwitcher />
30
+ <ThemeMenu />
31
+ <LocaleMenu />
32
32
  <NuxtLinkLocale :to="site.homePath" class="base-button base-button--secondary">
33
- {{ t("site.openMarketingSite") }}
33
+ {{ t("site.openSite") }}
34
34
  </NuxtLinkLocale>
35
35
  </slot>
36
36
  </div>
@@ -29,7 +29,7 @@ watch(
29
29
  <template>
30
30
  <div class="application-shell">
31
31
  <slot name="header">
32
- <ApplicationHeader @toggle-sidebar="sidebarOpen = !sidebarOpen" />
32
+ <AppHeader @toggle-sidebar="sidebarOpen = !sidebarOpen" />
33
33
  </slot>
34
34
 
35
35
  <div class="application-shell__body">
@@ -44,7 +44,7 @@ watch(
44
44
  :class="{ 'application-shell__sidebar--open': sidebarOpen }"
45
45
  >
46
46
  <slot name="sidebar">
47
- <ApplicationSidebar @navigate="closeSidebar" />
47
+ <AppSidebar @navigate="closeSidebar" />
48
48
  </slot>
49
49
  </div>
50
50
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- const { appNavGroups } = useNavigation();
2
+ const { appGroups } = useNavigation();
3
3
  const { t } = useI18n();
4
4
 
5
5
  defineEmits<{
@@ -11,7 +11,7 @@ defineEmits<{
11
11
  <aside class="application-sidebar" :aria-label="t('navigation.application')">
12
12
  <nav class="application-sidebar__nav">
13
13
  <section
14
- v-for="group in appNavGroups"
14
+ v-for="group in appGroups"
15
15
  :key="group.label || group.items[0]?.to"
16
16
  class="application-sidebar__group"
17
17
  >
@@ -27,7 +27,12 @@ const visible = computed(() => props.showIdle || resolvedStatus.value !== "idle"
27
27
  const statusMeta = computed(() => {
28
28
  const map: Record<
29
29
  CheckoutStatusValue,
30
- { icon: string; tone: "neutral" | "info" | "success" | "warning" | "danger"; title: string; description: string }
30
+ {
31
+ icon: string;
32
+ tone: "neutral" | "info" | "success" | "warning" | "danger";
33
+ title: string;
34
+ description: string;
35
+ }
31
36
  > = {
32
37
  idle: {
33
38
  icon: "i-lucide-circle",
@@ -69,8 +74,8 @@ const statusMeta = computed(() => {
69
74
  <section v-if="visible" class="checkout-status" :data-tone="statusMeta.tone" role="status">
70
75
  <UIcon :name="statusMeta.icon" class="checkout-status__icon" />
71
76
  <div>
72
- <BaseText as="h3" size="lg" weight="bold">{{ statusMeta.title }}</BaseText>
73
- <BaseText as="p" tone="muted">{{ statusMeta.description }}</BaseText>
77
+ <UiText as="h3" size="lg" weight="bold">{{ statusMeta.title }}</UiText>
78
+ <UiText as="p" tone="muted">{{ statusMeta.description }}</UiText>
74
79
  </div>
75
80
  </section>
76
81
  </template>
@@ -87,33 +87,33 @@ function openCheckout(): void {
87
87
  <template>
88
88
  <section class="checkout-summary" aria-label="Checkout summary">
89
89
  <div class="checkout-summary__header">
90
- <BaseText as="h3" size="lg" weight="bold">Checkout summary</BaseText>
91
- <BaseText tone="muted" size="sm"
92
- >{{ lineItems.length }} item{{ lineItems.length === 1 ? "" : "s" }}</BaseText
90
+ <UiText as="h3" size="lg" weight="bold">Checkout summary</UiText>
91
+ <UiText tone="muted" size="sm"
92
+ >{{ lineItems.length }} item{{ lineItems.length === 1 ? "" : "s" }}</UiText
93
93
  >
94
94
  </div>
95
95
 
96
96
  <ul class="checkout-summary__items">
97
97
  <li v-for="item in lineItems" :key="item.id" class="checkout-summary__item">
98
98
  <span>
99
- <BaseText weight="semibold">{{ item.label }}</BaseText>
100
- <BaseText v-if="item.description" as="span" tone="muted" size="sm">
99
+ <UiText weight="semibold">{{ item.label }}</UiText>
100
+ <UiText v-if="item.description" as="span" tone="muted" size="sm">
101
101
  {{ item.description }}
102
- </BaseText>
102
+ </UiText>
103
103
  </span>
104
104
  <span class="checkout-summary__item-meta">
105
- <BaseText as="span" tone="muted" size="sm">x{{ item.quantity }}</BaseText>
106
- <BaseText as="strong" weight="bold">{{ formatLineItem(item) }}</BaseText>
105
+ <UiText as="span" tone="muted" size="sm">x{{ item.quantity }}</UiText>
106
+ <UiText as="strong" weight="bold">{{ formatLineItem(item) }}</UiText>
107
107
  </span>
108
108
  </li>
109
109
  </ul>
110
110
 
111
111
  <div class="checkout-summary__total">
112
- <BaseText as="span" tone="muted">Total</BaseText>
113
- <BaseText as="strong" size="xl" weight="bold">{{ formattedTotal }}</BaseText>
112
+ <UiText as="span" tone="muted">Total</UiText>
113
+ <UiText as="strong" size="xl" weight="bold">{{ formattedTotal }}</UiText>
114
114
  </div>
115
115
 
116
- <BaseButton
116
+ <UiButton
117
117
  :label="actionLabel"
118
118
  trailing-icon="i-lucide-arrow-right"
119
119
  :loading="loading"
@@ -5,7 +5,7 @@ const site = useSiteShell();
5
5
  const consent = useConsent();
6
6
  const { t } = useI18n();
7
7
 
8
- const persistenceMessage = computed(() => {
8
+ const storageMessage = computed(() => {
9
9
  if (consent.state.value.error === "read-failed") {
10
10
  return t("consent.storageUnavailable");
11
11
  }
@@ -47,8 +47,8 @@ const persistenceMessage = computed(() => {
47
47
  <NuxtLink class="site-consent__policy" :to="site.consent.policyPath">
48
48
  {{ t("consent.policy") }}
49
49
  </NuxtLink>
50
- <p v-if="persistenceMessage" class="site-consent__status" role="status">
51
- {{ persistenceMessage }}
50
+ <p v-if="storageMessage" class="site-consent__status" role="status">
51
+ {{ storageMessage }}
52
52
  </p>
53
53
  <div class="site-consent__actions">
54
54
  <button type="button" class="base-button base-button--secondary" @click="consent.reject">
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
- import type { ContentArticle, ContentListItem } from "~/types/site-shell";
2
+ import type { Article, ListItem } from "~/content/types";
3
3
 
4
4
  defineProps<{
5
- article: ContentArticle;
6
- related?: ContentListItem[];
5
+ article: Article;
6
+ related?: ListItem[];
7
7
  }>();
8
8
  const { t } = useI18n();
9
9
  </script>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import type { ContentListItem } from "~/types/site-shell";
2
+ import type { ListItem } from "~/content/types";
3
3
 
4
4
  defineProps<{
5
5
  title: string;
6
6
  description?: string;
7
- items: ContentListItem[];
7
+ items: ListItem[];
8
8
  }>();
9
9
  </script>
10
10
 
@@ -95,7 +95,7 @@ watch(
95
95
  <template>
96
96
  <div class="google-sign-in">
97
97
  <div v-if="identity.enabled" ref="buttonRef" class="google-sign-in__button" />
98
- <BaseButton
98
+ <UiButton
99
99
  v-else
100
100
  color="neutral"
101
101
  variant="outline"
@@ -103,8 +103,8 @@ watch(
103
103
  :label="disabledLabel"
104
104
  disabled
105
105
  />
106
- <BaseText v-if="loading" as="p" size="sm" tone="muted">Completing sign-in...</BaseText>
107
- <BaseText v-if="errorMessage" as="p" size="sm" tone="danger">{{ errorMessage }}</BaseText>
106
+ <UiText v-if="loading" as="p" size="sm" tone="muted">Completing sign-in...</UiText>
107
+ <UiText v-if="errorMessage" as="p" size="sm" tone="danger">{{ errorMessage }}</UiText>
108
108
  </div>
109
109
  </template>
110
110
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- const emit = defineEmits<{ changed: [] }>();
2
+ const emit = defineEmits<{ select: [code: typeof locale.value] }>();
3
3
  const { locale, locales, setLocale, t } = useI18n();
4
4
  const dropdown = ref<HTMLDetailsElement | null>(null);
5
5
 
@@ -16,7 +16,7 @@ const current = computed(
16
16
  async function selectLocale(code: typeof locale.value): Promise<void> {
17
17
  dropdown.value?.removeAttribute("open");
18
18
  if (code !== locale.value) await setLocale(code);
19
- emit("changed");
19
+ emit("select", code);
20
20
  }
21
21
 
22
22
  function handleKeydown(event: KeyboardEvent): void {
@@ -6,12 +6,12 @@ const year = new Date().getFullYear();
6
6
  </script>
7
7
 
8
8
  <template>
9
- <footer class="marketing-footer">
10
- <div class="marketing-footer__inner">
11
- <section class="marketing-footer__brand" :aria-label="t('site.region')">
9
+ <footer class="site-footer">
10
+ <div class="site-footer__inner">
11
+ <section class="site-footer__brand" :aria-label="t('site.region')">
12
12
  <SiteBrand />
13
13
  <p>{{ t(site.descriptionKey) }}</p>
14
- <div v-if="socialLinks.length" class="marketing-footer__social">
14
+ <div v-if="socialLinks.length" class="site-footer__social">
15
15
  <a
16
16
  v-for="link in socialLinks"
17
17
  :key="link.label"
@@ -24,8 +24,8 @@ const year = new Date().getFullYear();
24
24
  </div>
25
25
  </section>
26
26
 
27
- <nav class="marketing-footer__nav" :aria-label="t('footer.navigation')">
28
- <section v-for="group in footerGroups" :key="group.title" class="marketing-footer__group">
27
+ <nav class="site-footer__nav" :aria-label="t('footer.navigation')">
28
+ <section v-for="group in footerGroups" :key="group.title" class="site-footer__group">
29
29
  <h2>{{ group.title }}</h2>
30
30
  <ul>
31
31
  <li v-for="link in group.links" :key="link.label">
@@ -41,13 +41,13 @@ const year = new Date().getFullYear();
41
41
  </nav>
42
42
  </div>
43
43
 
44
- <div class="marketing-footer__legal">
44
+ <div class="site-footer__legal">
45
45
  <span>{{ t("footer.copyright", { year, site: site.name }) }}</span>
46
46
  <nav :aria-label="t('footer.legal')">
47
47
  <NuxtLinkLocale v-for="link in legalLinks" :key="link.label" :to="link.to">
48
48
  {{ link.label }}
49
49
  </NuxtLinkLocale>
50
- <ConsentSettingsButton />
50
+ <ConsentSettings />
51
51
  </nav>
52
52
  </div>
53
53
  </footer>