@uxfront/layer-docs 0.4.0 → 0.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 (99) hide show
  1. package/README.md +31 -220
  2. package/app/app.config.ts +8 -92
  3. package/app/components/content/FrameworkSwitcher.vue +66 -40
  4. package/app/components/docs/DocsAsideLeftTop.vue +9 -15
  5. package/app/components/docs/DocsFrameworkSelect.vue +4 -7
  6. package/app/composables/useFramework.ts +30 -38
  7. package/nuxt.config.ts +14 -170
  8. package/package.json +10 -77
  9. package/CHANGELOG.md +0 -180
  10. package/LICENSE +0 -21
  11. package/app/app.vue +0 -138
  12. package/app/assets/css/main.css +0 -15
  13. package/app/components/IconMenuToggle.vue +0 -92
  14. package/app/components/LanguageSelect.vue +0 -73
  15. package/app/components/MorphingGradientBackground.vue +0 -261
  16. package/app/components/OgImage/OgImageDocs.satori.vue +0 -40
  17. package/app/components/OgImage/OgImageLanding.satori.vue +0 -41
  18. package/app/components/app/AppFooter.vue +0 -13
  19. package/app/components/app/AppFooterCenter.vue +0 -17
  20. package/app/components/app/AppFooterLeft.vue +0 -21
  21. package/app/components/app/AppFooterRight.vue +0 -33
  22. package/app/components/app/AppHeader.vue +0 -123
  23. package/app/components/app/AppHeaderAttribution.vue +0 -45
  24. package/app/components/app/AppHeaderBody.vue +0 -14
  25. package/app/components/app/AppHeaderCTA.vue +0 -31
  26. package/app/components/app/AppHeaderCenter.vue +0 -10
  27. package/app/components/app/AppHeaderLogo.vue +0 -16
  28. package/app/components/app/AppOgDecoration.vue +0 -27
  29. package/app/components/app/AppOgLogo.vue +0 -19
  30. package/app/components/app/AppSearch.vue +0 -59
  31. package/app/components/app/AppSubHeader.vue +0 -21
  32. package/app/components/content/BrowserFrame.vue +0 -28
  33. package/app/components/content/GradientPageHero.vue +0 -35
  34. package/app/components/content/StorybookEmbed.vue +0 -160
  35. package/app/components/content/Video.vue +0 -103
  36. package/app/components/docs/DocsAsideLeftBody.vue +0 -20
  37. package/app/components/docs/DocsAsideRightBottom.vue +0 -15
  38. package/app/components/docs/DocsPageHeaderLinks.vue +0 -75
  39. package/app/composables/useDocsSections.ts +0 -57
  40. package/app/composables/useDocusI18n.ts +0 -49
  41. package/app/constants/sections.ts +0 -25
  42. package/app/error.vue +0 -140
  43. package/app/layouts/default.vue +0 -24
  44. package/app/pages/[[lang]]/[...slug].vue +0 -58
  45. package/app/pages/[[lang]]/docs/[section]/[...slug].vue +0 -180
  46. package/app/plugins/i18n.ts +0 -21
  47. package/app/plugins/posthog.client.ts +0 -56
  48. package/app/types/non-route-categories.ts +0 -12
  49. package/app/utils/flattenNavigation.ts +0 -22
  50. package/app/utils/foldNonRouteCategories.ts +0 -47
  51. package/app/utils/prerender.ts +0 -9
  52. package/app/utils/storybookEmbed.test.ts +0 -98
  53. package/app/utils/storybookEmbed.ts +0 -93
  54. package/i18n/locales/ar.json +0 -24
  55. package/i18n/locales/be.json +0 -24
  56. package/i18n/locales/bn.json +0 -24
  57. package/i18n/locales/ca.json +0 -24
  58. package/i18n/locales/ckb.json +0 -24
  59. package/i18n/locales/cs.json +0 -24
  60. package/i18n/locales/da.json +0 -24
  61. package/i18n/locales/de.json +0 -24
  62. package/i18n/locales/el.json +0 -24
  63. package/i18n/locales/en.json +0 -24
  64. package/i18n/locales/et.json +0 -24
  65. package/i18n/locales/fr.json +0 -24
  66. package/i18n/locales/he.json +0 -24
  67. package/i18n/locales/hi.json +0 -24
  68. package/i18n/locales/hy.json +0 -24
  69. package/i18n/locales/it.json +0 -24
  70. package/i18n/locales/ja.json +0 -24
  71. package/i18n/locales/kk.json +0 -24
  72. package/i18n/locales/km.json +0 -24
  73. package/i18n/locales/ko.json +0 -24
  74. package/i18n/locales/ky.json +0 -24
  75. package/i18n/locales/lb.json +0 -24
  76. package/i18n/locales/ms.json +0 -24
  77. package/i18n/locales/nb.json +0 -24
  78. package/i18n/locales/pl.json +0 -24
  79. package/i18n/locales/ru.json +0 -24
  80. package/i18n/locales/sl.json +0 -24
  81. package/i18n/locales/sv.json +0 -24
  82. package/i18n/locales/uk.json +0 -24
  83. package/i18n/locales/ur.json +0 -24
  84. package/i18n/locales/vi.json +0 -24
  85. package/modules/config.ts +0 -144
  86. package/modules/optimizeDeps.ts +0 -45
  87. package/modules/routing.ts +0 -20
  88. package/nuxt.schema.ts +0 -374
  89. package/server/plugins/llms-redirect.ts +0 -60
  90. package/server/routes/raw/[...slug].md.get.ts +0 -74
  91. package/storybook/index.test.ts +0 -110
  92. package/storybook/index.ts +0 -362
  93. package/test/brand-palette.ts +0 -235
  94. package/test/no-brand-leakage.test.ts +0 -124
  95. package/tsconfig.json +0 -17
  96. package/utils/accent.ts +0 -80
  97. package/utils/content.ts +0 -193
  98. package/utils/git.ts +0 -114
  99. package/utils/meta.ts +0 -28
@@ -1,15 +0,0 @@
1
- @import "tailwindcss";
2
- @import "@nuxt/ui";
3
-
4
- @source "../../../content/**/*";
5
- @source "../../../layers/**/*";
6
- @source "../../app.config.ts";
7
-
8
- /*
9
- * Neutral shell styling only. The brand palette (the `--color-*` scale and the
10
- * `--ui-primary` mapping) is consumer-supplied — each consuming app defines its
11
- * own `@theme` in its CSS entrypoint, layered over this neutral base.
12
- */
13
- .prose-container {
14
- --ui-container: 760px;
15
- }
@@ -1,92 +0,0 @@
1
- <script setup lang="ts">
2
- import { motion } from "motion-v";
3
- import type { VariantType } from "motion-v";
4
- import { useLocale } from "@nuxt/ui/composables/useLocale";
5
-
6
- const props = defineProps<{
7
- open: boolean;
8
- }>();
9
-
10
- // Same strings Nuxt UI's default `UHeader` toggle uses, so the accessible name
11
- // stays in sync with the app's locale instead of being hardcoded here.
12
- const { t } = useLocale();
13
-
14
- const variants: {
15
- [k: string]: VariantType | ((custom: unknown) => VariantType);
16
- } = {
17
- normal: {
18
- rotate: 0,
19
- y: 0,
20
- opacity: 1,
21
- },
22
- close: (custom: unknown) => {
23
- const c = custom as number;
24
- return {
25
- rotate: c === 1 ? 45 : c === 3 ? -45 : 0,
26
- y: c === 1 ? 6 : c === 3 ? -6 : 0,
27
- opacity: c === 2 ? 0 : 1,
28
- transition: {
29
- type: "spring",
30
- stiffness: 260,
31
- damping: 20,
32
- },
33
- };
34
- },
35
- };
36
-
37
- const state = computed(() => (props.open ? "close" : "normal"));
38
- </script>
39
-
40
- <template>
41
- <UButton
42
- size="sm"
43
- variant="ghost"
44
- color="neutral"
45
- class="-me-1.5"
46
- square
47
- :aria-label="open ? t('header.close') : t('header.open')"
48
- >
49
- <svg
50
- xmlns="http://www.w3.org/2000/svg"
51
- class="size-5"
52
- viewBox="0 0 24 24"
53
- fill="none"
54
- stroke="currentColor"
55
- stroke-width="2"
56
- stroke-linecap="round"
57
- stroke-linejoin="round"
58
- aria-hidden="true"
59
- >
60
- <motion.line
61
- x1="4"
62
- y1="6"
63
- x2="20"
64
- y2="6"
65
- :variants="variants"
66
- :animate="state"
67
- :custom="1"
68
- class="outline-none"
69
- />
70
- <motion.line
71
- x1="4"
72
- y1="12"
73
- x2="20"
74
- y2="12"
75
- :variants="variants"
76
- :animate="state"
77
- :custom="2"
78
- class="outline-none"
79
- />
80
- <motion.line
81
- x1="4"
82
- y1="18"
83
- x2="20"
84
- y2="18"
85
- :variants="variants"
86
- :animate="state"
87
- :custom="3"
88
- class="outline-none"
89
- />
90
- </svg>
91
- </UButton>
92
- </template>
@@ -1,73 +0,0 @@
1
- <script setup lang="ts">
2
- const { locale, locales, switchLocalePath } = useDocusI18n();
3
-
4
- function getEmojiFlag(locale: string): string {
5
- const languageToCountry: Record<string, string> = {
6
- ar: "sa", // Arabic -> Saudi Arabia
7
- bn: "bd", // Bengali -> Bangladesh
8
- ca: "es", // Catalan -> Spain
9
- ckb: "iq", // Central Kurdish -> Iraq
10
- cs: "cz", // Czech -> Czech Republic (note: modern country code is actually 'cz')
11
- da: "dk", // Danish -> Denmark
12
- el: "gr", // Greek -> Greece
13
- en: "gb", // English -> Great Britain
14
- et: "ee", // Estonian -> Estonia
15
- he: "il", // Hebrew -> Israel
16
- hi: "in", // Hindi -> India
17
- hy: "am", // Armenian -> Armenia
18
- ja: "jp", // Japanese -> Japan
19
- kk: "kz", // Kazakh -> Kazakhstan
20
- km: "kh", // Khmer -> Cambodia
21
- ko: "kr", // Korean -> South Korea
22
- ky: "kg", // Kyrgyz -> Kyrgyzstan
23
- lb: "lu", // Luxembourgish -> Luxembourg
24
- ms: "my", // Malay -> Malaysia
25
- nb: "no", // Norwegian Bokmål -> Norway
26
- sl: "si", // Slovenian -> Slovenia
27
- sv: "se", // Swedish -> Sweden
28
- uk: "ua", // Ukrainian -> Ukraine
29
- ur: "pk", // Urdu -> Pakistan
30
- vi: "vn", // Vietnamese -> Vietnam
31
- };
32
-
33
- const baseLanguage = locale.split("-")[0]?.toLowerCase() || locale;
34
- const countryCode = languageToCountry[baseLanguage] || locale.replace(/^.*-/, "").slice(0, 2);
35
-
36
- return countryCode
37
- .toUpperCase()
38
- .split("")
39
- .map((char) => String.fromCodePoint(0x1f1a5 + char.charCodeAt(0)))
40
- .join("");
41
- }
42
- </script>
43
-
44
- <template>
45
- <UPopover mode="hover" :content="{ align: 'end' }">
46
- <UButton color="neutral" variant="ghost" class="size-8">
47
- <template #trailing>
48
- <span class="text-lg">
49
- {{ getEmojiFlag(locale) }}
50
- </span>
51
- </template>
52
- </UButton>
53
-
54
- <template #content>
55
- <ul class="flex flex-col">
56
- <li v-for="localeItem in locales" :key="localeItem.code">
57
- <NuxtLink
58
- class="flex justify-between py-1.5 px-2 gap-1 hover:bg-muted"
59
- :to="switchLocalePath(localeItem.code) as string"
60
- :aria-label="localeItem.name"
61
- >
62
- <span class="text-sm">
63
- {{ localeItem.name }}
64
- </span>
65
- <span class="size-5 text-center">
66
- {{ getEmojiFlag(localeItem.code) }}
67
- </span>
68
- </NuxtLink>
69
- </li>
70
- </ul>
71
- </template>
72
- </UPopover>
73
- </template>
@@ -1,261 +0,0 @@
1
- <script setup lang="ts">
2
- import { onBeforeUnmount, onMounted, ref } from "vue";
3
-
4
- const currentX = ref(0);
5
- const currentY = ref(0);
6
- const mouseX = ref(0);
7
- const mouseY = ref(0);
8
-
9
- let animationId: number | null = null;
10
-
11
- const interactiveBubbleStyle = computed(() => {
12
- return {
13
- transform: `translate(${Math.round(currentX.value)}px, ${Math.round(currentY.value)}px)`,
14
- };
15
- });
16
-
17
- function move() {
18
- currentX.value += (mouseX.value - currentX.value) / 20;
19
- currentY.value += (mouseY.value - currentY.value) / 20;
20
- animationId = requestAnimationFrame(move);
21
- }
22
-
23
- function handleMouseMove(event: MouseEvent) {
24
- mouseX.value = event.clientX;
25
- mouseY.value = event.clientY;
26
- }
27
-
28
- onMounted(() => {
29
- move();
30
- });
31
-
32
- onBeforeUnmount(() => {
33
- if (animationId) {
34
- cancelAnimationFrame(animationId);
35
- }
36
- });
37
- </script>
38
-
39
- <template>
40
- <div class="morphing-gradient-background">
41
- <svg xmlns="http://www.w3.org/2000/svg">
42
- <defs>
43
- <filter id="goo">
44
- <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
45
- <feBlend in="SourceGraphic" in2="goo" />
46
- </filter>
47
- </defs>
48
- </svg>
49
- <div class="gradients-container">
50
- <div class="g1"></div>
51
- <div class="g2"></div>
52
- <div class="g3"></div>
53
- <div class="g4"></div>
54
- <div class="g5"></div>
55
- <div class="interactive" :style="interactiveBubbleStyle"></div>
56
- </div>
57
- <slot :onMousemove="handleMouseMove" />
58
- </div>
59
- </template>
60
-
61
- <style scoped>
62
- @keyframes moveInCircle {
63
- 0% {
64
- transform: rotate(0deg);
65
- }
66
- 50% {
67
- transform: rotate(180deg);
68
- }
69
- 100% {
70
- transform: rotate(360deg);
71
- }
72
- }
73
-
74
- @keyframes moveVertical {
75
- 0% {
76
- transform: translateY(-50%);
77
- }
78
- 50% {
79
- transform: translateY(50%);
80
- }
81
- 100% {
82
- transform: translateY(-50%);
83
- }
84
- }
85
-
86
- @keyframes moveHorizontal {
87
- 0% {
88
- transform: translateX(-50%) translateY(-10%);
89
- }
90
- 50% {
91
- transform: translateX(50%) translateY(10%);
92
- }
93
- 100% {
94
- transform: translateX(-50%) translateY(-10%);
95
- }
96
- }
97
-
98
- .morphing-gradient-background {
99
- --color-bg1: var(--ui-bg);
100
- --color-bg2: var(--ui-bg);
101
- --color1: 18, 113, 255;
102
- --color2: 221, 74, 255;
103
- --color3: 100, 220, 255;
104
- --color4: 200, 50, 50;
105
- --color5: 180, 180, 50;
106
- --color-interactive: 140, 100, 255;
107
- --circle-size: 80%;
108
- --blending: hard-light;
109
- --intensity: 0.2;
110
-
111
- position: relative;
112
- background: linear-gradient(40deg, var(--color-bg1), var(--color-bg2));
113
- overflow: hidden;
114
- min-height: 100%;
115
- width: 100%;
116
-
117
- .gradients-container {
118
- filter: url(#goo) blur(40px);
119
- width: 100%;
120
- height: 100%;
121
- position: absolute;
122
- top: 0;
123
- left: 0;
124
- right: 0;
125
- bottom: 0;
126
- overflow: hidden;
127
- pointer-events: none;
128
- }
129
-
130
- svg {
131
- position: absolute;
132
- top: 0;
133
- left: 0;
134
- width: 0;
135
- height: 0;
136
- }
137
-
138
- .g1 {
139
- position: absolute;
140
- background: radial-gradient(
141
- circle at center,
142
- rgba(var(--color1), var(--intensity)) 0,
143
- rgba(var(--color1), 0) 50%
144
- )
145
- no-repeat;
146
- mix-blend-mode: var(--blending);
147
-
148
- width: var(--circle-size);
149
- height: var(--circle-size);
150
- top: calc(50% - var(--circle-size) / 2);
151
- left: calc(50% - var(--circle-size) / 2);
152
-
153
- transform-origin: center center;
154
- animation: moveVertical 30s ease infinite;
155
-
156
- opacity: 1;
157
- }
158
-
159
- .g2 {
160
- position: absolute;
161
- background: radial-gradient(
162
- circle at center,
163
- rgba(var(--color2), var(--intensity)) 0,
164
- rgba(var(--color2), 0) 50%
165
- )
166
- no-repeat;
167
- mix-blend-mode: var(--blending);
168
-
169
- width: var(--circle-size);
170
- height: var(--circle-size);
171
- top: calc(50% - var(--circle-size) / 2);
172
- left: calc(50% - var(--circle-size) / 2);
173
-
174
- transform-origin: calc(50% - 400px);
175
- animation: moveInCircle 20s reverse infinite;
176
-
177
- opacity: 1;
178
- }
179
-
180
- .g3 {
181
- position: absolute;
182
- background: radial-gradient(
183
- circle at center,
184
- rgba(var(--color3), var(--intensity)) 0,
185
- rgba(var(--color3), 0) 50%
186
- )
187
- no-repeat;
188
- mix-blend-mode: var(--blending);
189
-
190
- width: var(--circle-size);
191
- height: var(--circle-size);
192
- top: calc(50% - var(--circle-size) / 2 + 200px);
193
- left: calc(50% - var(--circle-size) / 2 - 500px);
194
-
195
- transform-origin: calc(50% + 400px);
196
- animation: moveInCircle 40s linear infinite;
197
-
198
- opacity: 1;
199
- }
200
-
201
- .g4 {
202
- position: absolute;
203
- background: radial-gradient(
204
- circle at center,
205
- rgba(var(--color4), var(--intensity)) 0,
206
- rgba(var(--color4), 0) 50%
207
- )
208
- no-repeat;
209
- mix-blend-mode: var(--blending);
210
-
211
- width: var(--circle-size);
212
- height: var(--circle-size);
213
- top: calc(50% - var(--circle-size) / 2);
214
- left: calc(50% - var(--circle-size) / 2);
215
-
216
- transform-origin: calc(50% - 200px);
217
- animation: moveHorizontal 40s ease infinite;
218
-
219
- opacity: 0.7;
220
- }
221
-
222
- .g5 {
223
- position: absolute;
224
- background: radial-gradient(
225
- circle at center,
226
- rgba(var(--color5), var(--intensity)) 0,
227
- rgba(var(--color5), 0) 50%
228
- )
229
- no-repeat;
230
- mix-blend-mode: var(--blending);
231
-
232
- width: calc(var(--circle-size) * 2);
233
- height: calc(var(--circle-size) * 2);
234
- top: calc(50% - var(--circle-size));
235
- left: calc(50% - var(--circle-size));
236
-
237
- transform-origin: calc(50% - 800px) calc(50% + 200px);
238
- animation: moveInCircle 20s ease infinite;
239
-
240
- opacity: 1;
241
- }
242
-
243
- .interactive {
244
- position: absolute;
245
- background: radial-gradient(
246
- circle at center,
247
- rgba(var(--color-interactive), var(--intensity)) 0,
248
- rgba(var(--color-interactive), 0) 50%
249
- )
250
- no-repeat;
251
- mix-blend-mode: var(--blending);
252
-
253
- width: 100%;
254
- height: 100%;
255
- top: -50%;
256
- left: -50%;
257
-
258
- opacity: 0.7;
259
- }
260
- }
261
- </style>
@@ -1,40 +0,0 @@
1
- <script lang="ts" setup>
2
- /**
3
- * Default OG card for docs, changelog and landing pages — `defineOgImage("DocsSatori", ...)`.
4
- *
5
- * Brandable without touching this package: the accent comes from the consumer's
6
- * own `--ui-primary` token (resolved in `modules/config.ts`), and both the
7
- * decoration and the wordmark are components a consumer can shadow. See
8
- * README § OG images.
9
- */
10
- withDefaults(defineProps<{ title?: string; description?: string; headline?: string }>(), {
11
- title: "",
12
- description: "",
13
- headline: "",
14
- });
15
-
16
- const appConfig = useAppConfig();
17
- const accent = computed(() => appConfig.ogImage?.accent);
18
- </script>
19
-
20
- <template>
21
- <div class="w-full h-full flex flex-col justify-center bg-neutral-900">
22
- <AppOgDecoration />
23
-
24
- <div class="pl-[100px]">
25
- <p
26
- v-if="headline"
27
- class="uppercase text-[24px] mb-4 font-semibold"
28
- :style="{ color: accent }"
29
- >
30
- {{ headline }}
31
- </p>
32
- <h1 v-if="title" class="m-0 text-[75px] font-semibold mb-4 text-white flex items-center">
33
- <span>{{ title.slice(0, 60) }}</span>
34
- </h1>
35
- <p v-if="description" class="text-[32px] text-neutral-300 leading-tight w-[700px]">
36
- {{ description.slice(0, 200) }}
37
- </p>
38
- </div>
39
- </div>
40
- </template>
@@ -1,41 +0,0 @@
1
- <script lang="ts" setup>
2
- /**
3
- * Centred OG card variant for landing pages — `defineOgImage("LandingSatori", ...)`.
4
- *
5
- * Same brand contract as {@link OgImageDocs}: accent from the consumer's token,
6
- * decoration and wordmark as overridable components.
7
- */
8
- withDefaults(defineProps<{ title?: string; description?: string; headline?: string }>(), {
9
- title: "",
10
- description: "",
11
- headline: "",
12
- });
13
-
14
- const appConfig = useAppConfig();
15
- const accent = computed(() => appConfig.ogImage?.accent);
16
- </script>
17
-
18
- <template>
19
- <div class="w-full h-full flex items-center justify-center bg-neutral-900">
20
- <AppOgDecoration />
21
-
22
- <div class="flex flex-col justify-center p-8">
23
- <div class="flex justify-center mb-8">
24
- <AppOgLogo />
25
- </div>
26
- <p
27
- v-if="headline"
28
- class="flex justify-center uppercase text-[24px] mb-4 font-semibold"
29
- :style="{ color: accent }"
30
- >
31
- {{ headline }}
32
- </p>
33
- <h1 v-if="title" class="flex justify-center m-0 text-5xl font-semibold mb-4 text-white">
34
- <span>{{ title.slice(0, 60) }}</span>
35
- </h1>
36
- <p v-if="description" class="text-center text-2xl text-neutral-300 leading-tight">
37
- {{ description.slice(0, 200) }}
38
- </p>
39
- </div>
40
- </div>
41
- </template>
@@ -1,13 +0,0 @@
1
- <template>
2
- <UFooter>
3
- <template #left>
4
- <AppFooterLeft />
5
- </template>
6
-
7
- <AppFooterCenter />
8
-
9
- <template #right>
10
- <AppFooterRight />
11
- </template>
12
- </UFooter>
13
- </template>
@@ -1,17 +0,0 @@
1
- <script lang="ts" setup>
2
- import type { NavigationMenuItem } from "@nuxt/ui";
3
-
4
- /**
5
- * Footer link row. Brand/legal links (Privacy, Terms, ...) are consumer data,
6
- * read from `footer.links` in `app.config.ts`. This layer ships none.
7
- */
8
- const appConfig = useAppConfig();
9
-
10
- const links = computed<NavigationMenuItem[]>(
11
- () => (appConfig.footer?.links as NavigationMenuItem[] | undefined) ?? [],
12
- );
13
- </script>
14
-
15
- <template>
16
- <UNavigationMenu v-if="links.length" orientation="horizontal" :items="links" />
17
- </template>
@@ -1,21 +0,0 @@
1
- <script setup lang="ts">
2
- /**
3
- * Footer credit line. The consumer supplies the full string via
4
- * `footer.credits` in `app.config.ts` (rendered verbatim). When absent, a
5
- * sensible default is built from the header title so a minimally-configured app
6
- * still shows a credit.
7
- */
8
- const appConfig = useAppConfig();
9
-
10
- const credit = computed(() => {
11
- if (appConfig.footer?.credits) return appConfig.footer.credits;
12
- const name = appConfig.header?.title;
13
- return name ? `© ${new Date().getFullYear()} ${name}. All rights reserved.` : "";
14
- });
15
- </script>
16
-
17
- <template>
18
- <div v-if="credit" class="text-sm text-muted">
19
- {{ credit }}
20
- </div>
21
- </template>
@@ -1,33 +0,0 @@
1
- <script setup lang="ts">
2
- const appConfig = useAppConfig();
3
-
4
- const links = computed(() =>
5
- [
6
- ...Object.entries(appConfig.socials || {}).map(([key, url]) => ({
7
- icon: `i-simple-icons-${key}`,
8
- to: url,
9
- target: "_blank",
10
- "aria-label": `${key} social link`,
11
- })),
12
- appConfig.github &&
13
- appConfig.github.url && {
14
- icon: "i-simple-icons-github",
15
- to: appConfig.github.url,
16
- target: "_blank",
17
- "aria-label": "GitHub repository",
18
- },
19
- ].filter(Boolean),
20
- );
21
- </script>
22
-
23
- <template>
24
- <template v-if="links.length">
25
- <UButton
26
- v-for="(link, index) of links"
27
- :key="index"
28
- size="sm"
29
- v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
30
- />
31
- </template>
32
- <UColorModeButton />
33
- </template>