Merge Docusaurus site: all content under docs/, align with gregFramework split layout

- Move markdown and wiki-import tree into docs/; keep app shell at repo root
- Point docusaurus docs path to docs/; edit links to mleem97/gregWiki
- Sync and i18n scripts use gregWiki root and ../.wiki under gregFramework
- Sidebars: workspace layout from root docs ids; plugins under mods/extensions
- Fix redirects, module catalog URLs, release note paths, and wiki-import category keys
- Update repo inventory for split repos; Dockerfile for single-repo context

Made-with: Cursor
This commit is contained in:
Marvin
2026-04-09 23:39:32 +02:00
parent 33636fde00
commit e2839584f4
320 changed files with 38803 additions and 97 deletions

View File

@@ -0,0 +1,159 @@
import React from 'react';
import Link from '@docusaurus/Link';
import Translate from '@docusaurus/Translate';
type ReleaseKind = 'mod' | 'plugin';
type ModReleasePageProps = {
title: string;
kind: ReleaseKind;
dllName: string;
description: string;
version?: string;
author?: string;
category?: string;
dependencies?: string[];
codeLanguages?: string[];
releaseReady?: boolean;
banner?: string;
releaseNotesPath?: string;
/** If set, replaces the default installation steps (e.g. MelonLoader `Plugins/` vs `Mods/`). */
installation?: React.ReactNode;
};
function toTitleCase(value: ReleaseKind): string {
return value.charAt(0).toUpperCase() + value.slice(1);
}
export default function ModReleasePage({
title,
kind,
dllName,
description,
version = 'NotReleasedYet',
author = 'FrikaMF Community',
category = 'Mod',
dependencies = [],
codeLanguages = ['C#'],
releaseReady = false,
banner,
releaseNotesPath,
installation,
}: ModReleasePageProps): JSX.Element {
const downloadPath = `/${kind}/${dllName}`;
const downloadUrl =
typeof window !== 'undefined' ? new URL(downloadPath, window.location.origin).toString() : downloadPath;
const normalizedReleaseNotesPath = releaseNotesPath?.startsWith('/mods/')
? `/wiki${releaseNotesPath}`
: releaseNotesPath;
return (
<div className="mod-release-page">
<section className="mod-release-hero">
<p className="mod-release-badge">{toTitleCase(kind)}</p>
<h1>{title}</h1>
<p>
{banner ?? (
<Translate id="modRelease.heroDefaultBanner">Official release download and module details</Translate>
)}
</p>
</section>
<section className="mod-release-layout">
<article className="mod-release-content">
<h2>
<Translate id="modRelease.descriptionTitle">Description</Translate>
</h2>
<p>{description}</p>
<h2>
<Translate id="modRelease.installationTitle">Installation</Translate>
</h2>
{installation ?? (
<ol>
<li>
<Translate id="modRelease.installStepOne">Download the release DLL.</Translate>
</li>
<li>
<Translate id="modRelease.installStepTwo">Copy it to your Data Center `Mods/` folder.</Translate>
</li>
<li>
<Translate id="modRelease.installStepThree">Start the game with MelonLoader.</Translate>
</li>
</ol>
)}
<h2>
<Translate id="modRelease.dllRouteTitle">Direct DLL Route</Translate>
</h2>
<p>
<code>{downloadPath}</code>
</p>
</article>
<aside className="mod-release-sidebar" aria-label="Download Sidebar">
<div className="mod-release-sidebar-card">
<h3>
<Translate id="modRelease.sidebarTitle">Download</Translate>
</h3>
{releaseReady ? (
<a className="button button--primary button--lg mod-release-download-btn" href={downloadUrl}>
<Translate id="modRelease.downloadButton">Download DLL</Translate>
</a>
) : (
<button className="button button--primary button--lg mod-release-download-btn" disabled type="button">
<Translate id="modRelease.notReleasedYet">NotReleasedYet</Translate>
</button>
)}
{normalizedReleaseNotesPath ? (
<Link className="button button--secondary button--sm mod-release-secondary-btn" to={normalizedReleaseNotesPath}>
<Translate id="modRelease.releaseNotesButton">Open module docs</Translate>
</Link>
) : null}
<dl className="mod-release-meta-list">
<div>
<dt>
<Translate id="modRelease.metaVersion">Version</Translate>
</dt>
<dd>{version}</dd>
</div>
<div>
<dt>
<Translate id="modRelease.metaAuthor">Author</Translate>
</dt>
<dd>{author}</dd>
</div>
<div>
<dt>
<Translate id="modRelease.metaCategory">Category</Translate>
</dt>
<dd>{category}</dd>
</div>
<div>
<dt>
<Translate id="modRelease.metaDependencies">Dependencies</Translate>
</dt>
<dd>{dependencies.join(', ')}</dd>
</div>
<div>
<dt>
<Translate id="modRelease.metaCodeLanguage">Code Language</Translate>
</dt>
<dd>{codeLanguages.join(', ')}</dd>
</div>
</dl>
<p className="mod-release-sidebar-note">
<Translate id="modRelease.sidebarHint">
The download route redirects to the latest GitHub release asset.
</Translate>
</p>
</div>
</aside>
</section>
</div>
);
}

650
src/css/custom.css Normal file
View File

