@eox/pages-theme-eox 1.9.0 → 2.0.0-alpha.2
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/README.md +13 -0
- package/cypress/support/mocks/vitepress.js +8 -1
- package/cypress.config.js +5 -0
- package/package.json +1 -1
- package/src/Layout.vue +58 -1
- package/src/authors.data.js +14 -0
- package/src/components/BlogList.vue +636 -0
- package/src/components/BlogPost.vue +450 -0
- package/src/components/FeatureCard/Index.vue +370 -26
- package/src/components/HeroSection.vue +1 -1
- package/src/components/SectionHeader.cy.js +54 -0
- package/src/components/SectionHeader.vue +115 -0
- package/src/components/TagList.vue +293 -0
- package/src/components/TeamPage.vue +333 -0
- package/src/components/TestimonialsSection.cy.js +79 -0
- package/src/components/TestimonialsSection.vue +265 -0
- package/src/index.js +12 -0
- package/src/pages/author/[author].md +4 -0
- package/src/pages/author/[author].paths.js +60 -0
- package/src/pages/blog.md +4 -0
- package/src/pages/tag/[tag].md +4 -0
- package/src/pages/tag/[tag].paths.js +49 -0
- package/src/pages/tag.md +4 -0
- package/src/pages/team.md +4 -0
- package/src/posts.data.js +96 -0
- package/src/style.css +160 -0
- package/src/tagUtils.js +148 -0
- package/src/team.data.js +1 -1
- package/src/vitepressConfig.mjs +100 -0
package/src/tagUtils.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
export const tagThemes = [
|
|
2
|
+
// Managed cloud workspace (Category 0)
|
|
3
|
+
[
|
|
4
|
+
"eoxhub",
|
|
5
|
+
"eurodatacube",
|
|
6
|
+
"cloud-processing",
|
|
7
|
+
"mundi",
|
|
8
|
+
"jupyter",
|
|
9
|
+
"vre",
|
|
10
|
+
"processing",
|
|
11
|
+
],
|
|
12
|
+
// Data management (Category 1)
|
|
13
|
+
[
|
|
14
|
+
"data-access",
|
|
15
|
+
"catalog",
|
|
16
|
+
"eo-data",
|
|
17
|
+
"open-search",
|
|
18
|
+
"ard",
|
|
19
|
+
"eoxserver",
|
|
20
|
+
"django",
|
|
21
|
+
"sxcat",
|
|
22
|
+
"data-cubes",
|
|
23
|
+
"opensearch",
|
|
24
|
+
],
|
|
25
|
+
// Data visualization (Category 2)
|
|
26
|
+
[
|
|
27
|
+
"3d",
|
|
28
|
+
"animation",
|
|
29
|
+
"cesium",
|
|
30
|
+
"globe",
|
|
31
|
+
"ndvi",
|
|
32
|
+
"plotty",
|
|
33
|
+
"graphly",
|
|
34
|
+
"geotiff-js",
|
|
35
|
+
"client",
|
|
36
|
+
"visualization",
|
|
37
|
+
"cog",
|
|
38
|
+
"geotiff",
|
|
39
|
+
"openlayers",
|
|
40
|
+
"vue-js",
|
|
41
|
+
"eoxc",
|
|
42
|
+
"dashboard",
|
|
43
|
+
"javascript",
|
|
44
|
+
"leaflet",
|
|
45
|
+
"d3",
|
|
46
|
+
"webgl",
|
|
47
|
+
"video",
|
|
48
|
+
"data visualization",
|
|
49
|
+
],
|
|
50
|
+
// Data products (Category 3)
|
|
51
|
+
[
|
|
52
|
+
"eoxcloudless",
|
|
53
|
+
"eoxmaps",
|
|
54
|
+
"s2cloudless",
|
|
55
|
+
"maps",
|
|
56
|
+
"sentinel-2",
|
|
57
|
+
"labels",
|
|
58
|
+
"cloudless-mosaic",
|
|
59
|
+
"voronoi-diagram",
|
|
60
|
+
"openstreetmap",
|
|
61
|
+
"mapchete",
|
|
62
|
+
],
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
export const tagColors = [
|
|
66
|
+
"rgb(255, 94, 94)", // Managed cloud workspace
|
|
67
|
+
"rgb(0, 177, 176)", // Data management
|
|
68
|
+
"rgb(219, 173, 66)", // Data visualization
|
|
69
|
+
"rgb(218, 71, 228)", // Data products
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
export const tagCategories = [
|
|
73
|
+
"Managed cloud workspace",
|
|
74
|
+
"Data management",
|
|
75
|
+
"Data visualization",
|
|
76
|
+
"Data products",
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get color for a given tag
|
|
81
|
+
* @param {string} tag
|
|
82
|
+
* @returns {string} color hex/rgb
|
|
83
|
+
*/
|
|
84
|
+
export function getTagColor(tag) {
|
|
85
|
+
if (!tag) return "#e0e0e0";
|
|
86
|
+
const cleanTag = tag.toLowerCase().trim();
|
|
87
|
+
for (let i = 0; i < tagThemes.length; i++) {
|
|
88
|
+
if (tagThemes[i].includes(cleanTag)) {
|
|
89
|
+
return tagColors[i];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return "#e0e0e0";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get style object for a given tag
|
|
97
|
+
* @param {string} tag
|
|
98
|
+
* @returns {object} style object
|
|
99
|
+
*/
|
|
100
|
+
export function getTagStyle(tag) {
|
|
101
|
+
const color = getTagColor(tag);
|
|
102
|
+
if (color.startsWith("rgb(255, 94, 94)")) {
|
|
103
|
+
return {
|
|
104
|
+
backgroundColor: "rgba(255, 94, 94, 0.15)",
|
|
105
|
+
border: "1px solid rgb(255, 94, 94)",
|
|
106
|
+
color: "#d32f2f"
|
|
107
|
+
};
|
|
108
|
+
} else if (color.startsWith("rgb(0, 177, 176)")) {
|
|
109
|
+
return {
|
|
110
|
+
backgroundColor: "rgba(0, 177, 176, 0.15)",
|
|
111
|
+
border: "1px solid rgb(0, 177, 176)",
|
|
112
|
+
color: "#00796b"
|
|
113
|
+
};
|
|
114
|
+
} else if (color.startsWith("rgb(219, 173, 66)")) {
|
|
115
|
+
return {
|
|
116
|
+
backgroundColor: "rgba(219, 173, 66, 0.15)",
|
|
117
|
+
border: "1px solid rgb(219, 173, 66)",
|
|
118
|
+
color: "#f57c00"
|
|
119
|
+
};
|
|
120
|
+
} else if (color.startsWith("rgb(218, 71, 228)")) {
|
|
121
|
+
return {
|
|
122
|
+
backgroundColor: "rgba(218, 71, 228, 0.15)",
|
|
123
|
+
border: "1px solid rgb(218, 71, 228)",
|
|
124
|
+
color: "#c2185b"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
backgroundColor: "rgba(0, 0, 0, 0.05)",
|
|
129
|
+
border: "1px solid rgba(0, 0, 0, 0.1)",
|
|
130
|
+
color: "#475569"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get category name for a given tag
|
|
136
|
+
* @param {string} tag
|
|
137
|
+
* @returns {string} category name
|
|
138
|
+
*/
|
|
139
|
+
export function getTagCategory(tag) {
|
|
140
|
+
if (!tag) return "Other";
|
|
141
|
+
const cleanTag = tag.toLowerCase().trim();
|
|
142
|
+
for (let i = 0; i < tagThemes.length; i++) {
|
|
143
|
+
if (tagThemes[i].includes(cleanTag)) {
|
|
144
|
+
return tagCategories[i];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return "Other";
|
|
148
|
+
}
|
package/src/team.data.js
CHANGED
package/src/vitepressConfig.mjs
CHANGED
|
@@ -1,3 +1,90 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function copyThemePages() {
|
|
5
|
+
try {
|
|
6
|
+
const themeSrcDir = path.dirname(new URL(import.meta.url).pathname);
|
|
7
|
+
const pages = [
|
|
8
|
+
{ src: "blog.md", dest: "blog.md" },
|
|
9
|
+
{ src: "tag.md", dest: "tag.md" },
|
|
10
|
+
{ src: "tag/[tag].md", dest: "tag/[tag].md" },
|
|
11
|
+
{
|
|
12
|
+
...(() => {
|
|
13
|
+
// Let's resolve the actual pages dir inside the theme package
|
|
14
|
+
// This is robust on any npm installation structure
|
|
15
|
+
return {};
|
|
16
|
+
})(),
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// Better yet, just copy our built-in custom pages directly relative to this module
|
|
21
|
+
const filesToCopy = [
|
|
22
|
+
"blog.md",
|
|
23
|
+
"team.md",
|
|
24
|
+
"tag.md",
|
|
25
|
+
"tag/[tag].md",
|
|
26
|
+
"tag/[tag].paths.js",
|
|
27
|
+
"author/[author].md",
|
|
28
|
+
"author/[author].paths.js",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const targetDir = process.cwd();
|
|
32
|
+
const pagesSrcDir = path.join(themeSrcDir, "pages");
|
|
33
|
+
|
|
34
|
+
for (const file of filesToCopy) {
|
|
35
|
+
const src = path.join(pagesSrcDir, file);
|
|
36
|
+
const dest = path.join(targetDir, file);
|
|
37
|
+
if (fs.existsSync(src)) {
|
|
38
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
39
|
+
|
|
40
|
+
let shouldCopy = !fs.existsSync(dest);
|
|
41
|
+
if (!shouldCopy) {
|
|
42
|
+
const srcContent = fs.readFileSync(src, "utf8");
|
|
43
|
+
const destContent = fs.readFileSync(dest, "utf8");
|
|
44
|
+
if (srcContent !== destContent) {
|
|
45
|
+
shouldCopy = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (shouldCopy) {
|
|
49
|
+
fs.writeFileSync(dest, fs.readFileSync(src));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch (err) {
|
|
54
|
+
console.error("Failed to copy theme pages:", err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getBlogRewrites() {
|
|
59
|
+
const rewrites = {};
|
|
60
|
+
const postsDir = path.resolve(process.cwd(), "_posts");
|
|
61
|
+
if (!fs.existsSync(postsDir)) return rewrites;
|
|
62
|
+
|
|
63
|
+
const files = fs.readdirSync(postsDir).filter((f) => f.endsWith(".md"));
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
const filePath = path.join(postsDir, file);
|
|
66
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
67
|
+
const dateMatch = content.match(/date:\s*["']?(\d{4})-(\d{2})-\d{2}["']?/);
|
|
68
|
+
if (dateMatch) {
|
|
69
|
+
const [_, year, month] = dateMatch;
|
|
70
|
+
const baseName = file.replace(/\.md$/, "");
|
|
71
|
+
const slug = baseName
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
74
|
+
.replace(/^-+|-+$/g, "");
|
|
75
|
+
rewrites[`_posts/${file}`] = `${year}/${month}/${slug}.md`;
|
|
76
|
+
} else {
|
|
77
|
+
const baseName = file.replace(/\.md$/, "");
|
|
78
|
+
const slug = baseName
|
|
79
|
+
.toLowerCase()
|
|
80
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
81
|
+
.replace(/^-+|-+$/g, "");
|
|
82
|
+
rewrites[`_posts/${file}`] = `blog/${slug}.md`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return rewrites;
|
|
86
|
+
}
|
|
87
|
+
|
|
1
88
|
/**
|
|
2
89
|
* Base configuration for VitePress
|
|
3
90
|
* Documentation: see https://vitepress.dev/reference/site-config
|
|
@@ -5,9 +92,18 @@
|
|
|
5
92
|
* @returns
|
|
6
93
|
*/
|
|
7
94
|
export const generate = (brandConfig) => {
|
|
95
|
+
copyThemePages();
|
|
8
96
|
const config = {
|
|
9
97
|
appearance: false,
|
|
10
98
|
cleanUrls: true,
|
|
99
|
+
markdown: {
|
|
100
|
+
headers: {
|
|
101
|
+
level: [2, 3, 4, 5, 6]
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
rewrites: {
|
|
105
|
+
...getBlogRewrites(),
|
|
106
|
+
},
|
|
11
107
|
description: brandConfig.meta?.description,
|
|
12
108
|
head: [
|
|
13
109
|
["link", { rel: "icon", href: brandConfig.meta?.favicon }],
|
|
@@ -93,6 +189,7 @@ export const generate = (brandConfig) => {
|
|
|
93
189
|
],
|
|
94
190
|
srcExclude: ["README.md"],
|
|
95
191
|
themeConfig: {
|
|
192
|
+
outline: "deep",
|
|
96
193
|
siteTitle: false,
|
|
97
194
|
theme: {
|
|
98
195
|
primaryColor: brandConfig.theme?.primary_color,
|
|
@@ -123,6 +220,9 @@ export const generate = (brandConfig) => {
|
|
|
123
220
|
ssr: {
|
|
124
221
|
noExternal: ["@eox/pages-theme-eox"],
|
|
125
222
|
},
|
|
223
|
+
optimizeDeps: {
|
|
224
|
+
exclude: ["@eox/pages-theme-eox"],
|
|
225
|
+
},
|
|
126
226
|
},
|
|
127
227
|
vue: {
|
|
128
228
|
template: {
|