refactor: standardize code formatting across components and utilities
Some checks failed
Push to github container register / push-docker (push) Has been cancelled
Some checks failed
Push to github container register / push-docker (push) Has been cancelled
- Updated formatting in SiteFooter and SiteHeader components for consistency. - Refactored Badge, Button, Card, Separator components to improve readability. - Enhanced markdown parsing logic in announcements and markdown utility functions. - Adjusted ESLint configuration for better code quality checks. - Added biome.json for BiomeJS configuration. - Updated package.json and configuration files for improved dependency management.
This commit is contained in:
@@ -3,64 +3,75 @@ import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { getAnnouncementBySlug, getAnnouncementSlugs } from "@/lib/announcements";
|
||||
import {
|
||||
getAnnouncementBySlug,
|
||||
getAnnouncementSlugs,
|
||||
} from "@/lib/announcements";
|
||||
|
||||
type PageProps = {
|
||||
params: Promise<{ slug: string }>;
|
||||
params: Promise<{ slug: string }>;
|
||||
};
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const slugs = await getAnnouncementSlugs();
|
||||
return slugs.map((slug) => ({ slug }));
|
||||
const slugs = await getAnnouncementSlugs();
|
||||
return slugs.map((slug) => ({ slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: PageProps): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
|
||||
try {
|
||||
const announcement = await getAnnouncementBySlug(slug);
|
||||
return {
|
||||
title: `${announcement.title} | お知らせ`,
|
||||
description: announcement.summary || `${announcement.title} のお知らせです。`,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
title: "お知らせ",
|
||||
description: "お知らせページ",
|
||||
};
|
||||
}
|
||||
try {
|
||||
const announcement = await getAnnouncementBySlug(slug);
|
||||
return {
|
||||
title: `${announcement.title} | お知らせ`,
|
||||
description:
|
||||
announcement.summary || `${announcement.title} のお知らせです。`,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
title: "お知らせ",
|
||||
description: "お知らせページ",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default async function AnnouncementDetailPage({ params }: PageProps) {
|
||||
const { slug } = await params;
|
||||
const { slug } = await params;
|
||||
|
||||
let announcement;
|
||||
try {
|
||||
announcement = await getAnnouncementBySlug(slug);
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
let announcement;
|
||||
try {
|
||||
announcement = await getAnnouncementBySlug(slug);
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="mx-auto flex w-full max-w-4xl flex-1 flex-col gap-6 px-4 py-8 sm:px-8 sm:py-12">
|
||||
<header className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-8">
|
||||
<Badge variant="outline" className="mb-3">
|
||||
お知らせ詳細
|
||||
</Badge>
|
||||
<p className="text-xs text-muted-foreground">{announcement.date}</p>
|
||||
<h1 className="mt-2 text-2xl font-semibold tracking-tight sm:text-4xl">{announcement.title}</h1>
|
||||
</header>
|
||||
return (
|
||||
<article className="mx-auto flex w-full max-w-4xl flex-1 flex-col gap-6 px-4 py-8 sm:px-8 sm:py-12">
|
||||
<header className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-8">
|
||||
<Badge variant="outline" className="mb-3">
|
||||
お知らせ詳細
|
||||
</Badge>
|
||||
<p className="text-xs text-muted-foreground">{announcement.date}</p>
|
||||
<h1 className="mt-2 text-2xl font-semibold tracking-tight sm:text-4xl">
|
||||
{announcement.title}
|
||||
</h1>
|
||||
</header>
|
||||
|
||||
<section className="rounded-3xl border bg-card p-6 shadow-sm motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-2 motion-safe:duration-700 sm:p-8">
|
||||
<div
|
||||
className="markdown-body space-y-4 text-sm leading-7 sm:text-base [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4 [&_blockquote]:border-l-2 [&_blockquote]:pl-3 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_h1]:text-2xl [&_h1]:font-semibold [&_h2]:text-xl [&_h2]:font-semibold [&_h3]:text-lg [&_h3]:font-semibold [&_li]:ml-5 [&_ol]:list-decimal [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-3 [&_ul]:list-disc"
|
||||
dangerouslySetInnerHTML={{ __html: announcement.contentHtml }}
|
||||
/>
|
||||
</section>
|
||||
<section className="rounded-3xl border bg-card p-6 shadow-sm motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-2 motion-safe:duration-700 sm:p-8">
|
||||
<div
|
||||
className="markdown-body space-y-4 text-sm leading-7 sm:text-base [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4 [&_blockquote]:border-l-2 [&_blockquote]:pl-3 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_h1]:text-2xl [&_h1]:font-semibold [&_h2]:text-xl [&_h2]:font-semibold [&_h3]:text-lg [&_h3]:font-semibold [&_li]:ml-5 [&_ol]:list-decimal [&_pre]:overflow-x-auto [&_pre]:rounded-lg [&_pre]:bg-muted [&_pre]:p-3 [&_ul]:list-disc"
|
||||
dangerouslySetInnerHTML={{ __html: announcement.contentHtml }}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Link href="/announcements" className="text-sm text-primary underline underline-offset-4 transition-transform duration-200 hover:-translate-y-0.5">
|
||||
お知らせ一覧へ戻る
|
||||
</Link>
|
||||
</article>
|
||||
);
|
||||
<Link
|
||||
href="/announcements"
|
||||
className="text-sm text-primary underline underline-offset-4 transition-transform duration-200 hover:-translate-y-0.5"
|
||||
>
|
||||
お知らせ一覧へ戻る
|
||||
</Link>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export default function AnnouncementsLoading() {
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-5xl flex-1 items-center justify-center px-4 py-12 sm:px-8">
|
||||
<div className="flex items-center gap-3 rounded-xl border bg-card px-5 py-3 text-sm text-muted-foreground shadow-sm">
|
||||
<span className="inline-block size-2.5 animate-pulse rounded-full bg-primary" />
|
||||
お知らせを読み込み中...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-5xl flex-1 items-center justify-center px-4 py-12 sm:px-8">
|
||||
<div className="flex items-center gap-3 rounded-xl border bg-card px-5 py-3 text-sm text-muted-foreground shadow-sm">
|
||||
<span className="inline-block size-2.5 animate-pulse rounded-full bg-primary" />
|
||||
お知らせを読み込み中...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,43 +4,48 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { getAllAnnouncements } from "@/lib/announcements";
|
||||
|
||||
export const metadata = {
|
||||
title: "お知らせ | Takasumi-Neodyマイクラサーバプロジェクト",
|
||||
description: "Takasumi-Neodyマイクラサーバプロジェクトのお知らせ一覧です。",
|
||||
title: "お知らせ | Takasumi-Neodyマイクラサーバプロジェクト",
|
||||
description: "Takasumi-Neodyマイクラサーバプロジェクトのお知らせ一覧です。",
|
||||
};
|
||||
|
||||
export default async function AnnouncementsPage() {
|
||||
const announcements = await getAllAnnouncements();
|
||||
const announcements = await getAllAnnouncements();
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-6 px-4 py-8 sm:px-8 sm:py-12">
|
||||
<header className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-8">
|
||||
<Badge variant="outline" className="mb-3">
|
||||
お知らせ
|
||||
</Badge>
|
||||
<h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">更新情報・告知</h1>
|
||||
<p className="mt-3 text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
このページは Markdown で作成したお知らせを表示します。
|
||||
</p>
|
||||
</header>
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-5xl flex-1 flex-col gap-6 px-4 py-8 sm:px-8 sm:py-12">
|
||||
<header className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-8">
|
||||
<Badge variant="outline" className="mb-3">
|
||||
お知らせ
|
||||
</Badge>
|
||||
<h1 className="text-3xl font-semibold tracking-tight sm:text-4xl">
|
||||
更新情報・告知
|
||||
</h1>
|
||||
<p className="mt-3 text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
このページは Markdown で作成したお知らせを表示します。
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<section className="grid gap-4">
|
||||
{announcements.map((item) => (
|
||||
<article className="rounded-2xl border border-foreground/10 bg-card p-5 shadow-xs transition-all duration-300 motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4 hover:-translate-y-1 hover:shadow-md sm:p-6" key={item.slug}>
|
||||
<header>
|
||||
<p className="text-xs text-muted-foreground">{item.date}</p>
|
||||
<h2 className="mt-1 text-xl font-semibold">
|
||||
<Link
|
||||
href={`/announcements/${item.slug}`}
|
||||
className="underline-offset-4 transition-colors duration-200 hover:text-primary hover:underline"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</h2>
|
||||
</header>
|
||||
<p className="mt-3 text-sm text-muted-foreground">{item.summary}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
<section className="grid gap-4">
|
||||
{announcements.map((item) => (
|
||||
<article
|
||||
className="rounded-2xl border border-foreground/10 bg-card p-5 shadow-xs transition-all duration-300 motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4 hover:-translate-y-1 hover:shadow-md sm:p-6"
|
||||
key={item.slug}
|
||||
>
|
||||
<header>
|
||||
<p className="text-xs text-muted-foreground">{item.date}</p>
|
||||
<h2 className="mt-1 text-xl font-semibold">
|
||||
<Link
|
||||
href={`/announcements/${item.slug}`}
|
||||
className="underline-offset-4 transition-colors duration-200 hover:text-primary hover:underline"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</h2>
|
||||
</header>
|
||||
<p className="mt-3 text-sm text-muted-foreground">{item.summary}</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
228
app/globals.css
228
app/globals.css
@@ -5,126 +5,126 @@
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--font-heading: var(--font-sans);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) * 0.6);
|
||||
--radius-md: calc(var(--radius) * 0.8);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) * 1.4);
|
||||
--radius-2xl: calc(var(--radius) * 1.8);
|
||||
--radius-3xl: calc(var(--radius) * 2.2);
|
||||
--radius-4xl: calc(var(--radius) * 2.6);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--font-heading: var(--font-sans);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) * 0.6);
|
||||
--radius-md: calc(var(--radius) * 0.8);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) * 1.4);
|
||||
--radius-2xl: calc(var(--radius) * 1.8);
|
||||
--radius-3xl: calc(var(--radius) * 2.2);
|
||||
--radius-4xl: calc(var(--radius) * 2.6);
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
--chart-2: oklch(0.556 0 0);
|
||||
--chart-3: oklch(0.439 0 0);
|
||||
--chart-4: oklch(0.371 0 0);
|
||||
--chart-5: oklch(0.269 0 0);
|
||||
--radius: 0.625rem;
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
--chart-2: oklch(0.556 0 0);
|
||||
--chart-3: oklch(0.439 0 0);
|
||||
--chart-4: oklch(0.371 0 0);
|
||||
--chart-5: oklch(0.269 0 0);
|
||||
--radius: 0.625rem;
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
--chart-2: oklch(0.556 0 0);
|
||||
--chart-3: oklch(0.439 0 0);
|
||||
--chart-4: oklch(0.371 0 0);
|
||||
--chart-5: oklch(0.269 0 0);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.87 0 0);
|
||||
--chart-2: oklch(0.556 0 0);
|
||||
--chart-3: oklch(0.439 0 0);
|
||||
--chart-4: oklch(0.371 0 0);
|
||||
--chart-5: oklch(0.269 0 0);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
html {
|
||||
@apply font-sans;
|
||||
}
|
||||
}
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
html {
|
||||
@apply font-sans;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,38 +5,46 @@ import { cn } from "@/lib/utils";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
|
||||
const inter = Inter({subsets:['latin'],variable:'--font-sans'});
|
||||
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Takasumi-Neodyマイクラサーバプロジェクト接続ガイド",
|
||||
description: "Takasumi-Neodyマイクラサーバプロジェクト(サバイバル鯖・建築鯖)への接続方法とサーバアドレスを案内します。",
|
||||
title: "Takasumi-Neodyマイクラサーバプロジェクト接続ガイド",
|
||||
description:
|
||||
"Takasumi-Neodyマイクラサーバプロジェクト(サバイバル鯖・建築鯖)への接続方法とサーバアドレスを案内します。",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html
|
||||
lang="ja"
|
||||
className={cn("h-full", "antialiased", geistSans.variable, geistMono.variable, "font-sans", inter.variable)}
|
||||
>
|
||||
<body className="min-h-full flex flex-col">
|
||||
<SiteHeader />
|
||||
{children}
|
||||
<SiteFooter />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return (
|
||||
<html
|
||||
lang="ja"
|
||||
className={cn(
|
||||
"h-full",
|
||||
"antialiased",
|
||||
geistSans.variable,
|
||||
geistMono.variable,
|
||||
"font-sans",
|
||||
inter.variable,
|
||||
)}
|
||||
>
|
||||
<body className="min-h-full flex flex-col">
|
||||
<SiteHeader />
|
||||
{children}
|
||||
<SiteFooter />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-1 items-center justify-center px-4 py-16 sm:px-8">
|
||||
<div className="flex items-center gap-3 rounded-xl border bg-card px-5 py-3 text-sm text-muted-foreground shadow-sm">
|
||||
<span className="inline-block size-2.5 animate-pulse rounded-full bg-primary" />
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-1 items-center justify-center px-4 py-16 sm:px-8">
|
||||
<div className="flex items-center gap-3 rounded-xl border bg-card px-5 py-3 text-sm text-muted-foreground shadow-sm">
|
||||
<span className="inline-block size-2.5 animate-pulse rounded-full bg-primary" />
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
202
app/page.tsx
202
app/page.tsx
@@ -9,104 +9,116 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const servers = [
|
||||
{
|
||||
name: "サバイバル鯖",
|
||||
description: "採掘・冒険・建築を楽しむ通常ワールドです。",
|
||||
address: "survival.mc.neody.ad.jp",
|
||||
icon: Sprout,
|
||||
badgeVariant: "default" as const,
|
||||
},
|
||||
{
|
||||
name: "建築鯖",
|
||||
description: "大型建築や街づくり向けのクリエイティブ環境です。",
|
||||
address: "kenchiku.mc.neody.ad.jp",
|
||||
icon: Pickaxe,
|
||||
badgeVariant: "secondary" as const,
|
||||
},
|
||||
{
|
||||
name: "サバイバル鯖",
|
||||
description: "採掘・冒険・建築を楽しむ通常ワールドです。",
|
||||
address: "survival.mc.neody.ad.jp",
|
||||
icon: Sprout,
|
||||
badgeVariant: "default" as const,
|
||||
},
|
||||
{
|
||||
name: "建築鯖",
|
||||
description: "大型建築や街づくり向けのクリエイティブ環境です。",
|
||||
address: "kenchiku.mc.neody.ad.jp",
|
||||
icon: Pickaxe,
|
||||
badgeVariant: "secondary" as const,
|
||||
},
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="relative flex min-h-full flex-1 flex-col overflow-x-clip bg-background">
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[380px] bg-gradient-to-b from-emerald-200/40 via-sky-200/25 to-transparent blur-3xl dark:from-emerald-500/20 dark:via-sky-500/20" />
|
||||
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-8 px-4 py-8 sm:gap-10 sm:px-8 sm:py-14">
|
||||
<section className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-10">
|
||||
<Badge variant="outline" className="mb-4 max-w-full text-[11px] sm:text-xs">
|
||||
Takasumi-Neodyマイクラサーバプロジェクト接続ガイド
|
||||
</Badge>
|
||||
<h1 className="text-3xl font-semibold tracking-tight sm:text-5xl">
|
||||
Minecraft サーバへ接続する
|
||||
</h1>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
サーバー追加画面で下記アドレスを入力してください。Java版 と
|
||||
統合版のどちらにも対応しています。
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<Link
|
||||
href="/announcements"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"transition-transform duration-200 hover:-translate-y-0.5"
|
||||
)}
|
||||
>
|
||||
お知らせを見る
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<div className="relative flex min-h-full flex-1 flex-col overflow-x-clip bg-background">
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-[380px] bg-gradient-to-b from-emerald-200/40 via-sky-200/25 to-transparent blur-3xl dark:from-emerald-500/20 dark:via-sky-500/20" />
|
||||
<main className="mx-auto flex w-full max-w-6xl flex-1 flex-col gap-8 px-4 py-8 sm:gap-10 sm:px-8 sm:py-14">
|
||||
<section className="rounded-3xl border bg-card/70 p-6 shadow-sm backdrop-blur motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-3 motion-safe:duration-700 sm:p-10">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="mb-4 max-w-full text-[11px] sm:text-xs"
|
||||
>
|
||||
Takasumi-Neodyマイクラサーバプロジェクト接続ガイド
|
||||
</Badge>
|
||||
<h1 className="text-3xl font-semibold tracking-tight sm:text-5xl">
|
||||
Minecraft サーバへ接続する
|
||||
</h1>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-7 text-muted-foreground sm:text-base">
|
||||
サーバー追加画面で下記アドレスを入力してください。Java版 と
|
||||
統合版のどちらにも対応しています。
|
||||
</p>
|
||||
<div className="mt-6">
|
||||
<Link
|
||||
href="/announcements"
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"transition-transform duration-200 hover:-translate-y-0.5",
|
||||
)}
|
||||
>
|
||||
お知らせを見る
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-4 md:grid-cols-2">
|
||||
{servers.map((server) => {
|
||||
const Icon = server.icon;
|
||||
return (
|
||||
<article
|
||||
key={server.name}
|
||||
className="rounded-2xl border border-foreground/10 bg-card p-5 shadow-xs transition-all duration-300 motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4 hover:-translate-y-1 hover:shadow-md sm:p-6"
|
||||
>
|
||||
<header>
|
||||
<div className="mb-2 flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||
<Badge variant={server.badgeVariant}>{server.name}</Badge>
|
||||
<span className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<Wifi className="size-3.5" />
|
||||
Java版 / 統合版 対応
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="flex items-center gap-2 text-xl font-semibold">
|
||||
<Icon className="size-5" />
|
||||
{server.name}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{server.description}</p>
|
||||
</header>
|
||||
<div className="mt-5">
|
||||
<div className="rounded-xl border bg-background px-4 py-3">
|
||||
<p className="text-xs text-muted-foreground">サーバアドレス</p>
|
||||
<p className="mt-1 break-all font-mono text-sm">{server.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 flex gap-2">
|
||||
<a
|
||||
className={cn(buttonVariants(), "w-full sm:w-auto")}
|
||||
href={`minecraft://?addExternalServer=${server.name}|${server.address}`}
|
||||
>
|
||||
Minecraft で開く
|
||||
<ExternalLink className="size-4" />
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
<section className="grid gap-4 md:grid-cols-2">
|
||||
{servers.map((server) => {
|
||||
const Icon = server.icon;
|
||||
return (
|
||||
<article
|
||||
key={server.name}
|
||||
className="rounded-2xl border border-foreground/10 bg-card p-5 shadow-xs transition-all duration-300 motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-4 hover:-translate-y-1 hover:shadow-md sm:p-6"
|
||||
>
|
||||
<header>
|
||||
<div className="mb-2 flex flex-col items-start gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3">
|
||||
<Badge variant={server.badgeVariant}>{server.name}</Badge>
|
||||
<span className="inline-flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<Wifi className="size-3.5" />
|
||||
Java版 / 統合版 対応
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="flex items-center gap-2 text-xl font-semibold">
|
||||
<Icon className="size-5" />
|
||||
{server.name}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{server.description}
|
||||
</p>
|
||||
</header>
|
||||
<div className="mt-5">
|
||||
<div className="rounded-xl border bg-background px-4 py-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
サーバアドレス
|
||||
</p>
|
||||
<p className="mt-1 break-all font-mono text-sm">
|
||||
{server.address}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 flex gap-2">
|
||||
<a
|
||||
className={cn(buttonVariants(), "w-full sm:w-auto")}
|
||||
href={`minecraft://?addExternalServer=${server.name}|${server.address}`}
|
||||
>
|
||||
Minecraft で開く
|
||||
<ExternalLink className="size-4" />
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
|
||||
<section className="rounded-3xl border bg-card p-6 shadow-sm motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-2 motion-safe:duration-700 sm:p-8">
|
||||
<h2 className="text-xl font-semibold">接続手順</h2>
|
||||
<Separator className="my-4" />
|
||||
<ol className="space-y-3 text-sm leading-7 sm:text-base">
|
||||
<li>1. Minecraft(Java版 または統合版)を起動する。</li>
|
||||
<li>2. サーバー追加画面を開く(Java版: マルチプレイ / 統合版: サーバー)。</li>
|
||||
<li>3. 接続したい鯖のサーバアドレスを入力して保存する。</li>
|
||||
<li>4. 一覧からサーバを選んで接続する。</li>
|
||||
</ol>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
<section className="rounded-3xl border bg-card p-6 shadow-sm motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-2 motion-safe:duration-700 sm:p-8">
|
||||
<h2 className="text-xl font-semibold">接続手順</h2>
|
||||
<Separator className="my-4" />
|
||||
<ol className="space-y-3 text-sm leading-7 sm:text-base">
|
||||
<li>1. Minecraft(Java版 または統合版)を起動する。</li>
|
||||
<li>
|
||||
2. サーバー追加画面を開く(Java版: マルチプレイ / 統合版:
|
||||
サーバー)。
|
||||
</li>
|
||||
<li>3. 接続したい鯖のサーバアドレスを入力して保存する。</li>
|
||||
<li>4. 一覧からサーバを選んで接続する。</li>
|
||||
</ol>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,42 +5,46 @@ import { getAllAnnouncements } from "@/lib/announcements";
|
||||
export const dynamic = "force-static";
|
||||
|
||||
const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL ??
|
||||
process.env.SITE_URL ??
|
||||
"https://mc.neody.ad.jp";
|
||||
process.env.NEXT_PUBLIC_SITE_URL ??
|
||||
process.env.SITE_URL ??
|
||||
"https://mc.neody.ad.jp";
|
||||
|
||||
function toAbsoluteUrl(pathname: string): string {
|
||||
return new URL(pathname, SITE_URL).toString();
|
||||
return new URL(pathname, SITE_URL).toString();
|
||||
}
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const announcements = await getAllAnnouncements();
|
||||
const announcements = await getAllAnnouncements();
|
||||
|
||||
const staticRoutes: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: toAbsoluteUrl("/"),
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
url: toAbsoluteUrl("/announcements/"),
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
];
|
||||
const staticRoutes: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: toAbsoluteUrl("/"),
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "weekly",
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
url: toAbsoluteUrl("/announcements/"),
|
||||
lastModified: new Date(),
|
||||
changeFrequency: "daily",
|
||||
priority: 0.8,
|
||||
},
|
||||
];
|
||||
|
||||
const announcementRoutes: MetadataRoute.Sitemap = announcements.map((item) => {
|
||||
const parsedDate = new Date(item.date);
|
||||
const announcementRoutes: MetadataRoute.Sitemap = announcements.map(
|
||||
(item) => {
|
||||
const parsedDate = new Date(item.date);
|
||||
|
||||
return {
|
||||
url: toAbsoluteUrl(`/announcements/${item.slug}/`),
|
||||
lastModified: Number.isNaN(parsedDate.getTime()) ? new Date() : parsedDate,
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
};
|
||||
});
|
||||
return {
|
||||
url: toAbsoluteUrl(`/announcements/${item.slug}/`),
|
||||
lastModified: Number.isNaN(parsedDate.getTime())
|
||||
? new Date()
|
||||
: parsedDate,
|
||||
changeFrequency: "monthly",
|
||||
priority: 0.7,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return [...staticRoutes, ...announcementRoutes];
|
||||
return [...staticRoutes, ...announcementRoutes];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user