@@ -0,0 +1,650 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Space+Grotesk:wght@300;500;700&display=swap");
@import "tailwindcss";
/* Luminescent Architect — design tokens (see docs/contributors/luminescent-design-system.md) */
@theme {
--color-background: #001110;
--color-surface: #001110;
--color-surface-dim: #001110;
--color-surface-bright: #00322f;
--color-surface-variant: #002b29;
--color-surface-container-lowest: #000000;
--color-surface-container-low: #001715;
--color-surface-container: #001e1c;
--color-surface-container-high: #002422;
--color-surface-container-highest: #002b29;
--color-on-background: #c0fcf6;
--color-on-surface: #c0fcf6;
--color-on-surface-variant: #7cb6b0;
--color-primary: #61f4d8;
--color-primary-dim: #4fe5ca;
--color-primary-container: #08c1a6;
--color-primary-fixed: #61f4d8;
--color-primary-fixed-dim: #4fe5ca;
--color-on-primary: #00594c;
--color-on-primary-container: #00332b;
--color-on-primary-fixed: #00443a;
--color-on-primary-fixed-variant: #006455;
--color-secondary: #1cede1;
--color-secondary-dim: #00ded3;
--color-secondary-container: #006a65;
--color-secondary-fixed: #1cede1;
--color-secondary-fixed-dim: #00ded3;
--color-on-secondary: #00534e;
--color-on-secondary-container: #dbfffa;
--color-on-secondary-fixed-variant: #005d58;
--color-tertiary: #64d0ff;
--color-tertiary-dim: #00b6eb;
--color-tertiary-container: #0ac4fd;
--color-tertiary-fixed: #0ac4fd;
--color-on-tertiary: #00445a;
--color-on-tertiary-container: #00394d;
--color-outline: #457f7a;
--color-outline-variant: #0f514c;
--color-error: #ff716c;
--color-error-dim: #d7383b;
--color-error-container: #9f0519;
--color-on-error: #490006;
--color-on-error-container: #ffa8a3;
--color-inverse-surface: #e4fffb;
--color-inverse-on-surface: #225e5a;
--color-inverse-primary: #006c5c;
--color-surface-tint: #61f4d8;
/* Legacy aliases (pages / older class names) */
--color-app-bg: #001110;
--color-text-main: #c0fcf6;
--color-text-soft: #7cb6b0;
--color-text-muted: #5a9a94;
--color-accent-green: #61f4d8;
--color-accent-green-deep: #08c1a6;
--color-accent-pink: #64d0ff;
--color-accent-violet: #1cede1;
--color-code-bg: #001110;
--color-code-surface: #002b29;
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-headline: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", monospace;
}
:root,
[data-theme="dark"] {
--ifm-color-primary: #61f4d8;
--ifm-color-primary-dark: #4fe5ca;
--ifm-color-primary-darker: #08c1a6;
--ifm-color-primary-light: #7ff7e4;
--ifm-color-primary-lighter: #bafdf1;
--ifm-background-color: var(--color-background);
--ifm-color-content: var(--color-on-surface);
--ifm-color-content-secondary: var(--color-on-surface-variant);
--ifm-font-family-base: var(--font-sans);
--ifm-heading-font-family: var(--font-headline);
--ifm-font-family-monospace: var(--font-mono);
--ifm-navbar-height: 72px;
--ifm-navbar-background-color: rgba(0, 17, 16, 0.8);
--ifm-card-background-color: var(--color-surface-container);
--ifm-code-font-size: 0.9rem;
--ifm-pre-background: var(--color-surface-container-highest);
--ifm-global-shadow-lw: 0 0 32px -4px rgba(0, 191, 165, 0.1);
}
html {
scroll-behavior: smooth;
}
html.dark,
html[data-theme="dark"] {
color-scheme: dark;
}
body {
min-height: 100vh;
color: var(--color-on-surface);
background-color: var(--color-background);
background-image:
radial-gradient(circle at 50% 50%, rgba(0, 191, 165, 0.05) 0%, transparent 70%),
radial-gradient(circle at 12% 0%, rgba(97, 244, 216, 0.06), transparent 50%),
linear-gradient(180deg, var(--color-background) 0%, var(--color-surface-container-low) 100%);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
::selection {
background: var(--color-primary);
color: var(--color-on-primary);
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--color-surface-container);
}
::-webkit-scrollbar-thumb {
border-radius: 999px;
background: linear-gradient(180deg, rgba(97, 244, 216, 0.45), rgba(28, 237, 225, 0.35));
}
::-webkit-scrollbar-thumb:hover {
background: linear-gradient(180deg, rgba(97, 244, 216, 0.65), rgba(100, 208, 255, 0.45));
}
/* Glass top bar — no hard bottom border; depth via blur + tinted shadow */
.navbar {
border: 0;
-webkit-backdrop-filter: blur(20px) saturate(140%);
backdrop-filter: blur(20px) saturate(140%);
background: rgba(0, 17, 16, 0.8) !important;
box-shadow: 0 0 32px -4px rgba(0, 191, 165, 0.1);
}
.navbar__title {
font-family: var(--font-headline);
font-weight: 700;
letter-spacing: -0.02em;
color: var(--color-on-surface);
}
.navbar__item .navbar__link {
font-family: var(--font-headline);
font-weight: 500;
letter-spacing: 0.06em;
font-size: 0.875rem;
text-transform: none;
color: rgba(192, 252, 246, 0.7);
transition: color 0.25s ease;
}
.navbar__item .navbar__link:hover {
color: var(--color-on-surface);
}
.navbar__item .navbar__link--active {
color: var(--color-primary);
box-shadow: inset 0 -2px 0 0 var(--color-primary);
}
.navbar [class*="colorModeToggle"] {
display: none !important;
}
.nav-right-icon {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.25rem;
height: 2.25rem;
border-radius: 999px;
backdrop-filter: blur(12px);
background: rgba(0, 30, 28, 0.65);
box-shadow: inset 0 0 0 1px rgba(15, 81, 76, 0.15);
}
.nav-icon-only {
color: transparent;
}
.nav-icon-only::before {
font-size: 0.95rem;
color: var(--color-on-surface-variant);
}
.nav-link-mods::before {
content: "🧩";
}
.nav-link-discord::before {
content: "💬";
}
.nav-link-support::before {
content: "🛟";
}
.nav-link-github::before {
content: "⌁";
}
.nav-locale::before {
content: "🌐";
}
/* Editorial bleed + hero (homepage) */
.editorial-bleed {
background: radial-gradient(circle at 50% 50%, rgba(0, 191, 165, 0.05) 0%, transparent 70%);
}
.hero-glow {
box-shadow: 0 0 80px -20px rgba(97, 244, 216, 0.15);
}
.hero-motion-wrap {
position: relative;
isolation: isolate;
}
.bg-hero-gradient {
background:
radial-gradient(circle at 50% -8%, rgba(97, 244, 216, 0.12), transparent 52%),
radial-gradient(circle at 14% 68%, rgba(28, 237, 225, 0.08), transparent 48%),
radial-gradient(circle at 84% 62%, rgba(100, 208, 255, 0.08), transparent 48%);
}
.hero-particles,
.hero-orb {
position: absolute;
pointer-events: none;
}
.hero-particles {
inset: 0;
opacity: 0.22;
background-image:
radial-gradient(circle at 18% 26%, rgba(192, 252, 246, 0.08) 0 1px, transparent 1px),
radial-gradient(circle at 72% 38%, rgba(97, 244, 216, 0.06) 0 1px, transparent 1px);
background-size: 280px 220px;
}
.hero-orb {
width: clamp(12rem, 20vw, 18rem);
height: clamp(12rem, 20vw, 18rem);
border-radius: 999px;
filter: blur(120px);
mix-blend-mode: screen;
opacity: 0.35;
}
.hero-orb-pink {
top: 6%;
right: 8%;
background: rgba(100, 208, 255, 0.45);
}
.hero-orb-green {
bottom: 12%;
left: 6%;
background: rgba(97, 244, 216, 0.35);
}
.homepage-logo-title {
font-family: var(--font-headline);
letter-spacing: -0.03em;
}
.text-gradient-brand {
background-image: linear-gradient(90deg, var(--color-primary), var(--color-secondary), var(--color-tertiary));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.text-reveal-glow {
text-shadow: 0 0 28px rgba(97, 244, 216, 0.18);
}
/* Tonal sectioning — avoid 1px rules; optional subtle top fade only */
.section-border {
position: relative;
}
.section-surface-alt {
background-color: var(--color-surface-container-low);
}
/* Glass cards */
.glass-card {
background: rgba(0, 30, 28, 0.8);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
.app-card {
background: linear-gradient(180deg, rgba(0, 30, 28, 0.85) 0%, rgba(0, 23, 21, 0.9) 100%);
border: 1px solid rgba(15, 81, 76, 0.1);
box-shadow: 0 18px 36px rgba(0, 0, 0, 0.35);
-webkit-backdrop-filter: blur(12px) saturate(125%);
backdrop-filter: blur(12px) saturate(125%);
transition: transform 0.28s ease, box-shadow 0.28s ease, border-color 0.28s ease;
}
.app-card-glow {
position: relative;
}
.app-card-glow::after {
content: "";
position: absolute;
inset: -1px;
border-radius: inherit;
pointer-events: none;
box-shadow: 0 0 24px rgba(97, 244, 216, 0.08);
}
.app-card-motion:hover {
transform: translateY(-5px);
border-color: rgba(97, 244, 216, 0.25);
box-shadow: 0 24px 42px rgba(0, 0, 0, 0.4), 0 0 32px -4px rgba(0, 191, 165, 0.12);
}
.btn-primary,
.btn-outline,
.btn-social {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
text-decoration: none !important;
transition: transform 0.25s ease, box-shadow 0.25s ease, background 0.25s ease, color 0.25s ease;
}
/* Primary CTA: 135° primary → primary-container, on-primary text */
.btn-primary {
border: 1px solid transparent;
color: var(--color-on-primary);
font-family: var(--font-headline);
font-weight: 700;
letter-spacing: 0.02em;
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-container) 100%);
box-shadow: 0 0 32px -4px rgba(0, 191, 165, 0.2);
}
.btn-primary:hover {
color: var(--color-on-primary);
transform: translateY(-2px);
box-shadow: 0 0 20px rgba(97, 244, 216, 0.3), 0 14px 28px rgba(0, 0, 0, 0.35);
}
/* Secondary: ghost border outline-variant @ 40% */
.btn-outline {
border: 1px solid rgba(15, 81, 76, 0.4);
color: var(--color-on-surface);
font-family: var(--font-headline);
font-weight: 700;
background: rgba(0, 17, 16, 0.5);
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
.btn-outline:hover {
color: var(--color-on-surface);
border-color: var(--color-primary);
transform: translateY(-2px);
}
.btn-social {
border: 1px solid transparent;
color: var(--color-on-surface-variant);
font-weight: 600;
font-size: 0.9rem;
border-radius: 0.75rem;
padding: 0.6rem 0.95rem;
background: rgba(0, 30, 28, 0.72);
box-shadow: inset 0 0 0 1px rgba(15, 81, 76, 0.15);
}
.btn-social:hover {
color: var(--color-on-surface);
transform: translateY(-2px);
box-shadow: inset 0 0 0 1px rgba(97, 244, 216, 0.25);
}
.btn-primary:focus-visible,
.btn-outline:focus-visible,
.btn-social:focus-visible {
outline: 2px solid rgba(97, 244, 216, 0.65);
outline-offset: 2px;
}
.btn-primary:focus,
.btn-outline:focus,
.btn-social:focus {
outline: 2px solid rgba(97, 244, 216, 0.65);
outline-offset: 2px;
}
.theme-doc-root .theme-doc-sidebar-container {
border: 0;
background: var(--color-surface-container-low);
box-shadow: inset -1px 0 0 rgba(15, 81, 76, 0.12);
}
.menu {
padding-top: 1rem;
padding-bottom: 1rem;
}
.menu__link {
margin: 0.2rem 0;
border-radius: 0.5rem;
border-left: 4px solid transparent;
padding-left: 0.75rem;
color: var(--color-on-surface-variant);
font-weight: 500;
transition: color 0.22s ease, background 0.22s ease, border-color 0.22s ease;
}
.menu__link:hover {
color: var(--color-on-surface);
background: rgba(97, 244, 216, 0.06);
}
.menu__link--active {
color: var(--color-on-surface) !important;
background: rgba(97, 244, 216, 0.1) !important;
border-left-color: var(--color-primary) !important;
}
.theme-doc-markdown {
line-height: 1.74;
}
.theme-doc-markdown h1,
.theme-doc-markdown h2,
.theme-doc-markdown h3,
.theme-doc-markdown h4 {
font-family: var(--font-headline);
letter-spacing: -0.015em;
color: var(--color-on-surface);
}
.theme-doc-markdown h2,
.theme-doc-markdown h3 {
border: 0;
margin-top: 2.2rem;
}
.theme-doc-markdown h2::after,
.theme-doc-markdown h3::after {
content: "";
display: block;
width: 64px;
height: 2px;
margin-top: 0.6rem;
background: linear-gradient(90deg, rgba(97, 244, 216, 0.55), rgba(28, 237, 225, 0.45));
border-radius: 999px;
opacity: 0.95;
}
.theme-doc-markdown p,
.theme-doc-markdown li {
color: var(--color-on-surface-variant);
}
.theme-doc-markdown a {
color: var(--color-primary);
text-decoration-color: rgba(97, 244, 216, 0.35);
}
.theme-doc-markdown a:hover {
color: var(--color-secondary);
text-decoration-color: rgba(28, 237, 225, 0.55);
}
.theme-doc-markdown :not(pre) > code {
color: var(--color-on-surface);
background: var(--color-surface-container-high);
box-shadow: inset 0 0 0 1px rgba(15, 81, 76, 0.15);
border-radius: 0.42rem;
font-family: var(--font-mono);
font-size: 0.84em;
padding: 0.12rem 0.34rem;
}
.theme-doc-markdown pre {
background: linear-gradient(180deg, var(--color-surface-container-highest) 0%, var(--color-surface-container-high) 100%) !important;
border-radius: 0.75rem;
border: 0;
box-shadow: 0 18px 32px rgba(0, 0, 0, 0.35), inset 0 0 0 1px rgba(15, 81, 76, 0.12);
}
.theme-doc-markdown pre code {
font-family: var(--font-mono);
}
.footer {
border: 0;
border-top: 1px solid rgba(0, 36, 34, 0.15);
background: var(--color-background);
color: rgba(192, 252, 246, 0.4);
font-size: 0.75rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.footer__title {
font-family: var(--font-headline);
color: #00bfa5;
}
.footer a {
color: rgba(192, 252, 246, 0.4);
transition: color 0.2s ease;
}
.footer a:hover {
color: var(--color-primary);
}
.footer-link-icon {
position: relative;
padding-left: 1.4rem;
}
.footer-link-icon::before {
position: absolute;
left: 0;
opacity: 0.9;
}
.footer-link-github::before {
content: "⌁";
}
.footer-link-discord::before {
content: "💬";
}
.footer-link-support::before {
content: "🛟";
}
.button.button--primary {
border: 0;
color: var(--color-on-primary);
background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-container) 100%);
box-shadow: 0 0 32px -4px rgba(0, 191, 165, 0.2);
}
.button.button--secondary {
border: 1px solid rgba(15, 81, 76, 0.4);
color: var(--color-on-surface);
background: rgba(0, 30, 28, 0.65);
}
.button.button--secondary:hover,
.button.button--primary:hover {
transform: translateY(-1px);
text-decoration: none;
}
.mod-release-card {
background: linear-gradient(180deg, rgba(0, 30, 28, 0.85) 0%, rgba(0, 17, 16, 0.92) 100%);
border-radius: 0.8rem;
box-shadow: inset 0 0 0 1px rgba(15, 81, 76, 0.12), 0 12px 24px rgba(0, 0, 0, 0.35);
}
.mod-release-badge {
color: var(--color-primary);
background: var(--color-surface-container-high);
border-radius: 999px;
padding: 0.24rem 0.62rem;
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.mod-release-btn-primary {
color: var(--color-on-primary);
font-weight: 700;
border-radius: 0.6rem;
padding: 0.52rem 1rem;
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-container));
box-shadow: 0 0 24px rgba(97, 244, 216, 0.15);
}
.mod-release-btn-outline {
color: var(--color-on-surface);
font-weight: 700;
border-radius: 0.6rem;
padding: 0.52rem 1rem;
background: rgba(0, 30, 28, 0.7);
box-shadow: inset 0 0 0 1px rgba(15, 81, 76, 0.35);
}
.mod-release-btn-primary:hover,
.mod-release-btn-outline:hover {
text-decoration: none;
}
@media (max-width: 996px) {
.navbar {
-webkit-backdrop-filter: blur(14px) saturate(130%);
backdrop-filter: blur(14px) saturate(130%);
}
.hero-orb {
opacity: 0.28;
}
.menu__link {
border-radius: 0.45rem;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition: none !important;
scroll-behavior: auto !important;
}
}
/* Optional: Material Symbols (loaded via docusaurus.config.js headTags) */
.material-symbols-outlined {
font-family: "Material Symbols Outlined", sans-serif;
font-variation-settings: "FILL" 0, "wght" 400, "GRAD" 0, "opsz" 24;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
}

