diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-04-05 00:02:58 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-04-05 00:02:58 +0900 |
| commit | 842bf9df75a5e23ab31bdfb5717b76449264e999 (patch) | |
| tree | 3acd09750c7fb40c32d8d0f8fc998a23be98aff1 | |
| parent | 278b6c9a65b93af61146a11575b32814624a3e40 (diff) | |
| download | LunaticChat-842bf9df75a5e23ab31bdfb5717b76449264e999.tar.gz LunaticChat-842bf9df75a5e23ab31bdfb5717b76449264e999.tar.bz2 LunaticChat-842bf9df75a5e23ab31bdfb5717b76449264e999.zip | |
docs: add download page and rename docs to website
- Add download page with GitHub API data loader for latest releases
- Add DownloadCard Vue component with JA/EN i18n support
- Include Modrinth and CI build links
- Rename docs/ directory to website/
Co-Authored-By: Claude <noreply@anthropic.com>
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | docs/.vitepress/theme/index.ts | 6 | ||||
| -rw-r--r-- | website/.gitignore (renamed from docs/.gitignore) | 0 | ||||
| -rw-r--r-- | website/.vitepress/config.mts (renamed from docs/.vitepress/config.mts) | 2 | ||||
| -rw-r--r-- | website/.vitepress/config/en.ts (renamed from docs/.vitepress/config/en.ts) | 1 | ||||
| -rw-r--r-- | website/.vitepress/config/ja.ts (renamed from docs/.vitepress/config/ja.ts) | 4 | ||||
| -rw-r--r-- | website/.vitepress/public/favicon.ico (renamed from docs/.vitepress/public/favicon.ico) | bin | 4286 -> 4286 bytes | |||
| -rw-r--r-- | website/.vitepress/theme/components/DownloadCard.vue | 338 | ||||
| -rw-r--r-- | website/.vitepress/theme/components/download.data.ts | 105 | ||||
| -rw-r--r-- | website/.vitepress/theme/custom.css (renamed from docs/.vitepress/theme/custom.css) | 0 | ||||
| -rw-r--r-- | website/.vitepress/theme/index.ts | 11 | ||||
| -rw-r--r-- | website/biome.jsonc (renamed from docs/biome.jsonc) | 0 | ||||
| -rw-r--r-- | website/bun.lock (renamed from docs/bun.lock) | 0 | ||||
| -rw-r--r-- | website/package.json (renamed from docs/package.json) | 0 | ||||
| -rw-r--r-- | website/src/assets/brand/LICENSE (renamed from docs/src/assets/brand/LICENSE) | 0 | ||||
| -rw-r--r-- | website/src/assets/brand/folia.svg (renamed from docs/src/assets/brand/folia.svg) | 0 | ||||
| -rw-r--r-- | website/src/assets/brand/paper.svg (renamed from docs/src/assets/brand/paper.svg) | 0 | ||||
| -rw-r--r-- | website/src/assets/brand/velocity.svg (renamed from docs/src/assets/brand/velocity.svg) | 0 | ||||
| -rw-r--r-- | website/src/assets/features/channel-chat.png (renamed from docs/src/assets/features/channel-chat.png) | bin | 1063857 -> 1063857 bytes | |||
| -rw-r--r-- | website/src/assets/features/cross-chat.png (renamed from docs/src/assets/features/cross-chat.png) | bin | 2146241 -> 2146241 bytes | |||
| -rw-r--r-- | website/src/assets/features/dm.png (renamed from docs/src/assets/features/dm.png) | bin | 964457 -> 964457 bytes | |||
| -rw-r--r-- | website/src/assets/features/romaji.png (renamed from docs/src/assets/features/romaji.png) | bin | 1023051 -> 1023051 bytes | |||
| -rw-r--r-- | website/src/assets/icon.png (renamed from docs/src/assets/icon.png) | bin | 122601 -> 122601 bytes | |||
| -rw-r--r-- | website/src/download.md | 6 | ||||
| -rw-r--r-- | website/src/index.md (renamed from docs/src/index.md) | 6 | ||||
| -rw-r--r-- | website/tsconfig.json (renamed from docs/tsconfig.json) | 0 | ||||
| -rw-r--r-- | website/wrangler.jsonc (renamed from docs/wrangler.jsonc) | 0 |
27 files changed, 468 insertions, 13 deletions
@@ -22,5 +22,5 @@ gradle-app.setting .classpath .idea/ -docs/.vitepress/cache/deps +website/.vitepress/cache/deps /docker/paper/plugins diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts deleted file mode 100644 index f789d9f..0000000 --- a/docs/.vitepress/theme/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import DefaultTheme from 'vitepress/theme'; -import './custom.css'; - -export default { - extends: DefaultTheme, -}; diff --git a/docs/.gitignore b/website/.gitignore index 0d28533..0d28533 100644 --- a/docs/.gitignore +++ b/website/.gitignore diff --git a/docs/.vitepress/config.mts b/website/.vitepress/config.mts index 096d3de..1cb5f83 100644 --- a/docs/.vitepress/config.mts +++ b/website/.vitepress/config.mts @@ -45,6 +45,6 @@ export default defineConfig({ { icon: 'github', link: 'https://github.com/m1sk9/LunaticChat' }, ], }, - title: 'LunaticChat Docs', + title: 'LunaticChat', titleTemplate: 'LunaticChat', }); diff --git a/docs/.vitepress/config/en.ts b/website/.vitepress/config/en.ts index 1865395..b42faeb 100644 --- a/docs/.vitepress/config/en.ts +++ b/website/.vitepress/config/en.ts @@ -8,6 +8,7 @@ export const en: DefaultTheme.Config = { nav: [ { link: '/en/player-guide/getting-started', text: 'Player Guide' }, { link: '/en/admin-guide/getting-started', text: 'Admin Guide' }, + { link: '/download', text: 'Download' }, ], sidebar: { '/en/admin-guide/': [ diff --git a/docs/.vitepress/config/ja.ts b/website/.vitepress/config/ja.ts index d4d5bdb..af2349b 100644 --- a/docs/.vitepress/config/ja.ts +++ b/website/.vitepress/config/ja.ts @@ -6,8 +6,8 @@ export const ja: DefaultTheme.Config = { text: 'GitHub で編集', }, nav: [ - { link: '/player-guide/getting-started', text: 'プレイヤーガイド' }, - { link: '/admin-guide/getting-started', text: '管理者ガイド' }, + { link: '/download', text: 'ダウンロード' }, + { link: '/docs', text: 'ドキュメント' } ], sidebar: { '/admin-guide/': [ diff --git a/docs/.vitepress/public/favicon.ico b/website/.vitepress/public/favicon.ico Binary files differindex 46c0cad..46c0cad 100644 --- a/docs/.vitepress/public/favicon.ico +++ b/website/.vitepress/public/favicon.ico diff --git a/website/.vitepress/theme/components/DownloadCard.vue b/website/.vitepress/theme/components/DownloadCard.vue new file mode 100644 index 0000000..d14b9d0 --- /dev/null +++ b/website/.vitepress/theme/components/DownloadCard.vue @@ -0,0 +1,338 @@ +<script setup lang="ts"> +import { computed } from 'vue'; +import { useData } from 'vitepress'; +import { data } from './download.data'; + +const { lang } = useData(); +const isEn = computed(() => lang.value === 'en-US'); + +const t = computed(() => + isEn.value + ? { + title: 'Download', + description: 'Download the latest releases of LunaticChat.', + requirements: 'Requirements', + javaReq: '25 or later', + paperReq: '26.1.x or later', + velocityReq: '3.4.x or later', + noRelease: 'No release', + releaseDate: 'Release date', + fileSize: 'File size', + download: 'Download', + releaseNotes: 'Release notes', + modrinthDesc: 'LunaticChat is also available on Modrinth.', + notYetReleased: 'Not yet released.', + devBuilds: 'Development Builds', + devBuildsDesc: + 'The latest build from the main branch is available from CI. Development builds are not guaranteed to be stable.', + viewCiBuilds: 'View latest CI builds', + } + : { + title: 'ダウンロード', + description: 'LunaticChat の最新リリースをダウンロードできます。', + requirements: '動作要件', + javaReq: '25 以降', + paperReq: '26.1.x 以降', + velocityReq: '3.4.x 以降', + noRelease: 'リリースなし', + releaseDate: 'リリース日', + fileSize: 'ファイルサイズ', + download: 'ダウンロード', + releaseNotes: 'リリースノート', + modrinthDesc: 'LunaticChat は Modrinth でも公開しています。', + notYetReleased: 'まだリリースされていません。', + devBuilds: '開発ビルド', + devBuildsDesc: + '最新の main ブランチのビルドは CI から取得できます。開発ビルドは安定性が保証されていません。', + viewCiBuilds: '最新の CI ビルドを確認', + }, +); + +function formatSize(bytes: number | null): string { + if (!bytes) return '-'; + const mb = bytes / (1024 * 1024); + return `${mb.toFixed(1)} MB`; +} + +function formatDate(dateStr: string | null): string { + if (!dateStr) return '-'; + const locale = isEn.value ? 'en-US' : 'ja-JP'; + return new Date(dateStr).toLocaleDateString(locale, { + year: 'numeric', + month: 'long', + day: 'numeric', + }); +} +</script> + +<template> + <div class="download-page"> + <h1>{{ t.title }}</h1> + <p>{{ t.description }}</p> + + <div class="download-requirements"> + <p class="download-requirements-title">{{ t.requirements }}</p> + <ul> + <li><strong>Java</strong>: {{ t.javaReq }}</li> + <li><strong>Paper / Folia</strong>: {{ t.paperReq }}</li> + <li><strong>Velocity</strong>: {{ t.velocityReq }}</li> + </ul> + </div> + + <div class="download-grid"> + <!-- Paper / Folia --> + <div class="download-card"> + <div class="download-card-header"> + <img src="/assets/brand/paper.svg" alt="Paper" class="download-icon" /> + <div> + <h2>Paper / Folia</h2> + <p class="download-version" v-if="data.paper">v{{ data.paper.version }}</p> + <p class="download-version" v-else>{{ t.noRelease }}</p> + </div> + </div> + <div class="download-card-body" v-if="data.paper"> + <dl class="download-meta"> + <div> + <dt>{{ t.releaseDate }}</dt> + <dd>{{ formatDate(data.paper.publishedAt) }}</dd> + </div> + <div> + <dt>{{ t.fileSize }}</dt> + <dd>{{ formatSize(data.paper.fileSize) }}</dd> + </div> + <div> + <dd><code>{{ data.paper.fileName ?? '-' }}</code></dd> + </div> + </dl> + <div class="download-actions"> + <a v-if="data.paper.downloadUrl" :href="data.paper.downloadUrl" class="download-btn primary">{{ t.download }}</a> + <a :href="data.paper.releaseUrl" class="download-btn" target="_blank" rel="noopener">{{ t.releaseNotes }}</a> + </div> + </div> + <div class="download-card-body" v-else> + <p class="download-empty">{{ t.notYetReleased }}</p> + </div> + </div> + + <!-- Velocity --> + <div class="download-card"> + <div class="download-card-header"> + <img src="/assets/brand/velocity.svg" alt="Velocity" class="download-icon" /> + <div> + <h2>Velocity</h2> + <p class="download-version" v-if="data.velocity">v{{ data.velocity.version }}</p> + <p class="download-version" v-else>{{ t.noRelease }}</p> + </div> + </div> + <div class="download-card-body" v-if="data.velocity"> + <dl class="download-meta"> + <div> + <dt>{{ t.releaseDate }}</dt> + <dd>{{ formatDate(data.velocity.publishedAt) }}</dd> + </div> + <div> + <dt>{{ t.fileSize }}</dt> + <dd>{{ formatSize(data.velocity.fileSize) }}</dd> + </div> + <div> + <dd><code>{{ data.velocity.fileName ?? '-' }}</code></dd> + </div> + </dl> + <div class="download-actions"> + <a v-if="data.velocity.downloadUrl" :href="data.velocity.downloadUrl" class="download-btn primary">{{ t.download }}</a> + <a :href="data.velocity.releaseUrl" class="download-btn" target="_blank" rel="noopener">{{ t.releaseNotes }}</a> + </div> + </div> + <div class="download-card-body" v-else> + <p class="download-empty">{{ t.notYetReleased }}</p> + </div> + </div> + </div> + + <h2>Modrinth</h2> + <p>{{ t.modrinthDesc }}</p> + <div class="download-ci"> + <a href="https://modrinth.com/plugin/lunaticchat" class="download-btn" target="_blank" rel="noopener">Modrinth</a> + </div> + + <h2>{{ t.devBuilds }}</h2> + <p>{{ t.devBuildsDesc }}</p> + <div class="download-ci"> + <a :href="data.ci.url" class="download-btn" target="_blank" rel="noopener">{{ t.viewCiBuilds }}</a> + </div> + </div> +</template> + +<style scoped> +.download-page { + max-width: 768px; + margin: 0 auto; + padding: 48px 24px; +} + +.download-page h1 { + font-size: 2rem; + font-weight: 700; + margin-bottom: 8px; +} + +.download-page > p { + color: var(--vp-c-text-2); + margin-bottom: 24px; +} + +.download-page h2 { + font-size: 1.25rem; + font-weight: 600; + margin-top: 40px; + margin-bottom: 8px; + border: none; + padding: 0; +} + +.download-requirements { + border: 1px solid var(--vp-c-brand-soft); + background: var(--vp-c-brand-soft); + border-radius: 8px; + padding: 16px 20px; + margin-bottom: 24px; +} + +.download-requirements-title { + font-weight: 600; + margin-bottom: 8px; +} + +.download-requirements ul { + margin: 0; + padding-left: 20px; +} + +.download-requirements li { + font-size: 0.9rem; + line-height: 1.7; +} + +.download-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + margin: 24px 0; +} + +@media (max-width: 640px) { + .download-grid { + grid-template-columns: 1fr; + } +} + +.download-card { + border: 1px solid var(--vp-c-divider); + border-radius: 12px; + overflow: hidden; + background: var(--vp-c-bg-soft); +} + +.download-card-header { + display: flex; + align-items: center; + gap: 16px; + padding: 24px 24px 0; +} + +.download-card-header h2 { + margin: 0; + font-size: 1.25rem; +} + +.download-icon { + width: 40px; + height: 40px; + object-fit: contain; + filter: brightness(0) saturate(100%); +} + +:global(.dark) .download-icon { + filter: brightness(0) saturate(100%) invert(1); +} + +.download-version { + margin: 2px 0 0; + font-size: 0.875rem; + color: var(--vp-c-text-2); +} + +.download-card-body { + padding: 20px 24px 24px; +} + +.download-meta { + margin: 0 0 20px; + padding: 0; +} + +.download-meta div { + display: flex; + justify-content: space-between; + padding: 6px 0; + border-bottom: 1px solid var(--vp-c-divider); + font-size: 0.875rem; +} + +.download-meta div:last-child { + border-bottom: none; +} + +.download-meta dt { + color: var(--vp-c-text-2); +} + +.download-meta dd { + margin: 0; + color: var(--vp-c-text-1); +} + +.download-actions { + display: flex; + gap: 8px; +} + +.download-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 8px 20px; + border-radius: 8px; + font-size: 0.875rem; + font-weight: 500; + text-decoration: none !important; + color: var(--vp-c-text-1); + border: 1px solid var(--vp-c-divider); + background: var(--vp-c-bg); + transition: border-color 0.25s, background 0.25s; +} + +.download-btn:hover { + border-color: var(--vp-c-brand-1); +} + +.download-btn.primary { + background: var(--vp-c-brand-1); + color: var(--vp-c-white); + border-color: var(--vp-c-brand-1); +} + +.download-btn.primary:hover { + background: var(--vp-c-brand-2); + border-color: var(--vp-c-brand-2); +} + +.download-empty { + color: var(--vp-c-text-3); + font-size: 0.875rem; +} + +.download-ci { + margin: 16px 0; +} +</style> diff --git a/website/.vitepress/theme/components/download.data.ts b/website/.vitepress/theme/components/download.data.ts new file mode 100644 index 0000000..11e0489 --- /dev/null +++ b/website/.vitepress/theme/components/download.data.ts @@ -0,0 +1,105 @@ +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/docs/.vitepress/theme/custom.css b/website/.vitepress/theme/custom.css index 1f928a1..1f928a1 100644 --- a/docs/.vitepress/theme/custom.css +++ b/website/.vitepress/theme/custom.css diff --git a/website/.vitepress/theme/index.ts b/website/.vitepress/theme/index.ts new file mode 100644 index 0000000..bc82522 --- /dev/null +++ b/website/.vitepress/theme/index.ts @@ -0,0 +1,11 @@ +import type { Theme } from 'vitepress'; +import DefaultTheme from 'vitepress/theme'; +import DownloadCard from './components/DownloadCard.vue'; +import './custom.css'; + +export default { + extends: DefaultTheme, + enhanceApp({ app }) { + app.component('DownloadCard', DownloadCard); + }, +} satisfies Theme; diff --git a/docs/biome.jsonc b/website/biome.jsonc index ac66012..ac66012 100644 --- a/docs/biome.jsonc +++ b/website/biome.jsonc diff --git a/docs/bun.lock b/website/bun.lock index 63d664a..63d664a 100644 --- a/docs/bun.lock +++ b/website/bun.lock diff --git a/docs/package.json b/website/package.json index 361214b..361214b 100644 --- a/docs/package.json +++ b/website/package.json diff --git a/docs/src/assets/brand/LICENSE b/website/src/assets/brand/LICENSE index bff9945..bff9945 100644 --- a/docs/src/assets/brand/LICENSE +++ b/website/src/assets/brand/LICENSE diff --git a/docs/src/assets/brand/folia.svg b/website/src/assets/brand/folia.svg index 59a821b..59a821b 100644 --- a/docs/src/assets/brand/folia.svg +++ b/website/src/assets/brand/folia.svg diff --git a/docs/src/assets/brand/paper.svg b/website/src/assets/brand/paper.svg index 126e636..126e636 100644 --- a/docs/src/assets/brand/paper.svg +++ b/website/src/assets/brand/paper.svg diff --git a/docs/src/assets/brand/velocity.svg b/website/src/assets/brand/velocity.svg index ee24f2b..ee24f2b 100644 --- a/docs/src/assets/brand/velocity.svg +++ b/website/src/assets/brand/velocity.svg diff --git a/docs/src/assets/features/channel-chat.png b/website/src/assets/features/channel-chat.png Binary files differindex 4f9dab2..4f9dab2 100644 --- a/docs/src/assets/features/channel-chat.png +++ b/website/src/assets/features/channel-chat.png diff --git a/docs/src/assets/features/cross-chat.png b/website/src/assets/features/cross-chat.png Binary files differindex 34857e2..34857e2 100644 --- a/docs/src/assets/features/cross-chat.png +++ b/website/src/assets/features/cross-chat.png diff --git a/docs/src/assets/features/dm.png b/website/src/assets/features/dm.png Binary files differindex bcc429d..bcc429d 100644 --- a/docs/src/assets/features/dm.png +++ b/website/src/assets/features/dm.png diff --git a/docs/src/assets/features/romaji.png b/website/src/assets/features/romaji.png Binary files differindex fd1dd9b..fd1dd9b 100644 --- a/docs/src/assets/features/romaji.png +++ b/website/src/assets/features/romaji.png diff --git a/docs/src/assets/icon.png b/website/src/assets/icon.png Binary files differindex 064445a..064445a 100644 --- a/docs/src/assets/icon.png +++ b/website/src/assets/icon.png diff --git a/website/src/download.md b/website/src/download.md new file mode 100644 index 0000000..ef1167b --- /dev/null +++ b/website/src/download.md @@ -0,0 +1,6 @@ +--- +layout: page +title: ダウンロード +--- + +<DownloadCard /> diff --git a/docs/src/index.md b/website/src/index.md index 4de1be2..2336b16 100644 --- a/docs/src/index.md +++ b/website/src/index.md @@ -8,10 +8,10 @@ hero: actions: - theme: brand text: ダウンロード - link: /player-guide/channel-chat/about + link: /download - theme: brand - text: プレイヤーガイド - link: /player-guide/channel-chat/about + text: ドキュメント + link: /docs/getting-started - theme: alt text: GitHub link: https://github.com/m1sk9/LunaticChat diff --git a/docs/tsconfig.json b/website/tsconfig.json index 5f61eef..5f61eef 100644 --- a/docs/tsconfig.json +++ b/website/tsconfig.json diff --git a/docs/wrangler.jsonc b/website/wrangler.jsonc index c1e3ce5..c1e3ce5 100644 --- a/docs/wrangler.jsonc +++ b/website/wrangler.jsonc |
