@eox/pages-theme-eox 2.0.0-alpha.3 → 2.0.0-alpha.5
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/package.json +1 -1
- package/src/components/BlogList.vue +21 -7
- package/src/components/BlogPost.vue +17 -7
- package/src/components/CTASection.vue +17 -3
- package/src/components/FeatureCard/Index.vue +43 -9
- package/src/components/FeatureCard/Poster.vue +25 -3
- package/src/components/FeaturesGallery.vue +29 -2
- package/src/components/Footer.vue +5 -2
- package/src/components/ImageCarousel.vue +11 -2
- package/src/components/ImageElement.vue +16 -4
- package/src/components/ImageGallery.vue +29 -4
- package/src/components/LogoSection.vue +21 -2
- package/src/components/NavBar.vue +4 -4
- package/src/components/TagList.vue +3 -2
package/package.json
CHANGED
|
@@ -49,10 +49,17 @@
|
|
|
49
49
|
"
|
|
50
50
|
:date="post.date"
|
|
51
51
|
:authors="post.authors"
|
|
52
|
-
:chips="
|
|
52
|
+
:chips="
|
|
53
|
+
post.tags
|
|
54
|
+
? post.tags.map((t) => ({
|
|
55
|
+
text: t,
|
|
56
|
+
link: withBase(`/tag/${t}/`),
|
|
57
|
+
}))
|
|
58
|
+
: []
|
|
59
|
+
"
|
|
53
60
|
:link="{
|
|
54
61
|
text: 'Read more',
|
|
55
|
-
href: post.url,
|
|
62
|
+
href: withBase(post.url),
|
|
56
63
|
target: '_self',
|
|
57
64
|
}"
|
|
58
65
|
variant="cover"
|
|
@@ -133,7 +140,7 @@
|
|
|
133
140
|
<div class="sidebar-widget">
|
|
134
141
|
<div class="sidebar-title-container">
|
|
135
142
|
<h3 class="sidebar-title-inline">Hot tags</h3>
|
|
136
|
-
<a href="/tag" class="see-all-link">
|
|
143
|
+
<a :href="withBase('/tag')" class="see-all-link">
|
|
137
144
|
<span>see all</span>
|
|
138
145
|
<i class="mdi mdi-arrow-right"></i>
|
|
139
146
|
</a>
|
|
@@ -142,7 +149,7 @@
|
|
|
142
149
|
<a
|
|
143
150
|
v-for="tag in calculatedHotTags"
|
|
144
151
|
:key="tag.name"
|
|
145
|
-
:href="`/tag/${tag.name}/`"
|
|
152
|
+
:href="withBase(`/tag/${tag.name}/`)"
|
|
146
153
|
:style="getTagStyle(tag.name)"
|
|
147
154
|
class="sidebar-tag"
|
|
148
155
|
>
|
|
@@ -157,7 +164,7 @@
|
|
|
157
164
|
|
|
158
165
|
<script setup>
|
|
159
166
|
import { ref, computed, onMounted, watch } from "vue";
|
|
160
|
-
import { useRouter, useData } from "vitepress";
|
|
167
|
+
import { useRouter, useData, withBase } from "vitepress";
|
|
161
168
|
import { data as posts } from "../posts.data.js";
|
|
162
169
|
import { data as authorsList } from "../authors.data.js";
|
|
163
170
|
import { getTagColor, getTagStyle } from "../tagUtils.js";
|
|
@@ -198,10 +205,17 @@ const postsPerPage = 9;
|
|
|
198
205
|
// Form cover image URL correctly (prefixing filename with /images/)
|
|
199
206
|
function getCoverImageUrl(coverImage) {
|
|
200
207
|
if (!coverImage) return "";
|
|
201
|
-
if (coverImage.startsWith("
|
|
208
|
+
if (coverImage.startsWith("http") || coverImage.startsWith("//")) {
|
|
209
|
+
return coverImage;
|
|
210
|
+
}
|
|
211
|
+
const base = withBase("/");
|
|
212
|
+
if (base && base !== "/" && coverImage.startsWith(base)) {
|
|
202
213
|
return coverImage;
|
|
203
214
|
}
|
|
204
|
-
|
|
215
|
+
const path = coverImage.startsWith("/")
|
|
216
|
+
? coverImage
|
|
217
|
+
: `/images/${coverImage}`;
|
|
218
|
+
return withBase(path);
|
|
205
219
|
}
|
|
206
220
|
|
|
207
221
|
const tagFromParam = computed(() => {
|
|
@@ -40,7 +40,10 @@
|
|
|
40
40
|
:key="authorId"
|
|
41
41
|
class="author-item"
|
|
42
42
|
>
|
|
43
|
-
<a
|
|
43
|
+
<a
|
|
44
|
+
:href="withBase(`/author/${authorId}/`)"
|
|
45
|
+
class="author-link-active"
|
|
46
|
+
>
|
|
44
47
|
{{ getAuthorName(authorId) }}
|
|
45
48
|
</a>
|
|
46
49
|
<span v-if="idx < authors.length - 1" class="author-separator"
|
|
@@ -58,7 +61,7 @@
|
|
|
58
61
|
<a
|
|
59
62
|
v-for="tag in frontmatter.tags"
|
|
60
63
|
:key="tag"
|
|
61
|
-
:href="`/tag/${tag}/`"
|
|
64
|
+
:href="withBase(`/tag/${tag}/`)"
|
|
62
65
|
:style="getTagStyle(tag)"
|
|
63
66
|
class="post-tag"
|
|
64
67
|
>
|
|
@@ -87,7 +90,7 @@
|
|
|
87
90
|
<a
|
|
88
91
|
v-for="tag in calculatedHotTags"
|
|
89
92
|
:key="tag.name"
|
|
90
|
-
:href="`/tag/${tag.name}/`"
|
|
93
|
+
:href="withBase(`/tag/${tag.name}/`)"
|
|
91
94
|
:style="getTagStyle(tag.name)"
|
|
92
95
|
class="sidebar-tag"
|
|
93
96
|
>
|
|
@@ -106,7 +109,7 @@
|
|
|
106
109
|
class="flex flex-col gap-1 border-b border-slate-100 last:border-none pb-3 last:pb-0"
|
|
107
110
|
>
|
|
108
111
|
<a
|
|
109
|
-
:href="post.url"
|
|
112
|
+
:href="withBase(post.url)"
|
|
110
113
|
class="text-sm font-semibold text-slate-900 hover:text-primary leading-snug transition-colors line-clamp-2"
|
|
111
114
|
>
|
|
112
115
|
{{ post.title }}
|
|
@@ -123,7 +126,7 @@
|
|
|
123
126
|
</template>
|
|
124
127
|
|
|
125
128
|
<script setup>
|
|
126
|
-
import { useData } from "vitepress";
|
|
129
|
+
import { useData, withBase } from "vitepress";
|
|
127
130
|
import { computed } from "vue";
|
|
128
131
|
import { data as posts } from "../posts.data.js";
|
|
129
132
|
import { data as authorsList } from "../authors.data.js";
|
|
@@ -145,10 +148,17 @@ const headers = computed(() => {
|
|
|
145
148
|
// Form cover image URL correctly (prefixing filename with /images/)
|
|
146
149
|
function getCoverImageUrl(coverImage) {
|
|
147
150
|
if (!coverImage) return "";
|
|
148
|
-
if (coverImage.startsWith("
|
|
151
|
+
if (coverImage.startsWith("http") || coverImage.startsWith("//")) {
|
|
152
|
+
return coverImage;
|
|
153
|
+
}
|
|
154
|
+
const base = withBase("/");
|
|
155
|
+
if (base && base !== "/" && coverImage.startsWith(base)) {
|
|
149
156
|
return coverImage;
|
|
150
157
|
}
|
|
151
|
-
|
|
158
|
+
const path = coverImage.startsWith("/")
|
|
159
|
+
? coverImage
|
|
160
|
+
: `/images/${coverImage}`;
|
|
161
|
+
return withBase(path);
|
|
152
162
|
}
|
|
153
163
|
|
|
154
164
|
// Format publication date
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
v-if="primaryButton"
|
|
19
19
|
class="button extra small-margin responsive-mobile"
|
|
20
20
|
:class="dark !== undefined ? 'surface' : 'primary'"
|
|
21
|
-
:href="
|
|
21
|
+
:href="resolvedPrimaryLink"
|
|
22
22
|
@click="
|
|
23
23
|
trackEvent(['CTA', 'Click', 'CTA Section Primary', primaryButton])
|
|
24
24
|
"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<a
|
|
28
28
|
v-if="secondaryButton"
|
|
29
29
|
class="button extra small-margin secondary responsive-mobile"
|
|
30
|
-
:href="
|
|
30
|
+
:href="resolvedSecondaryLink"
|
|
31
31
|
@click="
|
|
32
32
|
trackEvent(['CTA', 'Click', 'CTA Section Secondary', secondaryButton])
|
|
33
33
|
"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
v-if="altButton"
|
|
38
38
|
class="button border extra small-margin responsive-mobile"
|
|
39
39
|
:class="dark !== undefined ? 'white-text' : ''"
|
|
40
|
-
:href="
|
|
40
|
+
:href="resolvedAltLink"
|
|
41
41
|
@click="trackEvent(['CTA', 'Click', 'CTA Section Alt', altButton])"
|
|
42
42
|
>{{ altButton }}</a
|
|
43
43
|
>
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
</template>
|
|
50
50
|
|
|
51
51
|
<script setup>
|
|
52
|
+
import { computed } from "vue";
|
|
53
|
+
import { withBase } from "vitepress";
|
|
52
54
|
import { trackEvent } from "../helpers";
|
|
53
55
|
const props = defineProps([
|
|
54
56
|
"dark",
|
|
@@ -61,6 +63,18 @@ const props = defineProps([
|
|
|
61
63
|
"tagline",
|
|
62
64
|
"title",
|
|
63
65
|
]);
|
|
66
|
+
|
|
67
|
+
function resolveLink(link) {
|
|
68
|
+
if (!link) return "";
|
|
69
|
+
if (link.startsWith("http") || link.startsWith("//")) return link;
|
|
70
|
+
const base = withBase("/");
|
|
71
|
+
if (base && base !== "/" && link.startsWith(base)) return link;
|
|
72
|
+
return link.startsWith("/") ? withBase(link) : withBase("/" + link);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const resolvedPrimaryLink = computed(() => resolveLink(props.primaryLink));
|
|
76
|
+
const resolvedSecondaryLink = computed(() => resolveLink(props.secondaryLink));
|
|
77
|
+
const resolvedAltLink = computed(() => resolveLink(props.altLink));
|
|
64
78
|
</script>
|
|
65
79
|
|
|
66
80
|
<style>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { withBase } from "vitepress";
|
|
3
4
|
import { data as authorsList } from "../../authors.data.js";
|
|
4
5
|
import { getTagColor } from "../../tagUtils.js";
|
|
5
6
|
|
|
@@ -53,6 +54,22 @@ const props = defineProps({
|
|
|
53
54
|
},
|
|
54
55
|
});
|
|
55
56
|
|
|
57
|
+
function resolveUrl(url) {
|
|
58
|
+
if (!url) return "";
|
|
59
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
60
|
+
const base = withBase("/");
|
|
61
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
62
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const resolvedImage = computed(() => resolveUrl(props.image));
|
|
66
|
+
const resolvedLinkHref = computed(() =>
|
|
67
|
+
props.link?.href ? resolveUrl(props.link.href) : undefined,
|
|
68
|
+
);
|
|
69
|
+
const resolvedSecondaryLinkHref = computed(() =>
|
|
70
|
+
props.secondaryLink?.href ? resolveUrl(props.secondaryLink.href) : undefined,
|
|
71
|
+
);
|
|
72
|
+
|
|
56
73
|
const iconStyle = {
|
|
57
74
|
width: (props.icon?.width ?? 40) + "px",
|
|
58
75
|
height: (props.icon?.height ?? 40) + "px",
|
|
@@ -133,8 +150,13 @@ function formatDate(dateString) {
|
|
|
133
150
|
]"
|
|
134
151
|
>
|
|
135
152
|
<!-- Cover Image for cover/blog/rich cards -->
|
|
136
|
-
<a v-if="isCoverLayout" :href="
|
|
137
|
-
<img
|
|
153
|
+
<a v-if="isCoverLayout" :href="resolvedLinkHref" class="card-cover-link">
|
|
154
|
+
<img
|
|
155
|
+
:src="resolvedImage"
|
|
156
|
+
alt=""
|
|
157
|
+
class="card-cover-image"
|
|
158
|
+
loading="lazy"
|
|
159
|
+
/>
|
|
138
160
|
</a>
|
|
139
161
|
|
|
140
162
|
<div class="card-content-wrapper">
|
|
@@ -151,7 +173,7 @@ function formatDate(dateString) {
|
|
|
151
173
|
<!-- Render small icon image only if NOT rendering a full cover image -->
|
|
152
174
|
<img
|
|
153
175
|
v-else-if="image && !isCoverLayout"
|
|
154
|
-
:src="
|
|
176
|
+
:src="resolvedImage"
|
|
155
177
|
alt=""
|
|
156
178
|
class="icon"
|
|
157
179
|
/>
|
|
@@ -172,7 +194,7 @@ function formatDate(dateString) {
|
|
|
172
194
|
:key="authorId"
|
|
173
195
|
class="author-item"
|
|
174
196
|
>
|
|
175
|
-
<a :href="`/author/${authorId}/`" class="author-link">{{
|
|
197
|
+
<a :href="withBase(`/author/${authorId}/`)" class="author-link">{{
|
|
176
198
|
getAuthorName(authorId)
|
|
177
199
|
}}</a
|
|
178
200
|
><span v-if="idx < authors.length - 1" class="author-separator"
|
|
@@ -187,7 +209,13 @@ function formatDate(dateString) {
|
|
|
187
209
|
<a
|
|
188
210
|
v-for="chip in chips"
|
|
189
211
|
:key="chip.text"
|
|
190
|
-
:href="
|
|
212
|
+
:href="
|
|
213
|
+
chip.link
|
|
214
|
+
? resolveUrl(chip.link)
|
|
215
|
+
: chip.href
|
|
216
|
+
? resolveUrl(chip.href)
|
|
217
|
+
: withBase(`/tag/${chip.text}/`)
|
|
218
|
+
"
|
|
191
219
|
:style="getTagStyle(chip.text)"
|
|
192
220
|
class="tag-link"
|
|
193
221
|
>
|
|
@@ -197,7 +225,7 @@ function formatDate(dateString) {
|
|
|
197
225
|
</div>
|
|
198
226
|
|
|
199
227
|
<h5 class="small card-title-text">
|
|
200
|
-
<a v-if="
|
|
228
|
+
<a v-if="resolvedLinkHref" :href="resolvedLinkHref">{{ title }}</a>
|
|
201
229
|
<span v-else>{{ title }}</span>
|
|
202
230
|
</h5>
|
|
203
231
|
|
|
@@ -210,7 +238,13 @@ function formatDate(dateString) {
|
|
|
210
238
|
<template v-for="chip in chips" :key="chip.text">
|
|
211
239
|
<a
|
|
212
240
|
v-if="chip.link || chip.href"
|
|
213
|
-
:href="
|
|
241
|
+
:href="
|
|
242
|
+
chip.link
|
|
243
|
+
? resolveUrl(chip.link)
|
|
244
|
+
: chip.href
|
|
245
|
+
? resolveUrl(chip.href)
|
|
246
|
+
: undefined
|
|
247
|
+
"
|
|
214
248
|
:style="getTagStyle(chip.text)"
|
|
215
249
|
class="chip small-chip border clickable-chip"
|
|
216
250
|
:class="chip.class"
|
|
@@ -260,7 +294,7 @@ function formatDate(dateString) {
|
|
|
260
294
|
>
|
|
261
295
|
<a
|
|
262
296
|
v-if="link && !!link.text"
|
|
263
|
-
:href="
|
|
297
|
+
:href="resolvedLinkHref"
|
|
264
298
|
:target="link.target ?? '_blank'"
|
|
265
299
|
class="button transparent bold primary-text no-padding"
|
|
266
300
|
>
|
|
@@ -271,7 +305,7 @@ function formatDate(dateString) {
|
|
|
271
305
|
</a>
|
|
272
306
|
<a
|
|
273
307
|
v-if="secondaryLink"
|
|
274
|
-
:href="
|
|
308
|
+
:href="resolvedSecondaryLinkHref"
|
|
275
309
|
:target="secondaryLink.target ?? '_blank'"
|
|
276
310
|
class="button transparent primary-text no-padding secondary-link"
|
|
277
311
|
>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
+
import { withBase } from "vitepress";
|
|
3
4
|
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
title: String,
|
|
@@ -41,6 +42,22 @@ const props = defineProps({
|
|
|
41
42
|
},
|
|
42
43
|
});
|
|
43
44
|
|
|
45
|
+
function resolveUrl(url) {
|
|
46
|
+
if (!url) return "";
|
|
47
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
48
|
+
const base = withBase("/");
|
|
49
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
50
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const resolvedImage = computed(() => resolveUrl(props.image));
|
|
54
|
+
const resolvedLinkHref = computed(() =>
|
|
55
|
+
props.link?.href ? resolveUrl(props.link.href) : undefined,
|
|
56
|
+
);
|
|
57
|
+
const resolvedSecondaryLinkHref = computed(() =>
|
|
58
|
+
props.secondaryLink?.href ? resolveUrl(props.secondaryLink.href) : undefined,
|
|
59
|
+
);
|
|
60
|
+
|
|
44
61
|
const cardStyle = computed(() => ({
|
|
45
62
|
minHeight: props.minHeight,
|
|
46
63
|
}));
|
|
@@ -52,7 +69,12 @@ const cardStyle = computed(() => ({
|
|
|
52
69
|
:class="link && !link.text ? 'small-elevate' : 'no-elevate'"
|
|
53
70
|
:style="cardStyle"
|
|
54
71
|
>
|
|
55
|
-
<img
|
|
72
|
+
<img
|
|
73
|
+
v-if="image"
|
|
74
|
+
:src="resolvedImage"
|
|
75
|
+
alt=""
|
|
76
|
+
class="image-overlay-card-image"
|
|
77
|
+
/>
|
|
56
78
|
<div class="image-overlay-card-overlay"></div>
|
|
57
79
|
<div class="image-overlay-card-content padding">
|
|
58
80
|
<div style="padding-top: 3rem">
|
|
@@ -103,7 +125,7 @@ const cardStyle = computed(() => ({
|
|
|
103
125
|
<a
|
|
104
126
|
v-if="link && link.text"
|
|
105
127
|
class="button small white black-text"
|
|
106
|
-
:href="
|
|
128
|
+
:href="resolvedLinkHref"
|
|
107
129
|
:target="link.target ?? '_blank'"
|
|
108
130
|
>
|
|
109
131
|
{{ link.text }}
|
|
@@ -111,7 +133,7 @@ const cardStyle = computed(() => ({
|
|
|
111
133
|
<a
|
|
112
134
|
v-if="secondaryLink"
|
|
113
135
|
class="button small border white-text secondary-link"
|
|
114
|
-
:href="
|
|
136
|
+
:href="resolvedSecondaryLinkHref"
|
|
115
137
|
:target="secondaryLink.target ?? '_blank'"
|
|
116
138
|
>
|
|
117
139
|
{{ secondaryLink.text }}
|
|
@@ -63,9 +63,36 @@ const resolveCardComponent = (variant) => {
|
|
|
63
63
|
|
|
64
64
|
const { page, site } = useData();
|
|
65
65
|
|
|
66
|
+
function resolveUrl(url) {
|
|
67
|
+
if (!url) return "";
|
|
68
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
69
|
+
const base = withBase("/");
|
|
70
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
71
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeFeature(feature) {
|
|
75
|
+
return {
|
|
76
|
+
...feature,
|
|
77
|
+
image: resolveUrl(feature.image),
|
|
78
|
+
link: feature.link
|
|
79
|
+
? {
|
|
80
|
+
...feature.link,
|
|
81
|
+
href: resolveUrl(feature.link.href),
|
|
82
|
+
}
|
|
83
|
+
: undefined,
|
|
84
|
+
secondaryLink: feature.secondaryLink
|
|
85
|
+
? {
|
|
86
|
+
...feature.secondaryLink,
|
|
87
|
+
href: resolveUrl(feature.secondaryLink.href),
|
|
88
|
+
}
|
|
89
|
+
: undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
66
93
|
const featuresExcerpts =
|
|
67
94
|
cards && cards.length
|
|
68
|
-
? cards
|
|
95
|
+
? cards.map(normalizeFeature)
|
|
69
96
|
: features
|
|
70
97
|
.map((f) => {
|
|
71
98
|
if (import.meta.env.SSR) {
|
|
@@ -89,7 +116,7 @@ const featuresExcerpts =
|
|
|
89
116
|
}
|
|
90
117
|
: undefined,
|
|
91
118
|
link: {
|
|
92
|
-
href: f.url,
|
|
119
|
+
href: withBase(f.url),
|
|
93
120
|
text: "Read more",
|
|
94
121
|
target: "_self",
|
|
95
122
|
},
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
<a
|
|
15
15
|
v-if="theme.nav.find((i) => i.link && i.link.includes('contact'))"
|
|
16
16
|
:href="
|
|
17
|
-
|
|
17
|
+
withBase(
|
|
18
|
+
theme.nav.find((i) => i.link && i.link.includes('contact'))
|
|
19
|
+
.link,
|
|
20
|
+
)
|
|
18
21
|
"
|
|
19
22
|
class="button small border no-margin"
|
|
20
23
|
style="color: var(--on-surface)"
|
|
@@ -89,7 +92,7 @@
|
|
|
89
92
|
>
|
|
90
93
|
</p>
|
|
91
94
|
<p v-if="theme.theme.brandConfig?.analytics">
|
|
92
|
-
<a href="/cookie-settings" class="link">{{
|
|
95
|
+
<a :href="withBase('/cookie-settings')" class="link">{{
|
|
93
96
|
t("Cookie settings", theme.i18n)
|
|
94
97
|
}}</a>
|
|
95
98
|
</p>
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
|
|
80
80
|
<script setup>
|
|
81
81
|
import { computed, ref } from "vue";
|
|
82
|
+
import { withBase } from "vitepress";
|
|
82
83
|
|
|
83
84
|
const props = defineProps({
|
|
84
85
|
images: {
|
|
@@ -90,14 +91,22 @@ const props = defineProps({
|
|
|
90
91
|
const container = ref(null);
|
|
91
92
|
const activeIndex = ref(0);
|
|
92
93
|
|
|
94
|
+
function resolveUrl(url) {
|
|
95
|
+
if (!url) return "";
|
|
96
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
97
|
+
const base = withBase("/");
|
|
98
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
99
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
100
|
+
}
|
|
101
|
+
|
|
93
102
|
const normalizedImages = computed(() => {
|
|
94
103
|
if (!props.images) return [];
|
|
95
104
|
return props.images.map((img) => {
|
|
96
105
|
if (typeof img === "string") {
|
|
97
|
-
return { src: img, alt: "", caption: "" };
|
|
106
|
+
return { src: resolveUrl(img), alt: "", caption: "" };
|
|
98
107
|
}
|
|
99
108
|
return {
|
|
100
|
-
src: img.src
|
|
109
|
+
src: resolveUrl(img.src),
|
|
101
110
|
alt: img.alt || "",
|
|
102
111
|
caption: img.caption || "",
|
|
103
112
|
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
>
|
|
10
10
|
<!-- Background Image (Original/Right) -->
|
|
11
11
|
<img
|
|
12
|
-
:src="
|
|
12
|
+
:src="resolvedCompare"
|
|
13
13
|
:alt="alt || 'Image comparison background'"
|
|
14
14
|
class="compare-img compare-bg"
|
|
15
15
|
/>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}"
|
|
23
23
|
>
|
|
24
24
|
<img
|
|
25
|
-
:src="
|
|
25
|
+
:src="resolvedSrc"
|
|
26
26
|
:alt="alt || 'Image comparison foreground'"
|
|
27
27
|
class="compare-img compare-fg"
|
|
28
28
|
/>
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<!-- Standard Image Mode -->
|
|
62
62
|
<img
|
|
63
63
|
v-else
|
|
64
|
-
:src="
|
|
64
|
+
:src="resolvedSrc"
|
|
65
65
|
:alt="alt || 'Post image'"
|
|
66
66
|
:style="{ maxWidth: width || '100%' }"
|
|
67
67
|
class="standard-img"
|
|
@@ -78,7 +78,8 @@
|
|
|
78
78
|
</template>
|
|
79
79
|
|
|
80
80
|
<script setup>
|
|
81
|
-
import { ref } from "vue";
|
|
81
|
+
import { ref, computed } from "vue";
|
|
82
|
+
import { withBase } from "vitepress";
|
|
82
83
|
|
|
83
84
|
const props = defineProps({
|
|
84
85
|
src: {
|
|
@@ -103,6 +104,17 @@ const props = defineProps({
|
|
|
103
104
|
},
|
|
104
105
|
});
|
|
105
106
|
|
|
107
|
+
function resolveUrl(url) {
|
|
108
|
+
if (!url) return "";
|
|
109
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
110
|
+
const base = withBase("/");
|
|
111
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
112
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const resolvedSrc = computed(() => resolveUrl(props.src));
|
|
116
|
+
const resolvedCompare = computed(() => resolveUrl(props.compare));
|
|
117
|
+
|
|
106
118
|
const sliderValue = ref(50);
|
|
107
119
|
</script>
|
|
108
120
|
|
|
@@ -170,6 +170,7 @@
|
|
|
170
170
|
|
|
171
171
|
<script setup>
|
|
172
172
|
import { computed, ref, onMounted, onUnmounted } from "vue";
|
|
173
|
+
import { withBase } from "vitepress";
|
|
173
174
|
|
|
174
175
|
const props = defineProps({
|
|
175
176
|
images: {
|
|
@@ -192,6 +193,14 @@ const props = defineProps({
|
|
|
192
193
|
|
|
193
194
|
const lightboxIndex = ref(null);
|
|
194
195
|
|
|
196
|
+
function resolveUrl(url) {
|
|
197
|
+
if (!url) return "";
|
|
198
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
199
|
+
const base = withBase("/");
|
|
200
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
201
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
202
|
+
}
|
|
203
|
+
|
|
195
204
|
const layoutMode = computed(() => {
|
|
196
205
|
// If explicitly 'grid', or if we detect link in image objects, default to grid
|
|
197
206
|
if (props.layout === "grid") return "grid";
|
|
@@ -211,13 +220,23 @@ const normalizedImages = computed(() => {
|
|
|
211
220
|
if (Array.isArray(props.images)) {
|
|
212
221
|
return props.images.map((img) => {
|
|
213
222
|
if (typeof img === "string") {
|
|
214
|
-
return {
|
|
223
|
+
return {
|
|
224
|
+
src: resolveUrl(img),
|
|
225
|
+
alt: "",
|
|
226
|
+
caption: "",
|
|
227
|
+
link: "",
|
|
228
|
+
width: "",
|
|
229
|
+
};
|
|
215
230
|
}
|
|
216
231
|
return {
|
|
217
|
-
src: img.src
|
|
232
|
+
src: resolveUrl(img.src),
|
|
218
233
|
alt: img.alt || "",
|
|
219
234
|
caption: img.caption || "",
|
|
220
|
-
link: img.link
|
|
235
|
+
link: img.link
|
|
236
|
+
? img.link.startsWith("/")
|
|
237
|
+
? withBase(img.link)
|
|
238
|
+
: img.link
|
|
239
|
+
: "",
|
|
221
240
|
width: img.width || "",
|
|
222
241
|
};
|
|
223
242
|
});
|
|
@@ -227,7 +246,13 @@ const normalizedImages = computed(() => {
|
|
|
227
246
|
.split(",")
|
|
228
247
|
.map((img) => img.trim())
|
|
229
248
|
.filter(Boolean)
|
|
230
|
-
.map((src) => ({
|
|
249
|
+
.map((src) => ({
|
|
250
|
+
src: resolveUrl(src),
|
|
251
|
+
alt: "",
|
|
252
|
+
caption: "",
|
|
253
|
+
link: "",
|
|
254
|
+
width: "",
|
|
255
|
+
}));
|
|
231
256
|
}
|
|
232
257
|
return [];
|
|
233
258
|
});
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
<section class="logo-section">
|
|
3
3
|
<div ref="logo-row" class="logo-row">
|
|
4
4
|
<component
|
|
5
|
-
v-for="logo in
|
|
5
|
+
v-for="logo in normalizedLogos"
|
|
6
|
+
:key="logo.alt"
|
|
6
7
|
:is="logo.link ? 'a' : 'div'"
|
|
7
8
|
:href="logo.link || undefined"
|
|
8
9
|
:target="logo.target || undefined"
|
|
@@ -15,11 +16,29 @@
|
|
|
15
16
|
</template>
|
|
16
17
|
|
|
17
18
|
<script setup>
|
|
18
|
-
import { useTemplateRef, onMounted } from "vue";
|
|
19
|
+
import { useTemplateRef, onMounted, computed } from "vue";
|
|
20
|
+
import { withBase } from "vitepress";
|
|
19
21
|
|
|
20
22
|
const props = defineProps(["baseHeight", "logos", "strength"]);
|
|
21
23
|
const logoRow = useTemplateRef("logo-row");
|
|
22
24
|
|
|
25
|
+
function resolveUrl(url) {
|
|
26
|
+
if (!url) return "";
|
|
27
|
+
if (url.startsWith("http") || url.startsWith("//")) return url;
|
|
28
|
+
const base = withBase("/");
|
|
29
|
+
if (base && base !== "/" && url.startsWith(base)) return url;
|
|
30
|
+
return url.startsWith("/") ? withBase(url) : withBase("/" + url);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const normalizedLogos = computed(() => {
|
|
34
|
+
if (!props.logos) return [];
|
|
35
|
+
return props.logos.map((logo) => ({
|
|
36
|
+
...logo,
|
|
37
|
+
image: resolveUrl(logo.image),
|
|
38
|
+
link: logo.link ? resolveUrl(logo.link) : undefined,
|
|
39
|
+
}));
|
|
40
|
+
});
|
|
41
|
+
|
|
23
42
|
onMounted(() => {
|
|
24
43
|
// Function to update CSS variables and display values
|
|
25
44
|
function updateVariables() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="top-nav at-top row center-align">
|
|
3
3
|
<nav class="large-padding no-margin center-align nav-mobile">
|
|
4
|
-
<a href="/">
|
|
4
|
+
<a :href="withBase('/')">
|
|
5
5
|
<img
|
|
6
6
|
:src="withBase(theme.logo.light)"
|
|
7
7
|
:alt="`${site.title} logo`"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
</button>
|
|
21
21
|
<dialog id="mobile-menu" class="max">
|
|
22
22
|
<nav>
|
|
23
|
-
<a data-ui="#mobile-menu" href="/">
|
|
23
|
+
<a data-ui="#mobile-menu" :href="withBase('/')">
|
|
24
24
|
<img
|
|
25
25
|
:src="withBase(theme.logo.light)"
|
|
26
26
|
:alt="`${site.title} logo`"
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
</dialog>
|
|
120
120
|
</nav>
|
|
121
121
|
<nav class="large-padding no-margin center-align row holder nav-desktop">
|
|
122
|
-
<a href="/">
|
|
122
|
+
<a :href="withBase('/')">
|
|
123
123
|
<img
|
|
124
124
|
:src="withBase(theme.logo.light)"
|
|
125
125
|
:alt="`${site.title} logo`"
|
|
@@ -239,7 +239,7 @@ const langs = computed(() => {
|
|
|
239
239
|
}
|
|
240
240
|
return Object.entries(locales).map(([id, locale]) => {
|
|
241
241
|
const currentPath = route.path;
|
|
242
|
-
const base = withBase("");
|
|
242
|
+
const base = withBase("/");
|
|
243
243
|
let pathRelativeToBase = currentPath;
|
|
244
244
|
if (base !== "/" && currentPath.startsWith(base)) {
|
|
245
245
|
pathRelativeToBase = currentPath.substring(base.length);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<a
|
|
14
14
|
v-for="tag in allTagsList"
|
|
15
15
|
:key="tag.name"
|
|
16
|
-
:href="`/tag/${tag.name}/`"
|
|
16
|
+
:href="withBase(`/tag/${tag.name}/`)"
|
|
17
17
|
:style="getTagStyle(tag.name)"
|
|
18
18
|
class="tag-item"
|
|
19
19
|
>
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
</ul>
|
|
59
59
|
<p class="legend-note">
|
|
60
60
|
For more detailed information on our technology solutions, visit the
|
|
61
|
-
<a href="/
|
|
61
|
+
<a :href="withBase('/solutions')" class="legend-link"
|
|
62
62
|
>Solutions & Services</a
|
|
63
63
|
>
|
|
64
64
|
page.
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
|
|
70
70
|
<script setup>
|
|
71
71
|
import { computed } from "vue";
|
|
72
|
+
import { withBase } from "vitepress";
|
|
72
73
|
import { data as posts } from "../posts.data.js";
|
|
73
74
|
import { getTagColor, getTagStyle } from "../tagUtils.js";
|
|
74
75
|
|