173
src/data/moduleCatalog.ts Normal file
View File

@@ -0,0 +1,173 @@
export type ModuleType = 'plugin' | 'mod';
export type ModuleEntry = {
id: string;
name: string;
type: ModuleType;
description: string;
version: string;
author: string;
languages: string[];
dependencies: string[];
wikiPath: string;
releasePath: string;
downloadPath: string;
releaseReady: boolean;
};
export const moduleCatalog: ModuleEntry[] = [
{
id: 'ffm-plugin-asset-exporter',
name: 'FFM.Plugin.AssetExporter',
type: 'plugin',
description: 'Export-focused asset tooling for Data Center workflows.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'FrikaMF'],
wikiPath: '/wiki/mods/extensions/ffm-plugin-asset-exporter',
releasePath: '/wiki/releases/plugins/ffm-plugin-asset-exporter-release',
downloadPath: '/plugin/FFM.Plugin.AssetExporter.dll',
releaseReady: false,
},
{
id: 'ffm-plugin-multiplayer',
name: 'FFM.Plugin.Multiplayer',
type: 'plugin',
description: 'Multiplayer-oriented plugin surface for FrikaMF ecosystems.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'FrikaMF'],
wikiPath: '/wiki/mods/extensions/ffm-plugin-multiplayer',
releasePath: '/wiki/releases/plugins/ffm-plugin-multiplayer-release',
downloadPath: '/plugin/FFM.Plugin.Multiplayer.dll',
releaseReady: false,
},
{
id: 'ffm-plugin-player-models',
name: 'FFM.Plugin.PlayerModels',
type: 'plugin',
description: 'Player model extension plugin for runtime and presentation logic.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'FrikaMF'],
wikiPath: '/wiki/mods/extensions/ffm-plugin-player-models',
releasePath: '/wiki/releases/plugins/ffm-plugin-player-models-release',
downloadPath: '/plugin/FFM.Plugin.PlayerModels.dll',
releaseReady: false,
},
{
id: 'ffm-plugin-sysadmin',
name: 'FFM.Plugin.Sysadmin',
type: 'plugin',
description: 'Sysadmin utility plugin for diagnostics and operations.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'FrikaMF'],
wikiPath: '/wiki/mods/extensions/ffm-plugin-sysadmin',
releasePath: '/wiki/releases/plugins/ffm-plugin-sysadmin-release',
downloadPath: '/plugin/FFM.Plugin.Sysadmin.dll',
releaseReady: false,
},
{
id: 'ffm-plugin-web-ui-bridge',
name: 'FFM.Plugin.WebUIBridge',
type: 'plugin',
description: 'Bridge plugin between game runtime data and web UI overlays.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'FrikaMF'],
wikiPath: '/wiki/mods/extensions/ffm-plugin-web-ui-bridge',
releasePath: '/wiki/releases/plugins/ffm-plugin-web-ui-bridge-release',
downloadPath: '/plugin/FFM.Plugin.WebUIBridge.dll',
releaseReady: false,
},
{
id: 'fmf-modpathredirector',
name: 'FMF.ModPathRedirector',
type: 'plugin',
description: 'MelonLoader plugin: waits for each subscribed Workshop item (Steam + StreamingAssets/Mods/workshop_*) before MelonMods load.',
version: '1.5.0',
author: 'DataCenterExporter / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'Steam client'],
wikiPath: '/wiki/workshop-uploader',
releasePath: '/wiki/releases/plugins/fmf-modpathredirector-release',
downloadPath: '/plugin/FMF.ModPathRedirector.dll',
releaseReady: true,
},
{
id: 'fmf-console-input-guard',
name: 'FMF.ConsoleInputGuard',
type: 'mod',
description: 'Console interaction guardrails for safer gameplay input handling.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'Harmony'],
wikiPath: '/wiki/mods/mods/fmf-console-input-guard',
releasePath: '/wiki/releases/mods/fmf-console-input-guard-release',
downloadPath: '/mod/FMF.ConsoleInputGuard.dll',
releaseReady: false,
},
{
id: 'fmf-gregify-employees',
name: 'FMF.GregifyEmployees',
type: 'mod',
description: 'Gameplay mod for employee theming and behavior customization.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'Harmony'],
wikiPath: '/wiki/mods/mods/fmf-gregify-employees',
releasePath: '/wiki/releases/mods/fmf-gregify-employees-release',
downloadPath: '/mod/FMF.GregifyEmployees.dll',
releaseReady: false,
},
{
id: 'fmf-hex-label-mod',
name: 'FMF.HexLabelMod',
type: 'mod',
description: 'In-world hex color labels for cable spinners and racks.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'Harmony'],
wikiPath: '/wiki/mods/extensions/fmf-hex-label-mod',
releasePath: '/wiki/releases/mods/fmf-hex-label-mod-release',
downloadPath: '/mod/FMF.HexLabelMod.dll',
releaseReady: false,
},
{
id: 'fmf-lang-compat-bridge',
name: 'FMF.LangCompatBridge',
type: 'mod',
description: 'Localization compatibility bridge for mixed mod stacks.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader'],
wikiPath: '/wiki/mods/extensions/fmf-lang-compat-bridge',
releasePath: '/wiki/releases/mods/fmf-lang-compat-bridge-release',
downloadPath: '/mod/FMF.JoniMLCompatMod.dll',
releaseReady: false,
},
{
id: 'fmf-ui-replacement-mod',
name: 'FMF.UIReplacementMod',
type: 'mod',
description: 'Replaces and modernizes selected in-game UI layers.',
version: 'NotReleasedYet',
author: 'mleem97 / FrikaMF Community',
languages: ['C#'],
dependencies: ['MelonLoader', 'Harmony'],
wikiPath: '/wiki/mods/extensions/fmf-ui-replacement-mod',
releasePath: '/wiki/releases/mods/fmf-ui-replacement-mod-release',
downloadPath: '/mod/FMF.UIReplacementMod.dll',
releaseReady: false,
},
];

52
src/i18n/homepage/de.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const de: HomepageContent = {
heroLine1: 'DATACENTER MODDING HUB.',
heroLine2: 'SEI SMART WIE GREG.',
heroSub1: 'Klar. Flexibel. Community-getrieben.',
heroSub2: 'Mit FrikaMF, Rust-Stacks, Multiplayer und Plugins.',
ctaStart: 'MODDING STARTEN',
ctaMods: 'VERFÜGBARE STANDALONE MODS & PLUGINS',
docsPaths: 'Dokumentationspfade',
featureTitles: ['Dual-Track Modding', 'Runtime Hook Bridge', 'Wiki-basierte Doku', 'Community-Ökosystem'],
featureDescriptions: [
'Baue Mods direkt in C# oder nativ in Rust via C-ABI/FFI.',
'Harmony-Patches, Event-IDs und deterministische Hook-Weiterleitung.',
'Source of truth aus .wiki mit Enduser- und Moddev-Pfaden.',
'Core-Framework-Doku plus Standalone-Plugin- und Ökosystem-Doku.',
],
comingSoon: 'Kommt bald',
comingSoonText: '`datacentermods.com` wird gerade vorbereitet. Bis zum Launch findest du alles unter `Docs Hub → Standalone Mods`.',
communityTitle: 'Community & Maintainer',
communityText: 'Dieses Portal bündelt Framework-Doku und Community-Plugins für das Data-Center-Ökosystem.',
docsEndUserTitle: 'Endnutzer',
docsEndUserDescription: 'Installation, Updates, FAQ und Fehlerbehebung.',
docsModDevsTitle: 'Mod-Entwickler',
docsModDevsDescription: 'Debug-Workflows, Setup, Hooks und Referenzen.',
docsContributorsTitle: 'Mitwirkende',
docsContributorsDescription: 'Konventionen, CI-Checks und Contribution-Workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Komplette Feature-Übersicht und konkrete Use Cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook-System, FFI-Bridge, Event-Verträge und Runtime-Architektur.',
ecosystemCoreCta: 'Core-Doku öffnen →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI-Implementierungsleitfäden für externe und standalone Runtimes.',
ecosystemRustCta: 'Rust/FFI-Doku öffnen →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer-Roadmap, Plugin-Doku und Community-Modul-Referenzen.',
ecosystemMultiplayerCta: 'Multiplayer-Doku öffnen →',
gregTitle: 'Die Legende von Greg',
gregText1:
'Greg ist der unermüdliche Server-Techniker des Projekts. Er wirkt zwar so, als hätte er seit Monaten nicht mehr geschlafen, und er ist meistens still — aber sobald ein Rack ausfällt oder ein Mod zickt, ist Greg schon da.',
gregText2:
'Unterwürfig erfüllt er all deine Wünsche: mehr Uptime, sauberere Configs, bessere Logs und weniger Drama im Deployment. Er fragt nicht viel, er liefert einfach.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Melde Bugs, fordere Doku für neue Community-Plugins an und verfolge Workstreams.',
supportCta: 'GitHub Issues öffnen',
availableModsLabel: 'Verfügbare Mods',
repositoryLabel: 'Repository',
joinLabel: 'BEITRETEN',
};

