diff --git a/app/announcements/[slug]/page.tsx b/app/announcements/[slug]/page.tsx index 22811ba..c379737 100644 --- a/app/announcements/[slug]/page.tsx +++ b/app/announcements/[slug]/page.tsx @@ -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 { - const { slug } = await params; +export async function generateMetadata({ + params, +}: PageProps): Promise { + 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 ( -
-
- - お知らせ詳細 - -

{announcement.date}

-

{announcement.title}

-
+ return ( +
+
+ + お知らせ詳細 + +

{announcement.date}

+

+ {announcement.title} +

+
-
-
-
+
+
+
- - お知らせ一覧へ戻る - -
- ); + + お知らせ一覧へ戻る + +
+ ); } diff --git a/app/announcements/loading.tsx b/app/announcements/loading.tsx index 4ac9ba0..c92987f 100644 --- a/app/announcements/loading.tsx +++ b/app/announcements/loading.tsx @@ -1,10 +1,10 @@ export default function AnnouncementsLoading() { - return ( -
-
- - お知らせを読み込み中... -
-
- ); + return ( +
+
+ + お知らせを読み込み中... +
+
+ ); } diff --git a/app/announcements/page.tsx b/app/announcements/page.tsx index ec10217..8d3006a 100644 --- a/app/announcements/page.tsx +++ b/app/announcements/page.tsx @@ -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 ( -
-
- - お知らせ - -

更新情報・告知

-

- このページは Markdown で作成したお知らせを表示します。 -

-
+ return ( +
+
+ + お知らせ + +

+ 更新情報・告知 +

+

+ このページは Markdown で作成したお知らせを表示します。 +

+
-
- {announcements.map((item) => ( -
-
-

{item.date}

-

- - {item.title} - -

-
-

{item.summary}

-
- ))} -
-
- ); +
+ {announcements.map((item) => ( +
+
+

{item.date}

+

+ + {item.title} + +

+
+

{item.summary}

+
+ ))} +
+
+ ); } diff --git a/app/globals.css b/app/globals.css index c56032b..81c2ef1 100644 --- a/app/globals.css +++ b/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; - } -} \ No newline at end of file + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + html { + @apply font-sans; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 65613c7..cb97508 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -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 ( - - - - {children} - - - - ); + return ( + + + + {children} + + + + ); } diff --git a/app/loading.tsx b/app/loading.tsx index aacb26a..4e349cf 100644 --- a/app/loading.tsx +++ b/app/loading.tsx @@ -1,10 +1,10 @@ export default function Loading() { - return ( -
-
- - Loading... -
-
- ); + return ( +
+
+ + Loading... +
+
+ ); } diff --git a/app/page.tsx b/app/page.tsx index 424fef0..b5168fc 100644 --- a/app/page.tsx +++ b/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 ( -
-
-
-
- - Takasumi-Neodyマイクラサーバプロジェクト接続ガイド - -

- Minecraft サーバへ接続する -

-

- サーバー追加画面で下記アドレスを入力してください。Java版 と - 統合版のどちらにも対応しています。 -

-
- - お知らせを見る - -
-
+ return ( +
+
+
+
+ + Takasumi-Neodyマイクラサーバプロジェクト接続ガイド + +

+ Minecraft サーバへ接続する +

+

+ サーバー追加画面で下記アドレスを入力してください。Java版 と + 統合版のどちらにも対応しています。 +

+
+ + お知らせを見る + +
+
-
- {servers.map((server) => { - const Icon = server.icon; - return ( -
-
-
- {server.name} - - - Java版 / 統合版 対応 - -
-

- - {server.name} -

-

{server.description}

-
-
-
-

サーバアドレス

-

{server.address}

-
-
- -
- ); - })} -
+
+ {servers.map((server) => { + const Icon = server.icon; + return ( +
+
+
+ {server.name} + + + Java版 / 統合版 対応 + +
+

+ + {server.name} +

+

+ {server.description} +

+
+
+
+

+ サーバアドレス +

+

+ {server.address} +

+
+
+ +
+ ); + })} +
-
-

接続手順

- -
    -
  1. 1. Minecraft(Java版 または統合版)を起動する。
  2. -
  3. 2. サーバー追加画面を開く(Java版: マルチプレイ / 統合版: サーバー)。
  4. -
  5. 3. 接続したい鯖のサーバアドレスを入力して保存する。
  6. -
  7. 4. 一覧からサーバを選んで接続する。
  8. -
-
-
-
- ); +
+

接続手順

+ +
    +
  1. 1. Minecraft(Java版 または統合版)を起動する。
  2. +
  3. + 2. サーバー追加画面を開く(Java版: マルチプレイ / 統合版: + サーバー)。 +
  4. +
  5. 3. 接続したい鯖のサーバアドレスを入力して保存する。
  6. +
  7. 4. 一覧からサーバを選んで接続する。
  8. +
+
+
+
+ ); } diff --git a/app/sitemap.ts b/app/sitemap.ts index 64f71ac..b11bb24 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -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 { - 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]; } diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..199011c --- /dev/null +++ b/biome.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", + "css": { + "parser": { + "tailwindDirectives": true + } + } +} diff --git a/components.json b/components.json index 5794adb..8f5315a 100644 --- a/components.json +++ b/components.json @@ -1,25 +1,25 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "base-vega", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "iconLibrary": "lucide", - "rtl": false, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "menuColor": "default", - "menuAccent": "subtle", - "registries": {} + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-vega", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} } diff --git a/components/site-footer.tsx b/components/site-footer.tsx index 1cdcb10..418768c 100644 --- a/components/site-footer.tsx +++ b/components/site-footer.tsx @@ -1,17 +1,17 @@ export function SiteFooter() { - return ( - - ); -} \ No newline at end of file + return ( + + ); +} diff --git a/components/site-header.tsx b/components/site-header.tsx index 9611f81..129839f 100644 --- a/components/site-header.tsx +++ b/components/site-header.tsx @@ -1,30 +1,30 @@ import Link from "next/link"; export function SiteHeader() { - return ( -
-
- - Takasumi-Neodyマイクラサーバプロジェクト - - -
-
- ); + return ( +
+
+ + Takasumi-Neodyマイクラサーバプロジェクト + + +
+
+ ); } diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index b20959d..20f6061 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,52 +1,52 @@ -import { mergeProps } from "@base-ui/react/merge-props" -import { useRender } from "@base-ui/react/use-render" -import { cva, type VariantProps } from "class-variance-authority" +import { mergeProps } from "@base-ui/react/merge-props"; +import { useRender } from "@base-ui/react/use-render"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const badgeVariants = cva( - "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", - secondary: - "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", - destructive: - "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", - outline: - "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", - ghost: - "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", - link: "text-primary underline-offset-4 hover:underline", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", + secondary: + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", + destructive: + "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", + outline: + "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); function Badge({ - className, - variant = "default", - render, - ...props + className, + variant = "default", + render, + ...props }: useRender.ComponentProps<"span"> & VariantProps) { - return useRender({ - defaultTagName: "span", - props: mergeProps<"span">( - { - className: cn(badgeVariants({ variant }), className), - }, - props - ), - render, - state: { - slot: "badge", - variant, - }, - }) + return useRender({ + defaultTagName: "span", + props: mergeProps<"span">( + { + className: cn(badgeVariants({ variant }), className), + }, + props, + ), + render, + state: { + slot: "badge", + variant, + }, + }); } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index c1332b1..8357c2e 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -1,60 +1,60 @@ -"use client" +"use client"; -import { Button as ButtonPrimitive } from "@base-ui/react/button" -import { cva, type VariantProps } from "class-variance-authority" +import { Button as ButtonPrimitive } from "@base-ui/react/button"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", - { - variants: { - variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/80", - outline: - "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", - ghost: - "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", - destructive: - "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: - "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", - sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5", - lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", - icon: "size-9", - "icon-xs": - "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3", - "icon-sm": - "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md", - "icon-lg": "size-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) + "group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/80", + outline: + "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", + ghost: + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", + destructive: + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: + "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5", + lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", + icon: "size-9", + "icon-xs": + "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": + "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); function Button({ - className, - variant = "default", - size = "default", - ...props + className, + variant = "default", + size = "default", + ...props }: ButtonPrimitive.Props & VariantProps) { - return ( - - ) + return ( + + ); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/components/ui/card.tsx b/components/ui/card.tsx index bdb5bde..eb29b5e 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -1,103 +1,103 @@ -import * as React from "react" +import * as React from "react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Card({ - className, - size = "default", - ...props + className, + size = "default", + ...props }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) { - return ( -
img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", - className - )} - {...props} - /> - ) + return ( +
img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", + className, + )} + {...props} + /> + ); } function CardHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function CardTitle({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function CardDescription({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function CardAction({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function CardContent({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } function CardFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) + return ( +
+ ); } export { - Card, - CardHeader, - CardFooter, - CardTitle, - CardAction, - CardDescription, - CardContent, -} + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +}; diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx index 6e1369e..d59473a 100644 --- a/components/ui/separator.tsx +++ b/components/ui/separator.tsx @@ -1,25 +1,25 @@ -"use client" +"use client"; -import { Separator as SeparatorPrimitive } from "@base-ui/react/separator" +import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Separator({ - className, - orientation = "horizontal", - ...props + className, + orientation = "horizontal", + ...props }: SeparatorPrimitive.Props) { - return ( - - ) + return ( + + ); } -export { Separator } +export { Separator }; diff --git a/eslint.config.mjs b/eslint.config.mjs index 05e726d..74f101b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -3,16 +3,16 @@ import nextVitals from "eslint-config-next/core-web-vitals"; import nextTs from "eslint-config-next/typescript"; const eslintConfig = defineConfig([ - ...nextVitals, - ...nextTs, - // Override default ignores of eslint-config-next. - globalIgnores([ - // Default ignores of eslint-config-next: - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ]), + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), ]); export default eslintConfig; diff --git a/lib/announcements.ts b/lib/announcements.ts index 3f62b1d..03109c0 100644 --- a/lib/announcements.ts +++ b/lib/announcements.ts @@ -6,88 +6,95 @@ import { markdownToHtml } from "@/lib/markdown"; const ANNOUNCEMENTS_DIR = path.join(process.cwd(), "content", "announcements"); type AnnouncementFrontmatter = { - title?: string; - date?: string; - summary?: string; + title?: string; + date?: string; + summary?: string; }; export type AnnouncementListItem = { - slug: string; - title: string; - date: string; - summary: string; + slug: string; + title: string; + date: string; + summary: string; }; export type AnnouncementDetail = AnnouncementListItem & { - contentHtml: string; + contentHtml: string; }; function parseFrontmatter(markdown: string): { - frontmatter: AnnouncementFrontmatter; - body: string; + frontmatter: AnnouncementFrontmatter; + body: string; } { - if (!markdown.startsWith("---\n")) { - return { frontmatter: {}, body: markdown }; - } + if (!markdown.startsWith("---\n")) { + return { frontmatter: {}, body: markdown }; + } - const end = markdown.indexOf("\n---\n", 4); - if (end === -1) { - return { frontmatter: {}, body: markdown }; - } + const end = markdown.indexOf("\n---\n", 4); + if (end === -1) { + return { frontmatter: {}, body: markdown }; + } - const rawFrontmatter = markdown.slice(4, end); - const body = markdown.slice(end + 5); - const frontmatter: AnnouncementFrontmatter = {}; + const rawFrontmatter = markdown.slice(4, end); + const body = markdown.slice(end + 5); + const frontmatter: AnnouncementFrontmatter = {}; - for (const line of rawFrontmatter.split("\n")) { - const delimiterIndex = line.indexOf(":"); - if (delimiterIndex <= 0) { - continue; - } + for (const line of rawFrontmatter.split("\n")) { + const delimiterIndex = line.indexOf(":"); + if (delimiterIndex <= 0) { + continue; + } - const key = line.slice(0, delimiterIndex).trim(); - const value = line.slice(delimiterIndex + 1).trim().replace(/^"|"$/g, ""); + const key = line.slice(0, delimiterIndex).trim(); + const value = line + .slice(delimiterIndex + 1) + .trim() + .replace(/^"|"$/g, ""); - if (key === "title" || key === "date" || key === "summary") { - frontmatter[key] = value; - } - } + if (key === "title" || key === "date" || key === "summary") { + frontmatter[key] = value; + } + } - return { frontmatter, body: body.trim() }; + return { frontmatter, body: body.trim() }; } export async function getAnnouncementSlugs(): Promise { - const entries = await fs.readdir(ANNOUNCEMENTS_DIR, { withFileTypes: true }); + const entries = await fs.readdir(ANNOUNCEMENTS_DIR, { withFileTypes: true }); - return entries - .filter((entry) => entry.isFile() && entry.name.endsWith(".md")) - .map((entry) => entry.name.replace(/\.md$/, "")); + return entries + .filter((entry) => entry.isFile() && entry.name.endsWith(".md")) + .map((entry) => entry.name.replace(/\.md$/, "")); } -export async function getAnnouncementBySlug(slug: string): Promise { - const filePath = path.join(ANNOUNCEMENTS_DIR, `${slug}.md`); - const markdown = await fs.readFile(filePath, "utf-8"); - const { frontmatter, body } = parseFrontmatter(markdown); +export async function getAnnouncementBySlug( + slug: string, +): Promise { + const filePath = path.join(ANNOUNCEMENTS_DIR, `${slug}.md`); + const markdown = await fs.readFile(filePath, "utf-8"); + const { frontmatter, body } = parseFrontmatter(markdown); - return { - slug, - title: frontmatter.title ?? slug, - date: frontmatter.date ?? "日付未設定", - summary: frontmatter.summary ?? "", - contentHtml: markdownToHtml(body), - }; + return { + slug, + title: frontmatter.title ?? slug, + date: frontmatter.date ?? "日付未設定", + summary: frontmatter.summary ?? "", + contentHtml: markdownToHtml(body), + }; } export async function getAllAnnouncements(): Promise { - const slugs = await getAnnouncementSlugs(); - const items = await Promise.all(slugs.map((slug) => getAnnouncementBySlug(slug))); + const slugs = await getAnnouncementSlugs(); + const items = await Promise.all( + slugs.map((slug) => getAnnouncementBySlug(slug)), + ); - return items - .map((item) => ({ - slug: item.slug, - title: item.title, - date: item.date, - summary: item.summary, - })) - .sort((a, b) => b.date.localeCompare(a.date)); + return items + .map((item) => ({ + slug: item.slug, + title: item.title, + date: item.date, + summary: item.summary, + })) + .sort((a, b) => b.date.localeCompare(a.date)); } diff --git a/lib/markdown.ts b/lib/markdown.ts index 3491868..75db90d 100644 --- a/lib/markdown.ts +++ b/lib/markdown.ts @@ -1,144 +1,155 @@ type ParseState = { - inCodeBlock: boolean; - codeLang: string; - paragraphBuffer: string[]; - listType: "ul" | "ol" | null; - listBuffer: string[]; + inCodeBlock: boolean; + codeLang: string; + paragraphBuffer: string[]; + listType: "ul" | "ol" | null; + listBuffer: string[]; }; function escapeHtml(value: string): string { - return value - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); + return value + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); } function applyInlineMarkdown(value: string): string { - const escaped = escapeHtml(value); + const escaped = escapeHtml(value); - return escaped - .replace(/`([^`]+)`/g, "$1") - .replace(/\*\*([^*]+)\*\*/g, "$1") - .replace(/\*([^*]+)\*/g, "$1") - .replace(/~~([^~]+)~~/g, "$1") - .replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '$1'); + return escaped + .replace(/`([^`]+)`/g, "$1") + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/\*([^*]+)\*/g, "$1") + .replace(/~~([^~]+)~~/g, "$1") + .replace( + /\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, + '$1', + ); } function flushParagraph(state: ParseState, html: string[]): void { - if (state.paragraphBuffer.length === 0) { - return; - } + if (state.paragraphBuffer.length === 0) { + return; + } - html.push(`

${applyInlineMarkdown(state.paragraphBuffer.join(" "))}

`); - state.paragraphBuffer = []; + html.push(`

${applyInlineMarkdown(state.paragraphBuffer.join(" "))}

`); + state.paragraphBuffer = []; } function flushList(state: ParseState, html: string[]): void { - if (!state.listType || state.listBuffer.length === 0) { - return; - } + if (!state.listType || state.listBuffer.length === 0) { + return; + } - html.push(`<${state.listType}>${state.listBuffer.join("")}`); - state.listType = null; - state.listBuffer = []; + html.push( + `<${state.listType}>${state.listBuffer.join("")}`, + ); + state.listType = null; + state.listBuffer = []; } export function markdownToHtml(markdown: string): string { - const lines = markdown.replace(/\r\n/g, "\n").split("\n"); - const html: string[] = []; - const state: ParseState = { - inCodeBlock: false, - codeLang: "", - paragraphBuffer: [], - listType: null, - listBuffer: [], - }; + const lines = markdown.replace(/\r\n/g, "\n").split("\n"); + const html: string[] = []; + const state: ParseState = { + inCodeBlock: false, + codeLang: "", + paragraphBuffer: [], + listType: null, + listBuffer: [], + }; - for (const rawLine of lines) { - const line = rawLine.trimEnd(); + for (const rawLine of lines) { + const line = rawLine.trimEnd(); - if (line.startsWith("```")) { - flushParagraph(state, html); - flushList(state, html); + if (line.startsWith("```")) { + flushParagraph(state, html); + flushList(state, html); - if (!state.inCodeBlock) { - state.inCodeBlock = true; - state.codeLang = line.slice(3).trim(); - const className = state.codeLang ? ` class="language-${escapeHtml(state.codeLang)}"` : ""; - html.push(`
`);
-      } else {
-        state.inCodeBlock = false;
-        state.codeLang = "";
-        html.push("
"); - } - continue; - } + if (!state.inCodeBlock) { + state.inCodeBlock = true; + state.codeLang = line.slice(3).trim(); + const className = state.codeLang + ? ` class="language-${escapeHtml(state.codeLang)}"` + : ""; + html.push(`
`);
+			} else {
+				state.inCodeBlock = false;
+				state.codeLang = "";
+				html.push("
"); + } + continue; + } - if (state.inCodeBlock) { - html.push(`${escapeHtml(rawLine)}\n`); - continue; - } + if (state.inCodeBlock) { + html.push(`${escapeHtml(rawLine)}\n`); + continue; + } - if (line.length === 0) { - flushParagraph(state, html); - flushList(state, html); - continue; - } + if (line.length === 0) { + flushParagraph(state, html); + flushList(state, html); + continue; + } - const headingMatch = line.match(/^(#{1,6})\s+(.+)$/); - if (headingMatch) { - flushParagraph(state, html); - flushList(state, html); - const level = headingMatch[1].length; - html.push(`${applyInlineMarkdown(headingMatch[2])}`); - continue; - } + const headingMatch = line.match(/^(#{1,6})\s+(.+)$/); + if (headingMatch) { + flushParagraph(state, html); + flushList(state, html); + const level = headingMatch[1].length; + html.push( + `${applyInlineMarkdown(headingMatch[2])}`, + ); + continue; + } - if (/^---$/.test(line)) { - flushParagraph(state, html); - flushList(state, html); - html.push("
"); - continue; - } + if (/^---$/.test(line)) { + flushParagraph(state, html); + flushList(state, html); + html.push("
"); + continue; + } - const blockquoteMatch = line.match(/^>\s?(.*)$/); - if (blockquoteMatch) { - flushParagraph(state, html); - flushList(state, html); - html.push(`

${applyInlineMarkdown(blockquoteMatch[1])}

`); - continue; - } + const blockquoteMatch = line.match(/^>\s?(.*)$/); + if (blockquoteMatch) { + flushParagraph(state, html); + flushList(state, html); + html.push( + `

${applyInlineMarkdown(blockquoteMatch[1])}

`, + ); + continue; + } - const ulMatch = line.match(/^[-*+]\s+(.+)$/); - if (ulMatch) { - flushParagraph(state, html); - if (state.listType !== "ul") { - flushList(state, html); - state.listType = "ul"; - } - state.listBuffer.push(`
  • ${applyInlineMarkdown(ulMatch[1])}
  • `); - continue; - } + const ulMatch = line.match(/^[-*+]\s+(.+)$/); + if (ulMatch) { + flushParagraph(state, html); + if (state.listType !== "ul") { + flushList(state, html); + state.listType = "ul"; + } + state.listBuffer.push(`
  • ${applyInlineMarkdown(ulMatch[1])}
  • `); + continue; + } - const olMatch = line.match(/^\d+\.\s+(.+)$/); - if (olMatch) { - flushParagraph(state, html); - if (state.listType !== "ol") { - flushList(state, html); - state.listType = "ol"; - } - state.listBuffer.push(`
  • ${applyInlineMarkdown(olMatch[1])}
  • `); - continue; - } + const olMatch = line.match(/^\d+\.\s+(.+)$/); + if (olMatch) { + flushParagraph(state, html); + if (state.listType !== "ol") { + flushList(state, html); + state.listType = "ol"; + } + state.listBuffer.push(`
  • ${applyInlineMarkdown(olMatch[1])}
  • `); + continue; + } - flushList(state, html); - state.paragraphBuffer.push(line.trim()); - } + flushList(state, html); + state.paragraphBuffer.push(line.trim()); + } - flushParagraph(state, html); - flushList(state, html); + flushParagraph(state, html); + flushList(state, html); - return html.join("\n"); + return html.join("\n"); } diff --git a/lib/utils.ts b/lib/utils.ts index bd0c391..3200be2 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,6 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/next.config.ts b/next.config.ts index a18b38e..06d17fa 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,8 +1,8 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - output: "export", - trailingSlash: true, + output: "export", + trailingSlash: true, }; export default nextConfig; diff --git a/package.json b/package.json index 6325745..0b2d6fc 100644 --- a/package.json +++ b/package.json @@ -1,43 +1,43 @@ { - "name": "minecraft-server-web", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "eslint" - }, - "dependencies": { - "@base-ui/react": "^1.3.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "gray-matter": "^4.0.3", - "lucide-react": "^1.7.0", - "marked": "^17.0.5", - "next": "16.2.1", - "react": "19.2.4", - "react-dom": "19.2.4", - "shadcn": "^4.1.1", - "tailwind-merge": "^3.5.0", - "tw-animate-css": "^1.4.0" - }, - "devDependencies": { - "@tailwindcss/postcss": "^4", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "16.2.1", - "tailwindcss": "^4", - "typescript": "^5" - }, - "ignoreScripts": [ - "sharp", - "unrs-resolver" - ], - "trustedDependencies": [ - "sharp", - "unrs-resolver" - ] + "name": "minecraft-server-web", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "@base-ui/react": "^1.3.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "gray-matter": "^4.0.3", + "lucide-react": "^1.7.0", + "marked": "^17.0.5", + "next": "16.2.1", + "react": "19.2.4", + "react-dom": "19.2.4", + "shadcn": "^4.1.1", + "tailwind-merge": "^3.5.0", + "tw-animate-css": "^1.4.0" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.1", + "tailwindcss": "^4", + "typescript": "^5" + }, + "ignoreScripts": [ + "sharp", + "unrs-resolver" + ], + "trustedDependencies": [ + "sharp", + "unrs-resolver" + ] } diff --git a/postcss.config.mjs b/postcss.config.mjs index 61e3684..c42f31c 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,7 +1,7 @@ const config = { - plugins: { - "@tailwindcss/postcss": {}, - }, + plugins: { + "@tailwindcss/postcss": {}, + }, }; export default config; diff --git a/tsconfig.json b/tsconfig.json index 3a13f90..6b4686a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,34 +1,34 @@ { - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "react-jsx", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./*"] - } - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx", - ".next/types/**/*.ts", - ".next/dev/types/**/*.ts", - "**/*.mts" - ], - "exclude": ["node_modules"] + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] }