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 /website/.vitepress | |
| 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>
Diffstat (limited to 'website/.vitepress')
| -rw-r--r-- | website/.vitepress/config.mts | 50 | ||||
| -rw-r--r-- | website/.vitepress/config/en.ts | 133 | ||||
| -rw-r--r-- | website/.vitepress/config/ja.ts | 124 | ||||
| -rw-r--r-- | website/.vitepress/public/favicon.ico | bin | 0 -> 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 | 188 | ||||
| -rw-r--r-- | website/.vitepress/theme/index.ts | 11 |
8 files changed, 949 insertions, 0 deletions
diff --git a/website/.vitepress/config.mts b/website/.vitepress/config.mts new file mode 100644 index 0000000..1cb5f83 --- /dev/null +++ b/website/.vitepress/config.mts @@ -0,0 +1,50 @@ +import { execSync } from 'node:child_process'; +import { defineConfig } from 'vitepress'; +import { en } from './config/en'; +import { ja } from './config/ja'; + +const gitRoot = execSync('git rev-parse --show-toplevel').toString().trim(); +const commitHash = execSync(`git log -1 --format=%H -- ${gitRoot}/docs/`) + .toString() + .trim() + .slice(0, 7); + +export default defineConfig({ + cleanUrls: true, + description: 'Next-generation channel chat plugin for Paper/Velocity', + head: [['link', { href: '/favicon.ico', rel: 'icon' }]], + locales: { + en: { + label: 'English', + lang: 'en-US', + link: '/en/', + themeConfig: { + ...en, + footer: { + copyright: 'Copyright © 2026 m1sk9', + message: `<a href="https://github.com/m1sk9/LunaticChat/commit/${commitHash}">LunaticChat/docs@${commitHash}</a>`, + }, + }, + }, + root: { + label: '日本語', + lang: 'ja-JP', + themeConfig: { + ...ja, + footer: { + copyright: 'Copyright © 2026 m1sk9', + message: `<a href="https://github.com/m1sk9/LunaticChat/commit/${commitHash}">LunaticChat/docs@${commitHash}</a>`, + }, + }, + }, + }, + outDir: './dist', + srcDir: 'src', + themeConfig: { + socialLinks: [ + { icon: 'github', link: 'https://github.com/m1sk9/LunaticChat' }, + ], + }, + title: 'LunaticChat', + titleTemplate: 'LunaticChat', +}); diff --git a/website/.vitepress/config/en.ts b/website/.vitepress/config/en.ts new file mode 100644 index 0000000..b42faeb --- /dev/null +++ b/website/.vitepress/config/en.ts @@ -0,0 +1,133 @@ +import type { DefaultTheme } from 'vitepress'; + +export const en: DefaultTheme.Config = { + editLink: { + pattern: 'https://github.com/m1sk9/LunaticChat/edit/main/docs/src/:path', + text: 'Edit this page on GitHub', + }, + 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/': [ + { + link: '/en/admin-guide/getting-started', + text: 'Getting Started', + }, + { + link: '/en/admin-guide/configuration', + text: 'Configuration', + }, + { + link: '/en/admin-guide/permissions', + text: 'Permissions', + }, + { + items: [ + { + link: '/en/admin-guide/channel-chat/introduction', + text: 'Deployment Guide', + }, + { + link: '/en/admin-guide/channel-chat/logs', + text: 'Logs', + }, + ], + text: 'Channel Chat', + }, + { + link: '/en/admin-guide/velocity', + text: 'Velocity Integration', + }, + { + link: '/en/admin-guide/cache', + text: 'Cache System', + }, + { + link: '/en/admin-guide/management-data', + text: 'Data Management', + }, + ], + '/en/player-guide/': [ + { + link: '/en/player-guide/getting-started', + text: 'Getting Started', + }, + { + link: '/en/player-guide/about', + text: 'About LunaticChat', + }, + { + items: [ + { + link: '/en/player-guide/channel-chat/private-channel', + text: 'Private Channel', + }, + { + link: '/en/player-guide/channel-chat/moderation', + text: 'Moderation', + }, + ], + link: '/en/player-guide/channel-chat/about', + text: 'Channel Chat', + }, + { + link: '/en/player-guide/direct-message', + text: 'Direct Messages', + }, + { + link: '/en/player-guide/japanese-romanization', + text: 'Romanization Conversion', + }, + { + items: [ + { + link: '/en/player-guide/commands/tell', + text: '/tell', + }, + { + link: '/en/player-guide/commands/reply', + text: '/reply', + }, + { + link: '/en/player-guide/commands/jp', + text: '/jp', + }, + { + link: '/en/player-guide/commands/notice', + text: '/notice', + }, + { + items: [ + { + link: '/en/player-guide/commands/lc/settings', + text: '/lc settings', + }, + { + link: '/en/player-guide/commands/lc/status', + text: '/lc status', + }, + { + link: '/en/player-guide/commands/lc/channel', + text: '/lc channel', + }, + ], + text: '/lc', + }, + { + items: [ + { + link: '/en/player-guide/commands/lcv/status', + text: '/lcv status', + }, + ], + text: '/lcv', + }, + ], + text: 'Commands', + }, + ], + }, +}; diff --git a/website/.vitepress/config/ja.ts b/website/.vitepress/config/ja.ts new file mode 100644 index 0000000..af2349b --- /dev/null +++ b/website/.vitepress/config/ja.ts @@ -0,0 +1,124 @@ +import type { DefaultTheme } from 'vitepress'; + +export const ja: DefaultTheme.Config = { + editLink: { + pattern: 'https://github.com/m1sk9/LunaticChat/edit/main/docs/src/:path', + text: 'GitHub で編集', + }, + nav: [ + { link: '/download', text: 'ダウンロード' }, + { link: '/docs', text: 'ドキュメント' } + ], + sidebar: { + '/admin-guide/': [ + { + link: '/admin-guide/getting-started', + text: 'はじめる', + }, + { + link: '/admin-guide/configuration', + text: '設定', + }, + { + link: '/admin-guide/permissions', + text: 'パーミッション', + }, + { + items: [ + { + link: '/admin-guide/channel-chat/introduction', + text: '展開ガイド', + }, + { + link: '/admin-guide/channel-chat/logs', + text: 'ログ', + }, + ], + text: 'チャンネルチャット', + }, + { + link: '/admin-guide/velocity', + text: 'Velocity 連携', + }, + { + link: '/admin-guide/cache', + text: 'キャッシュシステム', + }, + { + link: '/admin-guide/management-data', + text: 'データの管理', + }, + ], + '/player-guide/': [ + { + link: '/player-guide/getting-started', + text: 'はじめる', + }, + { + link: '/player-guide/about', + text: 'LunaticChat について', + }, + { + items: [ + { + link: '/player-guide/channel-chat/private-channel', + text: 'プライベートチャンネル', + }, + { + link: '/player-guide/channel-chat/moderation', + text: 'モデレーション', + }, + ], + link: '/player-guide/channel-chat/about', + text: 'チャンネルチャット', + }, + { + link: '/player-guide/direct-message', + text: 'ダイレクトメッセージ', + }, + { + link: '/player-guide/japanese-romanization', + text: 'ローマ字変換', + }, + { + items: [ + { + link: '/player-guide/commands/tell', + text: '/tell', + }, + { + link: '/player-guide/commands/reply', + text: '/reply', + }, + { + items: [ + { + link: '/player-guide/commands/lc/settings', + text: '/lc settings', + }, + { + link: '/player-guide/commands/lc/status', + text: '/lc status', + }, + { + link: '/player-guide/commands/lc/channel', + text: '/lc channel', + }, + ], + text: '/lc', + }, + { + items: [ + { + link: '/player-guide/commands/lcv/status', + text: '/lcv status', + }, + ], + text: '/lcv', + }, + ], + text: 'コマンド', + }, + ], + }, +}; diff --git a/website/.vitepress/public/favicon.ico b/website/.vitepress/public/favicon.ico Binary files differnew file mode 100644 index 0000000..46c0cad --- /dev/null +++ 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/website/.vitepress/theme/custom.css b/website/.vitepress/theme/custom.css new file mode 100644 index 0000000..1f928a1 --- /dev/null +++ b/website/.vitepress/theme/custom.css @@ -0,0 +1,188 @@ +/* ── Home page custom styles ── */ + +/* Hero tagline emphasis */ +.VPHero .tagline { + font-size: 1.2rem !important; + max-width: 560px; +} + +/* ── Feature showcase: alternating text + image ── */ +.feature-showcase { + max-width: 1152px; + margin: 0 auto; + padding: 64px 24px; + display: flex; + align-items: center; + gap: 48px; +} + +.feature-showcase.reverse { + flex-direction: row-reverse; +} + +.feature-showcase-text { + flex: 3; + min-width: 0; +} + +.feature-showcase-text h2 { + font-size: 1.75rem; + font-weight: 700; + line-height: 1.3; + margin-bottom: 16px; +} + +.feature-showcase-text p { + color: var(--vp-c-text-2); + font-size: 1rem; + line-height: 1.7; + margin-bottom: 12px; +} + +.feature-showcase-text ul { + list-style: none; + padding: 0; + margin: 16px 0 0; +} + +.feature-showcase-text ul li { + position: relative; + padding-left: 24px; + color: var(--vp-c-text-2); + font-size: 0.95rem; + line-height: 1.7; + margin-bottom: 8px; +} + +.feature-showcase-text ul li::before { + content: ''; + position: absolute; + left: 0; + top: 10px; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--vp-c-brand-1); +} + +.feature-showcase-image { + flex: 3; + min-width: 0; +} + +.feature-showcase-image img { + width: 100%; + border-radius: 12px; + border: 1px solid var(--vp-c-divider); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08); +} + +.feature-showcase-image .image-placeholder { + width: 100%; + aspect-ratio: 16 / 10; + border-radius: 12px; + border: 2px dashed var(--vp-c-divider); + background: var(--vp-c-bg-soft); + display: flex; + align-items: center; + justify-content: center; + color: var(--vp-c-text-3); + font-size: 0.875rem; +} + +@media (max-width: 768px) { + .feature-showcase, + .feature-showcase.reverse { + flex-direction: column; + padding: 40px 24px; + gap: 32px; + } +} + +/* ── Platform cards ── */ +.platform-section { + max-width: 1024px; + margin: 0 auto; + padding: 64px 24px; + text-align: center; +} + +.platform-section h2 { + font-size: 1.75rem; + font-weight: 700; + margin-bottom: 8px; +} + +.platform-section .section-desc { + color: var(--vp-c-text-2); + margin-bottom: 32px; + font-size: 1rem; +} + +.platform-cards { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; +} + +@media (max-width: 640px) { + .platform-cards { + grid-template-columns: 1fr; + } +} + +.platform-card { + display: block; + border: 1px solid var(--vp-c-divider); + border-radius: 12px; + padding: 24px 16px; + background: var(--vp-c-bg-soft); + transition: border-color 0.25s, box-shadow 0.25s; + text-decoration: none !important; + color: inherit; +} + +.platform-card:hover { + border-color: var(--vp-c-brand-1); + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); +} + +.platform-icon { + margin-bottom: 12px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; +} + +.platform-icon img { + width: 40px; + height: 40px; + object-fit: contain; + filter: brightness(0) saturate(100%); +} + +.dark .platform-icon img { + filter: brightness(0) saturate(100%) invert(1); +} + +.platform-card .name { + font-size: 1.1rem; + font-weight: 600; + margin-bottom: 8px; + color: var(--vp-c-text-1); +} + +.platform-card .desc { + font-size: 0.875rem; + color: var(--vp-c-text-2); + line-height: 1.6; +} + +/* ── Divider ── */ +.home-divider { + max-width: 1024px; + margin: 0 auto; + border: none; + border-top: 1px solid var(--vp-c-divider); +} 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; |