52
src/i18n/homepage/en.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const en: HomepageContent = {
heroLine1: 'DATACENTER MODDING HUB.',
heroLine2: 'SMART LIKE GREG.',
heroSub1: 'Streamlined. Flexible. Community-curated.',
heroSub2: 'Covering FrikaMF, Rust stacks, multiplayer and plugins.',
ctaStart: 'START MODDING',
ctaMods: 'AVAILABLE STANDALONE MODS & PLUGINS',
docsPaths: 'Documentation Paths',
featureTitles: ['Dual-Track Modding', 'Runtime Hook Bridge', 'Wiki-Driven Docs', 'Community Ecosystem'],
featureDescriptions: [
'Build mods in C# directly or native in Rust via C-ABI/FFI.',
'Harmony patches, event IDs, and deterministic hook forwarding.',
'Source of truth from .wiki with end-user and mod-dev paths.',
'Core framework docs plus standalone plugin and ecosystem docs.',
],
comingSoon: 'Coming Soon',
comingSoonText: '`datacentermods.com` is currently in preparation. Until launch, use `Docs Hub → Standalone Mods`.',
communityTitle: 'Community & Maintainers',
communityText: 'This portal is designed for framework docs and community-built plugins across the Data Center ecosystem.',
docsEndUserTitle: 'End-User',
docsEndUserDescription: 'Install, update, FAQ, troubleshooting.',
docsModDevsTitle: 'Mod-Devs',
docsModDevsDescription: 'Debug workflows, setup, hooks and references.',
docsContributorsTitle: 'Contributors',
docsContributorsDescription: 'Conventions, CI checks, contribution workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Complete feature map and implementation use cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook system, FFI bridge, event contracts and runtime architecture.',
ecosystemCoreCta: 'Open core docs →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI implementation guides for external and standalone runtimes.',
ecosystemRustCta: 'Open Rust/FFI docs →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer roadmap, plugin docs, and community module references.',
ecosystemMultiplayerCta: 'Open multiplayer docs →',
gregTitle: 'The Legend of Greg',
gregText1:
'Greg is the tireless server technician of this project. He looks like he has not slept for months, and he is usually quiet — but once a rack fails or a mod breaks, Greg is already there.',
gregText2:
'He quietly fulfills your wishes: more uptime, cleaner configs, better logs and less deployment drama. He does not ask much, he just ships.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Report bugs, request docs for new community plugins, and track workstreams.',
supportCta: 'Open GitHub Issues',
availableModsLabel: 'Available Mods',
repositoryLabel: 'Repository',
joinLabel: 'JOIN',
};

52
src/i18n/homepage/es.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const es: HomepageContent = {
heroLine1: 'HUB DE MODDING DATACENTER.',
heroLine2: 'SÉ INTELIGENTE. SÉ COMO GREG.',
heroSub1: 'Claro. Flexible. Impulsado por la comunidad.',
heroSub2: 'Con FrikaMF, stacks Rust, multijugador y plugins.',
ctaStart: 'EMPEZAR MODDING',
ctaMods: 'MODS Y PLUGINS STANDALONE DISPONIBLES',
docsPaths: 'Rutas de documentación',
featureTitles: ['Modding doble vía', 'Puente de hooks runtime', 'Docs desde wiki', 'Ecosistema comunidad'],
featureDescriptions: [
'Crea mods en C# o en Rust nativo vía C-ABI/FFI.',
'Parches Harmony, IDs de eventos y forwarding determinista.',
'Fuente oficial en .wiki para usuarios y modders.',
'Docs del framework core y de plugins standalone.',
],
comingSoon: 'Próximamente',
comingSoonText: '`datacentermods.com` está en preparación. Mientras tanto, usa `Docs Hub → Standalone Mods`.',
communityTitle: 'Comunidad y mantenedores',
communityText: 'Este portal reúne docs del framework y plugins de la comunidad para el ecosistema Data Center.',
docsEndUserTitle: 'End-User',
docsEndUserDescription: 'Install, update, FAQ, troubleshooting.',
docsModDevsTitle: 'Mod-Devs',
docsModDevsDescription: 'Debug workflows, setup, hooks and references.',
docsContributorsTitle: 'Contributors',
docsContributorsDescription: 'Conventions, CI checks, contribution workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Complete feature map and implementation use cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook system, FFI bridge, event contracts and runtime architecture.',
ecosystemCoreCta: 'Open core docs →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI implementation guides for external and standalone runtimes.',
ecosystemRustCta: 'Open Rust/FFI docs →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer roadmap, plugin docs, and community module references.',
ecosystemMultiplayerCta: 'Open multiplayer docs →',
gregTitle: 'The Legend of Greg',
gregText1:
'Greg is the tireless server technician of this project. He looks like he has not slept for months, and he is usually quiet — but once a rack fails or a mod breaks, Greg is already there.',
gregText2:
'He quietly fulfills your wishes: more uptime, cleaner configs, better logs and less deployment drama. He does not ask much, he just ships.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Report bugs, request docs for new community plugins, and track workstreams.',
supportCta: 'Open GitHub Issues',
availableModsLabel: 'Available Mods',
repositoryLabel: 'Repository',
joinLabel: 'JOIN',
};

52
src/i18n/homepage/fr.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const fr: HomepageContent = {
heroLine1: 'HUB DE MODDING DATACENTER.',
heroLine2: 'SOYEZ MALIN, SOYEZ GREG.',
heroSub1: 'Simple. Flexible. Piloté par la communauté.',
heroSub2: 'FrikaMF, stacks Rust, multijoueur et plugins.',
ctaStart: 'COMMENCER LE MODDING',
ctaMods: 'MODS & PLUGINS STANDALONE DISPONIBLES',
docsPaths: 'Parcours de documentation',
featureTitles: ['Modding double piste', 'Pont runtime hooks', 'Docs basées sur le wiki', 'Écosystème communauté'],
featureDescriptions: [
'Créez des mods en C# ou en Rust natif via C-ABI/FFI.',
'Patches Harmony, IDs dévénements et routage déterministe.',
'Source de vérité issue de .wiki pour users et moddeurs.',
'Docs du framework principal et des plugins standalone.',
],
comingSoon: 'Bientôt disponible',
comingSoonText: '`datacentermods.com` est en préparation. En attendant, consultez `Docs Hub → Standalone Mods`.',
communityTitle: 'Communauté & Mainteneurs',
communityText: 'Ce portail regroupe la doc framework et les plugins communautaires de lécosystème Data Center.',
docsEndUserTitle: 'End-User',
docsEndUserDescription: 'Install, update, FAQ, troubleshooting.',
docsModDevsTitle: 'Mod-Devs',
docsModDevsDescription: 'Debug workflows, setup, hooks and references.',
docsContributorsTitle: 'Contributors',
docsContributorsDescription: 'Conventions, CI checks, contribution workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Complete feature map and implementation use cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook system, FFI bridge, event contracts and runtime architecture.',
ecosystemCoreCta: 'Open core docs →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI implementation guides for external and standalone runtimes.',
ecosystemRustCta: 'Open Rust/FFI docs →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer roadmap, plugin docs, and community module references.',
ecosystemMultiplayerCta: 'Open multiplayer docs →',
gregTitle: 'The Legend of Greg',
gregText1:
'Greg is the tireless server technician of this project. He looks like he has not slept for months, and he is usually quiet — but once a rack fails or a mod breaks, Greg is already there.',
gregText2:
'He quietly fulfills your wishes: more uptime, cleaner configs, better logs and less deployment drama. He does not ask much, he just ships.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Report bugs, request docs for new community plugins, and track workstreams.',
supportCta: 'Open GitHub Issues',
availableModsLabel: 'Available Mods',
repositoryLabel: 'Repository',
joinLabel: 'JOIN',
};

View File

@@ -0,0 +1,33 @@
import { de } from './de';
import { en } from './en';
import { es } from './es';
import { fr } from './fr';
import { ja } from './ja';
import { ru } from './ru';
import type { HomepageContent, LocaleKey } from './types';
const homepageByLocale: Record<LocaleKey, HomepageContent> = {
en,
de,
fr,
es,
ru,
ja,
};
export type { HomepageContent, LocaleKey } from './types';
export function getHomepageContent(locale: string): HomepageContent {
const normalizedLocale = (locale || 'en').toLowerCase();
const languageOnly = normalizedLocale.split('-')[0] as LocaleKey;
if (normalizedLocale in homepageByLocale) {
return homepageByLocale[normalizedLocale as LocaleKey];
}
if (languageOnly in homepageByLocale) {
return homepageByLocale[languageOnly];
}
return homepageByLocale.en;
}

