Updated changelog versioning
This commit is contained in:
parent
8369d59310
commit
af8c720ba0
|
|
@ -1,79 +1,31 @@
|
||||||
"use client";
|
import ForgeUI from "@/components/ForgeUI";
|
||||||
import { Hammer, History, Target, Code2 } from "lucide-react";
|
|
||||||
import PageLayout from "@/components/PageLayout";
|
import PageLayout from "@/components/PageLayout";
|
||||||
|
import { getAllForgeProjects } from "@/lib/git";
|
||||||
|
import { Hammer } from "lucide-react";
|
||||||
|
|
||||||
|
export default async function ForgePage() {
|
||||||
|
const projects = await getAllForgeProjects();
|
||||||
|
|
||||||
export default function ForgePage() {
|
|
||||||
return (
|
return (
|
||||||
<PageLayout backLink="/" maxWidth="5xl">
|
<PageLayout backLink="/" maxWidth="5xl">
|
||||||
<header className="mb-20">
|
<header className="mb-20">
|
||||||
<div className="flex items-center gap-3 mb-4">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<Hammer className="text-orange-500 animate-pulse" size={24} />
|
<Hammer className="text-orange-500 animate-pulse" size={24} />
|
||||||
<h1 className="text-4xl font-bold tracking-tighter text-white uppercase">
|
<h1 className="text-4xl font-bold tracking-tighter text-white uppercase font-mono">
|
||||||
TheForge
|
The Forge
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-neutral-500 max-w-2xl leading-relaxed text-sm font-mono">
|
<p className="text-neutral-500 max-w-2xl text-sm font-mono leading-relaxed">
|
||||||
<br />
|
The Forge is my active development logs, providing a glimpse into my
|
||||||
The Forge is where I document my current engineering focus. This space
|
current projects and future potential releases.
|
||||||
reflects active builds, experimental prototypes and gives a taste of
|
|
||||||
my next releases.
|
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Main Project Card */}
|
<div className="space-y-24">
|
||||||
<section className="bg-neutral-900/40 border border-neutral-800 rounded-2xl p-8 mb-16">
|
{projects.map((project) => (
|
||||||
<div className="flex flex-wrap items-center justify-between gap-4 mb-8">
|
<ForgeUI key={project.id} project={project} />
|
||||||
<div>
|
))}
|
||||||
<h2 className="text-2xl font-bold text-white mb-2 italic">
|
</div>
|
||||||
Project: PixelPals
|
|
||||||
</h2>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<span className="text-[10px] text-orange-500 font-bold uppercase tracking-[0.2em]">
|
|
||||||
Build Phase: Alpha 0.1
|
|
||||||
</span>
|
|
||||||
<span className="text-[10px] text-neutral-600 font-bold uppercase tracking-[0.2em]">
|
|
||||||
Engine: Godot / GDScript
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="px-4 py-2 bg-orange-500/10 border border-orange-500/20 rounded-full">
|
|
||||||
<span className="text-[10px] text-orange-500 font-bold uppercase tracking-widest">
|
|
||||||
Active Focus
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-neutral-400 text-sm leading-relaxed mb-8">
|
|
||||||
Exploring procedural generation and state-machine-driven AI behaviors.
|
|
||||||
The goal is to bridge my experience in systems architecture with
|
|
||||||
real-time game loops and interactive storytelling.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Milestones / Changelog */}
|
|
||||||
<div className="space-y-6">
|
|
||||||
<h3 className="flex items-center gap-2 text-xs font-bold text-neutral-500 uppercase tracking-widest border-b border-neutral-800 pb-2">
|
|
||||||
<History size={14} /> Development_Log
|
|
||||||
</h3>
|
|
||||||
<ul className="space-y-4">
|
|
||||||
<li className="flex gap-4">
|
|
||||||
<span className="text-xs text-neutral-600 font-mono mt-1 shrink-0">
|
|
||||||
2026-02-02
|
|
||||||
</span>
|
|
||||||
<p className="text-xs text-neutral-400">
|
|
||||||
Implemented dynamic weather patterns using custom shaders.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
<li className="flex gap-4">
|
|
||||||
<span className="text-xs text-neutral-600 font-mono mt-1 shrink-0">
|
|
||||||
2026-01-28
|
|
||||||
</span>
|
|
||||||
<p className="text-xs text-neutral-400">
|
|
||||||
Optimized asset loading pipeline for faster initial scene entry.
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
131
components/ForgeUI copy.tsx
Normal file
131
components/ForgeUI copy.tsx
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import { History, ExternalLink, Cpu } from "lucide-react";
|
||||||
|
import { ForgeProject } from "@/types";
|
||||||
|
|
||||||
|
export default function ForgeUI({ project }: { project: ForgeProject }) {
|
||||||
|
return (
|
||||||
|
<div className="bg-neutral-900/40 border border-neutral-800 rounded-3xl p-8 pt-0 mb-12 overflow-hidden">
|
||||||
|
{/* Upper Section: Hero Area */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||||
|
<div className="lg:col-span-8 flex flex-col justify-center">
|
||||||
|
<div className="flex flex-wrap justify-between items-start gap-6 mb-6">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4 tracking-tighter">
|
||||||
|
{project.projectName}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
{/* Version Badge */}
|
||||||
|
<span className="px-3 py-1 bg-orange-500/10 border border-orange-500/20 text-orange-500 text-[10px] font-bold uppercase tracking-widest rounded-full">
|
||||||
|
{project.currentVersion}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Engine Tag */}
|
||||||
|
<span className="px-3 py-1 bg-neutral-800 text-neutral-400 text-[10px] font-bold uppercase tracking-widest rounded-full flex items-center gap-2">
|
||||||
|
<Cpu size={12} /> {project.engine}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
{/* Status Indicator (Now part of the tag row) */}
|
||||||
|
<div className="flex items-center gap-2 text-[9px] font-mono text-green-500/60 ml-1">
|
||||||
|
<span className="relative flex h-1.5 w-1.5">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||||
|
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-green-500"></span>
|
||||||
|
</span>
|
||||||
|
ACTIVE STREAM
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{project.externalLink && (
|
||||||
|
<a
|
||||||
|
href={project.externalLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-xs font-bold text-white bg-neutral-800 hover:bg-neutral-700 transition-colors px-4 py-2 rounded-xl shrink-0"
|
||||||
|
>
|
||||||
|
Project Details <ExternalLink size={14} />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-neutral-400 text-base leading-relaxed max-w-2xl">
|
||||||
|
{project.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{project.imagePath && (
|
||||||
|
<div className="lg:col-span-4 flex justify-center lg:justify-end items-center">
|
||||||
|
<div className="relative w-full aspect-square max-w-[280px] group">
|
||||||
|
<Image
|
||||||
|
src={project.imagePath}
|
||||||
|
alt={`${project.projectName} Feature`}
|
||||||
|
fill
|
||||||
|
className="object-contain drop-shadow-[0_0_40px_rgba(249,115,22,0.15)] transition-transform duration-700 group-hover:scale-110"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-orange-500/10 blur-[80px] rounded-full -z-10 opacity-40 group-hover:opacity-60 transition-opacity" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Technical Highlights */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-12">
|
||||||
|
{project.highlights.map((tag) => (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="text-[10px] text-neutral-600 font-mono uppercase tracking-tighter border border-neutral-800 px-3 py-1 rounded"
|
||||||
|
>
|
||||||
|
{`// ${tag}`}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lower Section: Logs with Gradient Fade */}
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="flex items-center gap-2 text-xs font-bold text-neutral-500 uppercase tracking-widest border-b border-neutral-800 pb-2">
|
||||||
|
<History size={14} /> Dev_Log
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* This container handles the fade effect */}
|
||||||
|
<div
|
||||||
|
className="space-y-8 relative"
|
||||||
|
style={{
|
||||||
|
maskImage:
|
||||||
|
"linear-gradient(to bottom, black 60%, transparent 100%)",
|
||||||
|
WebkitMaskImage:
|
||||||
|
"linear-gradient(to bottom, black 60%, transparent 100%)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{project.changelog.map((entry) => (
|
||||||
|
<div
|
||||||
|
key={entry.version}
|
||||||
|
className="relative pl-8 border-l border-neutral-800"
|
||||||
|
>
|
||||||
|
<div className="absolute -left-1.5 top-1.5 w-3 h-3 bg-neutral-950 border border-neutral-700 rounded-full" />
|
||||||
|
<div className="flex items-baseline gap-4 mb-3">
|
||||||
|
<span className="text-orange-500 font-mono text-sm font-bold leading-none">
|
||||||
|
{entry.version}
|
||||||
|
</span>
|
||||||
|
<span className="text-neutral-600 font-mono text-xs leading-none">
|
||||||
|
{entry.date}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<ul className="space-y-2.5">
|
||||||
|
{entry.changes.map((change, idx) => (
|
||||||
|
<li
|
||||||
|
key={idx}
|
||||||
|
className="text-neutral-400 text-sm leading-relaxed max-w-4xl"
|
||||||
|
>
|
||||||
|
{change}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
136
components/ForgeUI.tsx
Normal file
136
components/ForgeUI.tsx
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import Image from "next/image";
|
||||||
|
import { History, ExternalLink, Cpu } from "lucide-react";
|
||||||
|
import { ForgeProject } from "@/types";
|
||||||
|
|
||||||
|
export default function ForgeUI({ project }: { project: ForgeProject }) {
|
||||||
|
return (
|
||||||
|
<div className="bg-neutral-900/40 border border-neutral-800 rounded-3xl p-8 pt-0 mb-12 overflow-hidden">
|
||||||
|
{/* Upper Section: Hero Area */}
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 pt-4">
|
||||||
|
{" "}
|
||||||
|
{/* Added pt-8 here to replace the p-8 pt-0 conflict */}
|
||||||
|
<div className="lg:col-span-8 flex flex-col justify-center">
|
||||||
|
<div className="flex flex-wrap justify-between items-start gap-6 mb-6">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4 tracking-tighter">
|
||||||
|
{project.projectName}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<span className="px-3 py-1 bg-orange-500/10 border border-orange-500/20 text-orange-500 text-[10px] font-bold uppercase tracking-widest rounded-full">
|
||||||
|
{project.currentVersion}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="px-3 py-1 bg-neutral-800 text-neutral-400 text-[10px] font-bold uppercase tracking-widest rounded-full flex items-center gap-2">
|
||||||
|
<Cpu size={12} /> {project.engine}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2 text-[9px] font-mono text-green-500/60 ml-1">
|
||||||
|
<span className="relative flex h-1.5 w-1.5">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
|
||||||
|
<span className="relative inline-flex rounded-full h-1.5 w-1.5 bg-green-500"></span>
|
||||||
|
</span>
|
||||||
|
ACTIVE STREAM
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{project.externalLink && (
|
||||||
|
<a
|
||||||
|
href={project.externalLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex items-center gap-2 text-xs font-bold text-white bg-neutral-800 hover:bg-neutral-700 transition-colors px-4 py-2 rounded-xl shrink-0"
|
||||||
|
>
|
||||||
|
Project Details <ExternalLink size={14} />
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-neutral-400 text-base leading-relaxed max-w-2xl">
|
||||||
|
{project.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{project.imagePath && (
|
||||||
|
<div className="lg:col-span-4 flex justify-center lg:justify-end items-center">
|
||||||
|
<div className="relative w-full aspect-square max-w-[280px] group">
|
||||||
|
<Image
|
||||||
|
src={project.imagePath}
|
||||||
|
alt={`${project.projectName} Feature`}
|
||||||
|
fill
|
||||||
|
className="object-contain drop-shadow-[0_0_40px_rgba(249,115,22,0.15)] transition-transform duration-700 group-hover:scale-110"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-orange-500/10 blur-[80px] rounded-full -z-10 opacity-40 group-hover:opacity-60 transition-opacity" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Technical Highlights */}
|
||||||
|
<div className="flex flex-wrap gap-2 mb-12 mt-6">
|
||||||
|
{project.highlights.map((tag) => (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="text-[10px] text-neutral-600 font-mono uppercase tracking-tighter border border-neutral-800 px-3 py-1 rounded"
|
||||||
|
>
|
||||||
|
{`// ${tag}`}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Lower Section: Logs */}
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="flex items-center gap-2 text-xs font-bold text-neutral-500 uppercase tracking-widest border-b border-neutral-800 pb-2">
|
||||||
|
<History size={14} /> Dev_Log
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="space-y-10 relative pb-4" // Increased space between version blocks
|
||||||
|
style={{
|
||||||
|
maskImage:
|
||||||
|
"linear-gradient(to bottom, black 60%, transparent 100%)",
|
||||||
|
WebkitMaskImage:
|
||||||
|
"linear-gradient(to bottom, black 60%, transparent 100%)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{project.changelog.map((entry) => (
|
||||||
|
<div
|
||||||
|
key={entry.version}
|
||||||
|
className="ml-1.5 relative pl-10 border-l border-neutral-800" // Increased pl-8 to pl-10 to fix clipping
|
||||||
|
>
|
||||||
|
{/* The Version Indicator Circle */}
|
||||||
|
<div className="absolute -left-1.5 top-1 w-3 h-3 bg-neutral-950 border border-neutral-700 rounded-full" />
|
||||||
|
|
||||||
|
<div className="flex items-baseline gap-4 mb-4">
|
||||||
|
<span className="text-orange-500 font-mono text-sm font-bold leading-none">
|
||||||
|
{entry.version}
|
||||||
|
</span>
|
||||||
|
<span className="text-neutral-600 font-mono text-[10px] uppercase tracking-wider leading-none">
|
||||||
|
{entry.date}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Individual Changes with Bullet Points */}
|
||||||
|
<ul className="space-y-3">
|
||||||
|
{entry.changes.map((change, idx) => (
|
||||||
|
<li
|
||||||
|
key={idx}
|
||||||
|
className="text-neutral-400 text-sm leading-relaxed max-w-4xl flex items-start gap-3"
|
||||||
|
>
|
||||||
|
{/* The Bullet Character */}
|
||||||
|
<span className="text-orange-500/40 mt-1.5 shrink-0 text-[10px]">
|
||||||
|
•
|
||||||
|
</span>
|
||||||
|
<span>{change}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,21 @@
|
||||||
import { ForgeProject } from "@/types";
|
import { ForgeProjectMetadata } from "@/types";
|
||||||
|
|
||||||
export const ACTIVE_BUILD: ForgeProject = {
|
export const FORGE_PROJECTS: ForgeProjectMetadata[] = [
|
||||||
id: "pixelpals",
|
{
|
||||||
name: "PixelPals",
|
id: "pixelpals",
|
||||||
status: "Alpha",
|
projectName: "PixelPals",
|
||||||
engine: "Godot 4.x",
|
repoName: "pixel-pals",
|
||||||
description:
|
status: "Alpha",
|
||||||
"A mobile-first creature collection game focusing on procedural behaviors and lightweight state-management.",
|
engine: "Flutter / Firebase",
|
||||||
highlights: [
|
imagePath: "/forge/pixel-pals.png",
|
||||||
"State-machine AI",
|
description:
|
||||||
"Custom Shader Pipelines",
|
"A GPS-driven, cooperative creature collection game built to encourage exercise. Features turn-based tactical combat, class-based progression, and AI-generated companion art based on player descriptions.",
|
||||||
"Mobile-optimized Loops",
|
highlights: [
|
||||||
],
|
"GPS / Mapbox Integration",
|
||||||
externalLink: "/projects/pixelpals", // Link to your internal portfolio or a dedicated site
|
"Generative AI (Pet Synthesis)",
|
||||||
changelog: [
|
"Real-time Firebase Sync",
|
||||||
{
|
"Turn-based Battle Engine",
|
||||||
date: "2026-02-03",
|
],
|
||||||
update: "Refined touch-input latency for mobile devices.",
|
externalLink: "https://pixelpals.app",
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
date: "2026-01-25",
|
|
||||||
update: "Integrated local SQLite database for creature persistence.",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
|
||||||
91
lib/git.ts
Normal file
91
lib/git.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { FORGE_PROJECTS } from "@/data/forge";
|
||||||
|
import { ChangelogEntry, ForgeProject } from "@/types";
|
||||||
|
import "server-only";
|
||||||
|
|
||||||
|
export async function getAllForgeProjects(): Promise<ForgeProject[]> {
|
||||||
|
// We use Promise.all to fetch all changelogs in parallel for speed
|
||||||
|
return Promise.all(
|
||||||
|
FORGE_PROJECTS.map(async (metadata) => {
|
||||||
|
const changelog = await getPrivateChangelog(metadata.repoName);
|
||||||
|
const latestVersion =
|
||||||
|
changelog.length > 0 ? changelog[0].version : "0.0.0";
|
||||||
|
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
currentVersion: `${latestVersion}_${metadata.status}`,
|
||||||
|
changelog: changelog,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPrivateChangelog(
|
||||||
|
repoName: string,
|
||||||
|
): Promise<ChangelogEntry[]> {
|
||||||
|
const repoOwner = "georgew";
|
||||||
|
const filePath = "changelog.md";
|
||||||
|
const url = `https://git.georgew.dev/api/v1/repos/${repoOwner}/${repoName}/contents/${filePath}`;
|
||||||
|
const headers = {
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${process.env.FORGEJO_TOKEN}`,
|
||||||
|
Accept: "application/vnd.forgejo.raw",
|
||||||
|
},
|
||||||
|
next: { revalidate: 3600 },
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, headers);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
version: "v0.0.0",
|
||||||
|
date: new Date().toISOString().split("T")[0],
|
||||||
|
changes: ["Documentation currently synchronizing with the Forge..."],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// 2. Forgejo returns the content as a base64 string
|
||||||
|
// We need to decode it to UTF-8
|
||||||
|
const markdownText = Buffer.from(data.content, "base64").toString("utf-8");
|
||||||
|
|
||||||
|
return parseMarkdown(markdownText);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Git Fetch Error:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple parser for your ## [Version] - YYYY-MM-DD format
|
||||||
|
function parseMarkdown(text: string): ChangelogEntry[] {
|
||||||
|
// Split by "## " at the start of a line to isolate version blocks
|
||||||
|
const sections = text.split(/\n(?=## )/g);
|
||||||
|
|
||||||
|
return sections
|
||||||
|
.map((section) => {
|
||||||
|
const lines = section
|
||||||
|
.split("\n")
|
||||||
|
.map((l) => l.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
if (lines.length === 0) return null;
|
||||||
|
|
||||||
|
// Match the pattern: ## [Version] - YYYY-MM-DD
|
||||||
|
const headerMatch = lines[0].match(/## \[(.*?)\] - (.*)/);
|
||||||
|
if (!headerMatch) return null;
|
||||||
|
|
||||||
|
const version = headerMatch[1];
|
||||||
|
const date = headerMatch[2];
|
||||||
|
|
||||||
|
// Collect all lines starting with "- " anywhere in this version block
|
||||||
|
const changes = lines
|
||||||
|
.filter((line) => line.startsWith("- "))
|
||||||
|
.map((line) => line.replace("- ", ""));
|
||||||
|
|
||||||
|
return { version, date, changes };
|
||||||
|
})
|
||||||
|
.filter((entry): entry is ChangelogEntry => entry !== null)
|
||||||
|
.slice(0, 3);
|
||||||
|
}
|
||||||
BIN
public/forge/pixel-pals.png
Normal file
BIN
public/forge/pixel-pals.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
|
|
@ -50,11 +50,31 @@ export interface LabService {
|
||||||
|
|
||||||
export interface ForgeProject {
|
export interface ForgeProject {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
|
||||||
status: "Alpha" | "Beta" | "R&D";
|
status: "Alpha" | "Beta" | "R&D";
|
||||||
engine: string;
|
engine: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
imagePath?: string;
|
||||||
highlights: string[];
|
highlights: string[];
|
||||||
externalLink?: string;
|
externalLink?: string;
|
||||||
changelog: { date: string; update: string }[];
|
projectName: string;
|
||||||
|
currentVersion: string; // Dynamically parsed
|
||||||
|
changelog: ChangelogEntry[]; // Dynamically fetched
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ForgeProjectMetadata {
|
||||||
|
id: string;
|
||||||
|
projectName: string;
|
||||||
|
repoName: string;
|
||||||
|
status: "Alpha" | "Beta" | "R&D";
|
||||||
|
engine: string;
|
||||||
|
imagePath?: string;
|
||||||
|
description: string;
|
||||||
|
highlights: string[];
|
||||||
|
externalLink?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChangelogEntry {
|
||||||
|
version: string;
|
||||||
|
date: string;
|
||||||
|
changes: string[];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue