feat: add content system with MDX support, types, and meta files for 4 games
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
8
content/maplestory/meta.json
Normal file
8
content/maplestory/meta.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "MapleStory",
|
||||||
|
"subtitle": "El Mundo del Arce",
|
||||||
|
"description": "Bajo la sombra del Arbol del Mundo, heroes de todas las eras se alzan contra la oscuridad del Mago Negro en un mundo vibrante lleno de aventura y misterio.",
|
||||||
|
"color": "#2d5a1e",
|
||||||
|
"colorDark": "#1a3a12",
|
||||||
|
"accentColor": "#ff9b2e"
|
||||||
|
}
|
||||||
8
content/ragnarok/meta.json
Normal file
8
content/ragnarok/meta.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "Ragnarok Online",
|
||||||
|
"subtitle": "La Saga de Midgard",
|
||||||
|
"description": "En un mundo donde dioses y mortales coexisten, la tierra de Midgard se enfrenta a amenazas que podrian desatar el Ragnarok, el fin de todas las cosas.",
|
||||||
|
"color": "#8b2500",
|
||||||
|
"colorDark": "#5c1a00",
|
||||||
|
"accentColor": "#ff8c42"
|
||||||
|
}
|
||||||
8
content/tales-of-pirates/meta.json
Normal file
8
content/tales-of-pirates/meta.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "Tales of Pirates",
|
||||||
|
"subtitle": "Leyendas del Mar",
|
||||||
|
"description": "En un mundo de islas infinitas y mares inexplorados, piratas, aventureros y cazadores de tesoros escriben su legenda en las aguas del destino.",
|
||||||
|
"color": "#1a3050",
|
||||||
|
"colorDark": "#0f1e35",
|
||||||
|
"accentColor": "#d4a843"
|
||||||
|
}
|
||||||
8
content/wow/meta.json
Normal file
8
content/wow/meta.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"title": "World of Warcraft",
|
||||||
|
"subtitle": "Cronicas de Azeroth",
|
||||||
|
"description": "Desde la creacion del universo por los Titanes hasta las guerras que definieron el destino de Azeroth, esta es la historia epica del mundo mas grande jamas creado en un MMORPG.",
|
||||||
|
"color": "#1a3a5c",
|
||||||
|
"colorDark": "#0f2440",
|
||||||
|
"accentColor": "#d4a843"
|
||||||
|
}
|
||||||
93
src/lib/content.ts
Normal file
93
src/lib/content.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import matter from "gray-matter";
|
||||||
|
import { GameMeta, Chapter, GameWithChapters } from "@/types";
|
||||||
|
|
||||||
|
const contentDir = path.join(process.cwd(), "content");
|
||||||
|
|
||||||
|
export function getAllGames(): GameMeta[] {
|
||||||
|
const gameDirs = fs.readdirSync(contentDir).filter((dir) => {
|
||||||
|
const fullPath = path.join(contentDir, dir);
|
||||||
|
return fs.statSync(fullPath).isDirectory();
|
||||||
|
});
|
||||||
|
|
||||||
|
return gameDirs.map((slug) => {
|
||||||
|
const metaPath = path.join(contentDir, slug, "meta.json");
|
||||||
|
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
||||||
|
return { slug, ...meta };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGame(slug: string): GameWithChapters {
|
||||||
|
const metaPath = path.join(contentDir, slug, "meta.json");
|
||||||
|
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
||||||
|
|
||||||
|
const gameDir = path.join(contentDir, slug);
|
||||||
|
const chapterFiles = fs
|
||||||
|
.readdirSync(gameDir)
|
||||||
|
.filter((f) => f.endsWith(".mdx"))
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
const chapters = chapterFiles.map((file, index) => {
|
||||||
|
const filePath = path.join(gameDir, file);
|
||||||
|
const raw = fs.readFileSync(filePath, "utf-8");
|
||||||
|
const { data } = matter(raw);
|
||||||
|
const chapterSlug = file.replace(".mdx", "");
|
||||||
|
|
||||||
|
return {
|
||||||
|
slug: chapterSlug,
|
||||||
|
number: index + 1,
|
||||||
|
title: (data.title as string) || chapterSlug,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { slug, ...meta, chapters };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getChapter(gameSlug: string, chapterSlug: string): Chapter {
|
||||||
|
const gameDir = path.join(contentDir, gameSlug);
|
||||||
|
const chapterFiles = fs
|
||||||
|
.readdirSync(gameDir)
|
||||||
|
.filter((f) => f.endsWith(".mdx"))
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
const chapterIndex = chapterFiles.findIndex(
|
||||||
|
(f) => f.replace(".mdx", "") === chapterSlug
|
||||||
|
);
|
||||||
|
const filePath = path.join(gameDir, `${chapterSlug}.mdx`);
|
||||||
|
const raw = fs.readFileSync(filePath, "utf-8");
|
||||||
|
const { data, content } = matter(raw);
|
||||||
|
|
||||||
|
return {
|
||||||
|
slug: chapterSlug,
|
||||||
|
number: chapterIndex + 1,
|
||||||
|
title: (data.title as string) || chapterSlug,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAdjacentChapters(
|
||||||
|
gameSlug: string,
|
||||||
|
chapterSlug: string
|
||||||
|
): { prev: string | null; next: string | null } {
|
||||||
|
const gameDir = path.join(contentDir, gameSlug);
|
||||||
|
const chapterFiles = fs
|
||||||
|
.readdirSync(gameDir)
|
||||||
|
.filter((f) => f.endsWith(".mdx"))
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
const currentIndex = chapterFiles.findIndex(
|
||||||
|
(f) => f.replace(".mdx", "") === chapterSlug
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prev:
|
||||||
|
currentIndex > 0
|
||||||
|
? chapterFiles[currentIndex - 1].replace(".mdx", "")
|
||||||
|
: null,
|
||||||
|
next:
|
||||||
|
currentIndex < chapterFiles.length - 1
|
||||||
|
? chapterFiles[currentIndex + 1].replace(".mdx", "")
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
21
src/types/index.ts
Normal file
21
src/types/index.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export interface GameMeta {
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
description: string;
|
||||||
|
color: string;
|
||||||
|
colorDark: string;
|
||||||
|
accentColor: string;
|
||||||
|
coverImage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Chapter {
|
||||||
|
slug: string;
|
||||||
|
number: number;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameWithChapters extends GameMeta {
|
||||||
|
chapters: Omit<Chapter, "content">[];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user