52
src/i18n/homepage/ja.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const ja: HomepageContent = {
heroLine1: 'DATACENTER MODDING HUB.',
heroLine2: 'BE SMART. BE LIKE GREG.',
heroSub1: 'シンプル。柔軟。コミュニティ主導。',
heroSub2: 'FrikaMF、Rustスタック、マルチプレイ、プラグインを網羅。',
ctaStart: 'MODDING を始める',
ctaMods: '利用可能な STANDALONE MODS & PLUGINS',
docsPaths: 'ドキュメント導線',
featureTitles: ['デュアルトラック Modding', 'ランタイム Hook Bridge', 'Wiki ベース Docs', 'コミュニティ エコシステム'],
featureDescriptions: [
'C# または Rust (C-ABI/FFI) で Mod を開発。',
'Harmony パッチ、Event ID、決定的な Hook 転送。',
'.wiki を source of truth としたユーザー/開発者向け導線。',
'Framework 本体と Standalone プラグインの情報を統合。',
],
comingSoon: '近日公開',
comingSoonText: '`datacentermods.com` は準備中です。公開まで `Docs Hub → Standalone Mods` をご利用ください。',
communityTitle: 'コミュニティ & メンテナー',
communityText: 'このポータルは Data Center エコシステム向けに framework docs と community plugins をまとめています。',
docsEndUserTitle: 'End-User',
docsEndUserDescription: 'Install, update, FAQ, troubleshooting.',
docsModDevsTitle: 'Mod-Devs',
docsModDevsDescription: 'Debug workflows, setup, hooks and references.',
docsContributorsTitle: 'Contributors',
docsContributorsDescription: 'Conventions, CI checks, contribution workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Complete feature map and implementation use cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook system, FFI bridge, event contracts and runtime architecture.',
ecosystemCoreCta: 'Open core docs →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI implementation guides for external and standalone runtimes.',
ecosystemRustCta: 'Open Rust/FFI docs →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer roadmap, plugin docs, and community module references.',
ecosystemMultiplayerCta: 'Open multiplayer docs →',
gregTitle: 'The Legend of Greg',
gregText1:
'Greg is the tireless server technician of this project. He looks like he has not slept for months, and he is usually quiet — but once a rack fails or a mod breaks, Greg is already there.',
gregText2:
'He quietly fulfills your wishes: more uptime, cleaner configs, better logs and less deployment drama. He does not ask much, he just ships.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Report bugs, request docs for new community plugins, and track workstreams.',
supportCta: 'Open GitHub Issues',
availableModsLabel: 'Available Mods',
repositoryLabel: 'Repository',
joinLabel: 'JOIN',
};

52
src/i18n/homepage/ru.ts Normal file
View File

@@ -0,0 +1,52 @@
import type { HomepageContent } from './types';
export const ru: HomepageContent = {
heroLine1: 'ХАБ МОДДИНГА DATACENTER.',
heroLine2: 'БУДЬ УМНЫМ. БУДЬ КАК ГРЕГ.',
heroSub1: 'Просто. Гибко. Силой сообщества.',
heroSub2: 'FrikaMF, Rust-стек, мультиплеер и плагины.',
ctaStart: 'НАЧАТЬ МОДДИНГ',
ctaMods: 'ДОСТУПНЫЕ STANDALONE МОДЫ И ПЛАГИНЫ',
docsPaths: 'Разделы документации',
featureTitles: ['Два пути моддинга', 'Runtime Hook Bridge', 'Вики-документация', 'Экосистема сообщества'],
featureDescriptions: [
'Пишите моды на C# или нативно на Rust через C-ABI/FFI.',
'Harmony-патчи, event IDs и детерминированный forwarding.',
'Единый источник в .wiki для пользователей и моддеров.',
'Документация ядра и standalone-плагинов сообщества.',
],
comingSoon: 'Скоро',
comingSoonText: '`datacentermods.com` пока готовится. До запуска используйте `Docs Hub → Standalone Mods`.',
communityTitle: 'Сообщество и мейнтейнеры',
communityText: 'Портал объединяет документацию фреймворка и community-плагинов экосистемы Data Center.',
docsEndUserTitle: 'End-User',
docsEndUserDescription: 'Install, update, FAQ, troubleshooting.',
docsModDevsTitle: 'Mod-Devs',
docsModDevsDescription: 'Debug workflows, setup, hooks and references.',
docsContributorsTitle: 'Contributors',
docsContributorsDescription: 'Conventions, CI checks, contribution workflow.',
docsCapabilityTitle: 'Capability Matrix',
docsCapabilityDescription: 'Complete feature map and implementation use cases.',
ecosystemTitle: 'Ecosystem Coverage',
ecosystemCoreTitle: 'FrikaMF Core',
ecosystemCoreDescription: 'Hook system, FFI bridge, event contracts and runtime architecture.',
ecosystemCoreCta: 'Open core docs →',
ecosystemRustTitle: 'Standalone Rust',
ecosystemRustDescription: 'Rust/FFI implementation guides for external and standalone runtimes.',
ecosystemRustCta: 'Open Rust/FFI docs →',
ecosystemMultiplayerTitle: 'Multiplayer & Plugins',
ecosystemMultiplayerDescription: 'Multiplayer roadmap, plugin docs, and community module references.',
ecosystemMultiplayerCta: 'Open multiplayer docs →',
gregTitle: 'The Legend of Greg',
gregText1:
'Greg is the tireless server technician of this project. He looks like he has not slept for months, and he is usually quiet — but once a rack fails or a mod breaks, Greg is already there.',
gregText2:
'He quietly fulfills your wishes: more uptime, cleaner configs, better logs and less deployment drama. He does not ask much, he just ships.',
gregQuote: '"Be smart. Be like Greg."',
supportTitle: 'Support',
supportText: 'Report bugs, request docs for new community plugins, and track workstreams.',
supportCta: 'Open GitHub Issues',
availableModsLabel: 'Available Mods',
repositoryLabel: 'Repository',
joinLabel: 'JOIN',
};

View File

@@ -0,0 +1,45 @@
export type LocaleKey = 'en' | 'de' | 'fr' | 'es' | 'ru' | 'ja';
export type HomepageContent = {
heroLine1: string;
heroLine2: string;
heroSub1: string;
heroSub2: string;
ctaStart: string;
ctaMods: string;
docsPaths: string;
featureTitles: [string, string, string, string];
featureDescriptions: [string, string, string, string];
comingSoon: string;
comingSoonText: string;
communityTitle: string;
communityText: string;
docsEndUserTitle: string;
docsEndUserDescription: string;
docsModDevsTitle: string;
docsModDevsDescription: string;
docsContributorsTitle: string;
docsContributorsDescription: string;
docsCapabilityTitle: string;
docsCapabilityDescription: string;
ecosystemTitle: string;
ecosystemCoreTitle: string;
ecosystemCoreDescription: string;
ecosystemCoreCta: string;
ecosystemRustTitle: string;
ecosystemRustDescription: string;
ecosystemRustCta: string;
ecosystemMultiplayerTitle: string;
ecosystemMultiplayerDescription: string;
ecosystemMultiplayerCta: string;
gregTitle: string;
gregText1: string;
gregText2: string;
gregQuote: string;
supportTitle: string;
supportText: string;
supportCta: string;
availableModsLabel: string;
repositoryLabel: string;
joinLabel: string;
};

BIN
src/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

368
src/pages/index.tsx Normal file
View File

