summaryrefslogtreecommitdiff
path: root/website/.vitepress
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-04-05 02:08:18 +0900
committerSho Sakuma <me@m1sk9.dev>2026-04-05 02:08:18 +0900
commit50fac99f6b9627186f97b036eef9f0cbe0aa37cb (patch)
tree15c59026581e59796a644017548f173e23d8434a /website/.vitepress
parent579ca7f5bf01187e1f758cadfb4eda652fd5a4c3 (diff)
downloadLunaticChat-50fac99f6b9627186f97b036eef9f0cbe0aa37cb.tar.gz
LunaticChat-50fac99f6b9627186f97b036eef9f0cbe0aa37cb.tar.bz2
LunaticChat-50fac99f6b9627186f97b036eef9f0cbe0aa37cb.zip
fix(website): fetch download data dynamically on client side
Replace the VitePress build-time data loader with a client-side composable so the download page always shows the latest GitHub release without requiring a site redeploy. Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'website/.vitepress')
-rw-r--r--website/.vitepress/theme/components/DownloadCard.vue32
-rw-r--r--website/.vitepress/theme/components/download.data.ts105
-rw-r--r--website/.vitepress/theme/components/useDownloadData.ts104
3 files changed, 134 insertions, 107 deletions
diff --git a/website/.vitepress/theme/components/DownloadCard.vue b/website/.vitepress/theme/components/DownloadCard.vue
index 019a8f8..a61cff6 100644
--- a/website/.vitepress/theme/components/DownloadCard.vue
+++ b/website/.vitepress/theme/components/DownloadCard.vue
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useData } from 'vitepress';
-import { data } from './download.data';
+import { useDownloadData } from './useDownloadData';
const { lang } = useData();
const isEn = computed(() => lang.value === 'en-US');
+const { data, loading, error } = useDownloadData();
const t = computed(() =>
isEn.value
@@ -93,7 +94,15 @@ function formatDate(dateStr: string | null): string {
<p><a :href="isEn ? '/en/docs/features/velocity#protocol-version' : '/docs/features/velocity#プロトコルバージョン'">{{ t.compatLink }}</a></p>
</div>
- <div class="download-grid">
+ <div v-if="loading" class="download-loading">
+ <p>{{ isEn ? 'Loading releases...' : 'リリース情報を取得中...' }}</p>
+ </div>
+
+ <div v-else-if="error" class="download-error">
+ <p>{{ isEn ? 'Failed to load release information. Please check ' : 'リリース情報の取得に失敗しました.' }}<a href="https://github.com/m1sk9/LunaticChat/releases" target="_blank" rel="noopener">GitHub Releases</a>{{ isEn ? ' directly.' : ' を直接ご確認ください.' }}</p>
+ </div>
+
+ <div v-else class="download-grid">
<!-- Paper / Folia -->
<div class="download-card">
<div class="download-card-header">
@@ -377,4 +386,23 @@ function formatDate(dateStr: string | null): string {
.download-ci {
margin: 16px 0;
}
+
+.download-loading {
+ text-align: center;
+ padding: 48px 0;
+ color: var(--vp-c-text-2);
+}
+
+.download-error {
+ border: 1px solid var(--vp-c-danger-soft);
+ background: var(--vp-c-danger-soft);
+ border-radius: 8px;
+ padding: 16px 20px;
+ margin: 24px 0;
+}
+
+.download-error a {
+ color: var(--vp-c-brand-1);
+ text-decoration: underline;
+}
</style>
diff --git a/website/.vitepress/theme/components/download.data.ts b/website/.vitepress/theme/components/download.data.ts
deleted file mode 100644
index 11e0489..0000000
--- a/website/.vitepress/theme/components/download.data.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-const REPO = 'm1sk9/LunaticChat';
-
-interface ReleaseAsset {
- name: string;
- size: number;
- browser_download_url: string;
-}
-
-interface GitHubRelease {
- tag_name: string;
- published_at: string;
- html_url: string;
- assets: ReleaseAsset[];
-}
-
-interface PlatformRelease {
- version: string;
- publishedAt: string;
- releaseUrl: string;
- downloadUrl: string | null;
- fileName: string | null;
- fileSize: number | null;
-}
-
-interface CIBuild {
- url: string;
-}
-
-export interface DownloadData {
- paper: PlatformRelease | null;
- velocity: PlatformRelease | null;
- ci: CIBuild;
-}
-
-async function fetchLatestRelease(
- tagPrefix: string,
-): Promise<GitHubRelease | null> {
- const res = await fetch(
- `https://api.github.com/repos/${REPO}/releases?per_page=20`,
- );
- if (!res.ok) return null;
-
- const releases: GitHubRelease[] = await res.json();
- return (
- releases.find(
- (r) =>
- r.tag_name.startsWith(tagPrefix) || r.tag_name.startsWith('v'),
- ) ?? null
- );
-}
-
-function parsePlatformRelease(
- release: GitHubRelease | null,
- jarPattern: RegExp,
-): PlatformRelease | null {
- if (!release) return null;
-
- const asset = release.assets.find((a) => jarPattern.test(a.name));
- const version = release.tag_name.replace(/^(paper\/|velocity\/)?v/, '');
-
- return {
- version,
- publishedAt: release.published_at,
- releaseUrl: release.html_url,
- downloadUrl: asset?.browser_download_url ?? null,
- fileName: asset?.name ?? null,
- fileSize: asset?.size ?? null,
- };
-}
-
-export default {
- async load(): Promise<DownloadData> {
- // Fetch all recent releases and find the latest for each platform
- const res = await fetch(
- `https://api.github.com/repos/${REPO}/releases?per_page=30`,
- );
- const releases: GitHubRelease[] = res.ok ? await res.json() : [];
-
- // Find latest Paper release (paper/v* or v*)
- const paperRelease =
- releases.find((r) => r.tag_name.startsWith('paper/v')) ??
- releases.find((r) => /^v\d/.test(r.tag_name)) ??
- null;
-
- // Find latest Velocity release (velocity/v* or v*)
- const velocityRelease =
- releases.find((r) => r.tag_name.startsWith('velocity/v')) ??
- releases.find((r) => /^v\d/.test(r.tag_name)) ??
- null;
-
- return {
- paper: parsePlatformRelease(
- paperRelease,
- /^LunaticChat-[\d.]+\.jar$/,
- ),
- velocity: parsePlatformRelease(
- velocityRelease,
- /^LunaticChat-[\d.]+-velocity\.jar$/,
- ),
- ci: {
- url: `https://github.com/${REPO}/actions/workflows/ci.yaml?query=branch%3Amain`,
- },
- };
- },
-};
diff --git a/website/.vitepress/theme/components/useDownloadData.ts b/website/.vitepress/theme/components/useDownloadData.ts
new file mode 100644
index 0000000..0c9e7e0
--- /dev/null
+++ b/website/.vitepress/theme/components/useDownloadData.ts
@@ -0,0 +1,104 @@
+import { ref, onMounted } from 'vue';
+
+const REPO = 'm1sk9/LunaticChat';
+
+interface ReleaseAsset {
+ name: string;
+ size: number;
+ browser_download_url: string;
+}
+
+interface GitHubRelease {
+ tag_name: string;
+ published_at: string;
+ html_url: string;
+ assets: ReleaseAsset[];
+}
+
+export interface PlatformRelease {
+ version: string;
+ publishedAt: string;
+ releaseUrl: string;
+ downloadUrl: string | null;
+ fileName: string | null;
+ fileSize: number | null;
+}
+
+export interface DownloadData {
+ paper: PlatformRelease | null;
+ velocity: PlatformRelease | null;
+ ci: { url: string };
+}
+
+function parsePlatformRelease(
+ release: GitHubRelease | null,
+ jarPattern: RegExp,
+): PlatformRelease | null {
+ if (!release) return null;
+
+ const asset = release.assets.find((a) => jarPattern.test(a.name));
+ const version = release.tag_name.replace(/^(paper\/|velocity\/)?v/, '');
+
+ return {
+ version,
+ publishedAt: release.published_at,
+ releaseUrl: release.html_url,
+ downloadUrl: asset?.browser_download_url ?? null,
+ fileName: asset?.name ?? null,
+ fileSize: asset?.size ?? null,
+ };
+}
+
+export function useDownloadData() {
+ const data = ref<DownloadData>({
+ paper: null,
+ velocity: null,
+ ci: {
+ url: `https://github.com/${REPO}/actions/workflows/ci.yaml?query=branch%3Amain`,
+ },
+ });
+ const loading = ref(true);
+ const error = ref(false);
+
+ onMounted(async () => {
+ try {
+ const res = await fetch(
+ `https://api.github.com/repos/${REPO}/releases?per_page=30`,
+ );
+ if (!res.ok) {
+ error.value = true;
+ return;
+ }
+
+ const releases: GitHubRelease[] = await res.json();
+
+ const paperRelease =
+ releases.find((r) => r.tag_name.startsWith('paper/v')) ??
+ releases.find((r) => /^v\d/.test(r.tag_name)) ??
+ null;
+
+ const velocityRelease =
+ releases.find((r) => r.tag_name.startsWith('velocity/v')) ??
+ releases.find((r) => /^v\d/.test(r.tag_name)) ??
+ null;
+
+ data.value = {
+ paper: parsePlatformRelease(
+ paperRelease,
+ /^LunaticChat-[\d.]+\.jar$/,
+ ),
+ velocity: parsePlatformRelease(
+ velocityRelease,
+ /^LunaticChat-[\d.]+-velocity\.jar$/,
+ ),
+ ci: data.value.ci,
+ };
+ } catch {
+ error.value = true;
+ } finally {
+ loading.value = false;
+ }
+ });
+
+ return { data, loading, error };
+}