diff --git a/astro.config.mjs b/astro.config.mjs
index 5042d4a..6bcb407 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -13,6 +13,7 @@ export default defineConfig({
"/feed": "/rss.xml",
},
output: "server",
+ prefetch: true,
vite: {
plugins: [tailwindcss()],
},
diff --git a/src/components/NavigationBar.tsx b/src/components/NavigationBar.tsx
index e2e8206..213187e 100644
--- a/src/components/NavigationBar.tsx
+++ b/src/components/NavigationBar.tsx
@@ -108,6 +108,7 @@ export default function NavigationBar({ currentPath }: NavigationBarProps) {
class={`tooltip tooltip-top min-h-[44px] min-w-[44px] inline-flex items-center justify-center ${isActive ? "menu-active" : ""}`}
aria-label={item.tooltip}
data-tip={item.tooltip}
+ data-astro-prefetch="hover"
>
{item.name}
diff --git a/src/components/ProjectsIsland.astro b/src/components/ProjectsIsland.astro
new file mode 100644
index 0000000..cacdc7c
--- /dev/null
+++ b/src/components/ProjectsIsland.astro
@@ -0,0 +1,59 @@
+---
+import ProjectCard from "./ProjectCard.astro";
+import { config } from "../config";
+import { fetchGiteaInfoFromUrl } from "../utils/gitea";
+import type { Project } from "../types";
+
+Astro.response.headers.set(
+ "Cache-Control",
+ "public, max-age=300, s-maxage=300, stale-while-revalidate=60",
+);
+
+function isGiteaDomain(url: string): boolean {
+ if (!config.siteConfig.giteaDomains) return true;
+ try {
+ const urlObj = new URL(url);
+ return config.siteConfig.giteaDomains.some(
+ (domain) => urlObj.origin === new URL(domain).origin,
+ );
+ } catch {
+ return false;
+ }
+}
+
+const projectsWithGiteaInfo = await Promise.all(
+ config.projects.map(async (project) => {
+ if (project.link && !project.giteaInfo && isGiteaDomain(project.link)) {
+ const giteaInfo = await fetchGiteaInfoFromUrl(project.link);
+ if (giteaInfo) {
+ return { ...project, giteaInfo } as Project;
+ }
+ }
+ return project;
+ }),
+);
+
+const sortedProjects = projectsWithGiteaInfo.sort((a, b) => {
+ const aTime = a.giteaInfo?.updatedAt
+ ? new Date(a.giteaInfo.updatedAt).getTime()
+ : 0;
+ const bTime = b.giteaInfo?.updatedAt
+ ? new Date(b.giteaInfo.updatedAt).getTime()
+ : 0;
+ return bTime - aTime;
+});
+---
+
+
+ {sortedProjects.map((project) =>
)}
+
+
+{
+ sortedProjects.length === 0 && (
+
+ No projects available yet. Check back soon!
+
+ )
+}
diff --git a/src/components/ProjectsLoader.astro b/src/components/ProjectsLoader.astro
new file mode 100644
index 0000000..fab146d
--- /dev/null
+++ b/src/components/ProjectsLoader.astro
@@ -0,0 +1,46 @@
+---
+const skeletonCount = 6;
+---
+
+
+ {
+ Array.from({ length: skeletonCount }).map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))
+ }
+
diff --git a/src/pages/projects.astro b/src/pages/projects.astro
index 35111da..c21cb0e 100644
--- a/src/pages/projects.astro
+++ b/src/pages/projects.astro
@@ -1,43 +1,9 @@
---
import Layout from "../layouts/Layout.astro";
-import ProjectCard from "../components/ProjectCard.astro";
-import { config } from "../config";
-import { fetchGiteaInfoFromUrl } from "../utils/gitea";
-import type { Project } from "../types";
+import ProjectsIsland from "../components/ProjectsIsland.astro";
+import ProjectsLoader from "../components/ProjectsLoader.astro";
-function isGiteaDomain(url: string): boolean {
- if (!config.siteConfig.giteaDomains) return true;
- try {
- const urlObj = new URL(url);
- return config.siteConfig.giteaDomains.some(
- (domain) => urlObj.origin === new URL(domain).origin,
- );
- } catch {
- return false;
- }
-}
-
-const projectsWithGiteaInfo = await Promise.all(
- config.projects.map(async (project) => {
- if (project.link && !project.giteaInfo && isGiteaDomain(project.link)) {
- const giteaInfo = await fetchGiteaInfoFromUrl(project.link);
- if (giteaInfo) {
- return { ...project, giteaInfo } as Project;
- }
- }
- return project;
- }),
-);
-
-const sortedProjects = projectsWithGiteaInfo.sort((a, b) => {
- const aTime = a.giteaInfo?.updatedAt
- ? new Date(a.giteaInfo.updatedAt).getTime()
- : 0;
- const bTime = b.giteaInfo?.updatedAt
- ? new Date(b.giteaInfo.updatedAt).getTime()
- : 0;
- return bTime - aTime;
-});
+export const prerender = false;
---
@@ -47,18 +13,9 @@ const sortedProjects = projectsWithGiteaInfo.sort((a, b) => {
>
Projects
-
- {sortedProjects.map((project) =>
)}
-
- {
- sortedProjects.length === 0 && (
-
- No projects available yet. Check back soon!
-
- )
- }
+
+
+