@@ -0,0 +1,368 @@
import React, { useMemo } from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { motion, type Variants, useReducedMotion } from 'framer-motion';
import { getHomepageContent } from '../i18n/homepage';
import gregImage from '../image.png';
import {
FaArrowUpRightFromSquare,
FaBookOpen,
FaCode,
FaDiscord,
FaGithub,
FaLifeRing,
FaPeopleGroup,
FaShop,
FaScrewdriverWrench,
} from 'react-icons/fa6';
type FeatureItem = {
icon: React.ReactNode;
title: string;
description: string;
};
type DocPathItem = {
title: string;
description: string;
link: string;
};
const viewport = { once: true, margin: '-90px' };
function buildVariants(reducedMotion: boolean) {
const section: Variants = reducedMotion
? { hidden: { opacity: 0 }, show: { opacity: 1 } }
: {
hidden: { opacity: 0, y: 26 },
show: {
opacity: 1,
y: 0,
transition: { duration: 0.65, ease: [0.16, 1, 0.3, 1] },
},
};
const grid: Variants = {
hidden: {},
show: {
transition: {
staggerChildren: reducedMotion ? 0 : 0.09,
delayChildren: reducedMotion ? 0 : 0.06,
},
},
};
const card: Variants = reducedMotion
? { hidden: { opacity: 0 }, show: { opacity: 1 } }
: {
hidden: { opacity: 0, y: 18, scale: 0.98 },
show: {
opacity: 1,
y: 0,
scale: 1,
transition: { duration: 0.45, ease: [0.16, 1, 0.3, 1] },
},
};
const textReveal: Variants = reducedMotion
? { hidden: { opacity: 0 }, show: { opacity: 1 } }
: {
hidden: { opacity: 0, y: 18 },
show: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: [0.16, 1, 0.3, 1] },
},
};
return { section, grid, card, textReveal };
}
export default function HomePage(): JSX.Element {
const {
i18n: { currentLocale },
} = useDocusaurusContext();
const t = getHomepageContent(currentLocale);
const reducedMotion = useReducedMotion();
const variants = useMemo(() => buildVariants(Boolean(reducedMotion)), [reducedMotion]);
const features: FeatureItem[] = [
{
icon: <FaScrewdriverWrench className="text-xl" />,
title: t.featureTitles[0],
description: t.featureDescriptions[0],
},
{
icon: <FaCode className="text-xl" />,
title: t.featureTitles[1],
description: t.featureDescriptions[1],
},
{
icon: <FaBookOpen className="text-xl" />,
title: t.featureTitles[2],
description: t.featureDescriptions[2],
},
{
icon: <FaPeopleGroup className="text-xl" />,
title: t.featureTitles[3],
description: t.featureDescriptions[3],
},
];
const knowledgePaths: DocPathItem[] = [
{ title: 'Wiki Overview', description: 'Canonical docs entrypoint under /wiki.', link: '/wiki' },
{ title: 'Framework Core', description: 'Runtime hooks, bridge, events and architecture.', link: '/wiki/mods/framework' },
{ title: 'Plugin Wiki', description: 'Plugin-specific docs and release pages.', link: '/wiki/mods/extensions/' },
{ title: 'Mod Wiki', description: 'Gameplay mods, release state and module docs.', link: '/wiki/mods/mods' },
{ title: 'FMF Hooks Catalog', description: 'Auto-generated hook strings and event-id map from framework sources.', link: '/wiki/reference/fmf-hooks-catalog' },
{ title: 'Release Channels', description: 'Steam Workshop for discovery; GitHub for beta and alternate DLLs.', link: '/wiki/reference/release-channels' },
{ title: 'Unified Roadmap', description: 'Consolidated roadmap with duplicate tracks removed.', link: '/wiki/roadmap/unified-roadmap' },
{ title: 'Mods Catalog', description: 'Dynamic /mods catalog with wiki and download links.', link: '/mods' },
];
const workflowPaths: DocPathItem[] = [
{ title: 'End-User Docs', description: 'Install, update and troubleshooting paths.', link: '/wiki/wiki-import/EndUser/End-User-Release' },
{ title: 'Mod Developer Docs', description: 'Setup, debug and hook integration guides.', link: '/wiki/wiki-import/ModDevs/Mod-Developer-Debug' },
{ title: 'Repo Inventory', description: 'Monorepo layout, projects, and solution drift for contributors.', link: '/wiki/contributors/repo-inventory' },
{ title: 'Contributor Workflow', description: 'Contribution standards and repository workflow.', link: '/wiki/contributors/docusaurus-workflow' },
{ title: 'Plugin Security Audit', description: 'Git-link submission and malicious-code audit process.', link: '/wiki/contributors/plugin-submission-audit' },
];
return (
<Layout
description="Community docs for FrikaMF, standalone Rust stacks, multiplayer, and plugins.">
<main className="bg-background text-on-surface font-sans min-h-screen editorial-bleed bg-hero-gradient">
<section className="hero-motion-wrap relative flex min-h-[68vh] flex-col items-center justify-center overflow-hidden px-4 py-20 text-center">
<div className="hero-particles" aria-hidden="true" />
<div className="hero-orb hero-orb-pink" aria-hidden="true" />
<div className="hero-orb hero-orb-green" aria-hidden="true" />
<motion.h1
className="homepage-logo-title text-reveal-glow mb-8 text-4xl font-black leading-none tracking-tight text-on-surface md:text-6xl"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.textReveal}>
FRIKA MOD <span className="text-primary">🍪</span>
<br />
<span className="text-gradient-brand">FRAMEWORK</span>
</motion.h1>
<motion.h2
className="mb-4 max-w-3xl font-headline text-2xl font-bold tracking-tight text-on-surface md:text-4xl"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.textReveal}
transition={{ delay: reducedMotion ? 0 : 0.08 }}>
{t.heroLine1}
<br />
<span className="text-on-surface-variant">{t.heroLine2}</span>
</motion.h2>
<motion.p
className="mb-10 max-w-lg text-base font-medium text-on-surface-variant md:text-lg"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.textReveal}
transition={{ delay: reducedMotion ? 0 : 0.14 }}>
{t.heroSub1}
<br />
{t.heroSub2}
</motion.p>
<motion.div
className="flex flex-wrap items-center justify-center gap-3"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.textReveal}
transition={{ delay: reducedMotion ? 0 : 0.22 }}>
<Link to="/wiki/mods/framework" className="btn-primary hero-glow px-8 py-4 rounded-xl text-lg">
{t.ctaStart}
</Link>
<Link to="/mods" className="btn-outline px-8 py-4 rounded-xl text-lg">
{t.ctaMods}
</Link>
</motion.div>
</section>
<motion.section
id="features"
className="section-surface-alt px-4 py-20"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<motion.div className="mx-auto grid max-w-6xl grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4" variants={variants.grid}>
{features.map((feature) => (
<motion.article
key={feature.title}
className="app-card app-card-motion app-card-glow glass-card p-5 rounded-xl text-on-surface"
variants={variants.card}
whileHover={
reducedMotion
? undefined
: {
y: -6,
rotateX: 2,
rotateY: -2,
scale: 1.01,
transition: { type: 'spring', stiffness: 280, damping: 18 },
}
}
style={{ transformStyle: 'preserve-3d' }}>
<h3 className="mb-2 flex items-center gap-2 font-headline text-lg font-bold text-on-surface">
<span className="text-primary">{feature.icon}</span>
<span>{feature.title}</span>
</h3>
<p className="text-sm font-medium text-on-surface-variant">{feature.description}</p>
</motion.article>
))}
</motion.div>
</motion.section>
<motion.section
id="docs"
className="px-4 py-20"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<div className="mx-auto max-w-5xl text-center">
<motion.h2 className="mb-10 font-headline text-3xl font-bold text-on-surface" variants={variants.textReveal}>
Knowledge Architecture
</motion.h2>
<motion.div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3" variants={variants.grid}>
{knowledgePaths.map((doc) => (
<motion.div key={doc.link} variants={variants.card}>
<Link to={doc.link} className="app-card app-card-motion app-card-glow rounded-lg p-5 text-left text-on-surface block group">
<div className="mb-2 font-headline text-lg font-bold text-on-surface transition-colors group-hover:text-primary">{doc.title}</div>
<div className="text-sm text-on-surface-variant">{doc.description}</div>
</Link>
</motion.div>
))}
</motion.div>
</div>
</motion.section>
<motion.section
id="ecosystem"
className="section-surface-alt px-4 py-20"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<div className="mx-auto max-w-5xl text-center">
<motion.h2 className="mb-10 font-headline text-3xl font-bold text-on-surface" variants={variants.textReveal}>
Workflows
</motion.h2>
<motion.div className="grid grid-cols-1 gap-4 md:grid-cols-2" variants={variants.grid}>
{workflowPaths.map((doc) => (
<motion.div key={doc.link} variants={variants.card}>
<Link to={doc.link} className="app-card app-card-motion app-card-glow rounded-lg p-5 text-left text-on-surface block group">
<div className="mb-2 font-headline text-lg font-bold text-on-surface transition-colors group-hover:text-primary">{doc.title}</div>
<div className="text-sm text-on-surface-variant">{doc.description}</div>
</Link>
</motion.div>
))}
</motion.div>
</div>
</motion.section>
<motion.section
id="greg-story"
className="px-4 py-20"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<div className="mx-auto max-w-6xl">
<motion.div className="app-card app-card-glow rounded-2xl p-6 md:p-8 flex flex-col md:flex-row md:items-center md:justify-between gap-6" variants={variants.card}>
<div>
<h2 className="mb-4 font-headline text-2xl font-bold text-on-surface md:text-3xl">{t.gregTitle}</h2>
<p className="mb-4 text-on-surface-variant text-sm md:text-base leading-relaxed max-w-md">
{t.gregText1}
</p>
<p className="mb-4 text-on-surface-variant text-sm md:text-base leading-relaxed max-w-md">
{t.gregText2}
</p>
<p className="font-headline text-lg font-bold text-secondary italic">{t.gregQuote}</p>
</div>
<motion.div className="shrink-0" whileHover={reducedMotion ? undefined : { rotate: 1.2, y: -3 }}>
<div className="w-32 h-40 md:w-48 md:h-56 overflow-hidden rounded-xl border-2 border-primary/25">
<img src={gregImage} alt="Greg" className="h-full w-full object-cover" />
</div>
</motion.div>
</motion.div>
</div>
</motion.section>
<motion.section
id="community"
className="px-4 py-16"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<div className="mx-auto max-w-6xl">
<motion.div
className="mb-6 rounded-xl border border-outline-variant/15 bg-surface-container-high p-4"
variants={variants.card}>
<div className="text-sm font-semibold uppercase tracking-wide text-tertiary">{t.comingSoon}</div>
<div className="mt-1 text-base font-medium text-on-surface">{t.comingSoonText}</div>
</motion.div>
<motion.div className="app-card app-card-glow p-6 rounded-xl flex flex-col md:flex-row md:items-center md:justify-between gap-6" variants={variants.card}>
<div>
<h3 className="font-headline text-2xl font-bold text-on-surface">{t.communityTitle}</h3>
<p className="mt-2 text-on-surface-variant">{t.communityText}</p>
</div>
<div className="flex flex-wrap gap-3">
<Link to="https://frikadellental.de" className="btn-social">
<FaArrowUpRightFromSquare /> frikadellental.de
</Link>
<Link to="/mods" className="btn-social">
<FaShop /> {t.availableModsLabel}
</Link>
<Link to="https://github.com/mleem97/gregFramework" className="btn-social">
<FaGithub /> {t.repositoryLabel}
</Link>
<Link to="https://discord.gg/greg" className="btn-social bg-[#5865F2] border-transparent text-white hover:bg-[#4752C4]">
<FaDiscord /> {t.joinLabel}
</Link>
</div>
</motion.div>
</div>
</motion.section>
<motion.section
id="support"
className="section-surface-alt px-4 py-16"
initial="hidden"
whileInView="show"
viewport={viewport}
variants={variants.section}>
<div className="mx-auto max-w-6xl flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h3 className="font-headline text-2xl font-bold text-on-surface">{t.supportTitle}</h3>
<p className="text-on-surface-variant">{t.supportText}</p>
</div>
<motion.div whileHover={reducedMotion ? undefined : { y: -2, scale: 1.01 }}>
<Link
to="https://github.com/mleem97/gregFramework/issues"
className="btn-primary inline-flex items-center gap-2 rounded-xl px-5 py-3">
<FaLifeRing /> {t.supportCta}
</Link>
</motion.div>
</div>
</motion.section>
</main>
</Layout>
);
}

96
src/pages/mods.tsx Normal file
View File

@@ -0,0 +1,96 @@
import React, {useMemo} from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import {moduleCatalog} from '../data/moduleCatalog';
export default function ModsCatalogPage(): JSX.Element {
const grouped = useMemo(() => {
const plugins = moduleCatalog.filter((entry) => entry.type === 'plugin');
const mods = moduleCatalog.filter((entry) => entry.type === 'mod');
return {plugins, mods};
}, []);
return (
<Layout title="Mods Catalog" description="Dynamic catalog of mods and plugins with wiki and release links.">
<main className="bg-background min-h-screen text-on-surface px-4 py-12">
<section className="mx-auto max-w-6xl mb-10">
<h1 className="font-headline text-4xl font-bold text-on-surface mb-3">Mods & Plugins Catalog</h1>
<p className="text-on-surface-variant">
This page is generated from the module catalog and links each entry to its wiki page, release page, and
download route.
</p>
</section>
<section className="mx-auto max-w-6xl mb-10">
<h2 className="font-headline text-2xl font-bold text-on-surface mb-4">Plugins</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{grouped.plugins.map((entry) => (
<article key={entry.id} className="app-card app-card-motion app-card-glow rounded-xl p-5">
<h3 className="font-headline text-lg font-bold text-on-surface mb-2">{entry.name}</h3>
<p className="text-sm text-on-surface-variant mb-4">{entry.description}</p>
<p className="text-xs text-on-surface-variant mb-1">Version: {entry.version}</p>
<p className="text-xs text-on-surface-variant mb-4">Languages: {entry.languages.join(', ')}</p>
<div className="flex flex-wrap gap-2">
<Link to={entry.wikiPath} className="button button--secondary button--sm">
Wiki
</Link>
<Link to={entry.releasePath} className="button button--secondary button--sm">
Release
</Link>
{entry.releaseReady ? (
<a href={entry.downloadPath} className="button button--primary button--sm">
Download DLL
</a>
) : (
<span
className="button button--secondary button--sm cursor-not-allowed opacity-80"
role="button"
aria-disabled="true"
tabIndex={-1}>
NotReleasedYet
</span>
)}
</div>
</article>
))}
</div>
</section>
<section className="mx-auto max-w-6xl">
<h2 className="font-headline text-2xl font-bold text-on-surface mb-4">Mods</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{grouped.mods.map((entry) => (
<article key={entry.id} className="app-card app-card-motion app-card-glow rounded-xl p-5">
<h3 className="font-headline text-lg font-bold text-on-surface mb-2">{entry.name}</h3>
<p className="text-sm text-on-surface-variant mb-4">{entry.description}</p>
<p className="text-xs text-on-surface-variant mb-1">Version: {entry.version}</p>
<p className="text-xs text-on-surface-variant mb-4">Dependencies: {entry.dependencies.join(', ')}</p>
<div className="flex flex-wrap gap-2">
<Link to={entry.wikiPath} className="button button--secondary button--sm">
Wiki
</Link>
<Link to={entry.releasePath} className="button button--secondary button--sm">
Release
</Link>
{entry.releaseReady ? (
<a href={entry.downloadPath} className="button button--primary button--sm">
Download DLL
</a>
) : (
<span
className="button button--secondary button--sm cursor-not-allowed opacity-80"
role="button"
aria-disabled="true"
tabIndex={-1}>
NotReleasedYet
</span>
)}
</div>
</article>
))}
</div>
</section>
</main>
</Layout>
);
}

25
src/pages/wiki.tsx Normal file
View File

@@ -0,0 +1,25 @@
import React, {useEffect} from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function WikiLandingPage(): JSX.Element {
useEffect(() => {
if (typeof window !== 'undefined') {
window.location.replace('/wiki/docs');
}
}, []);
return (
<Layout title="Wiki" description="Wiki entrypoint">
<main className="bg-background min-h-screen text-on-surface px-4 py-16">
<section className="mx-auto max-w-3xl text-center app-card app-card-glow glass-card rounded-xl p-8">
<h1 className="font-headline text-3xl font-bold text-on-surface mb-3">Wiki</h1>
<p className="text-on-surface-variant mb-6">Redirecting to the wiki overview...</p>
<Link to="/wiki/docs" className="button button--primary">
Open Wiki Overview
</Link>
</section>
</main>
</Layout>
);
}