mirror of
https://github.com/mleem97/gregWiki.git
synced 2026-04-11 03:29:19 +02:00
feat(homepage): add codeSnippetCaption to localization and integrate snippet component
- Added 'codeSnippetCaption' to the English localization file for improved content clarity. - Updated the homepage to include the GregCoreRandomSnippet component, enhancing user engagement with dynamic content. This commit enriches the homepage experience by providing localized captions and integrating a new snippet feature.
This commit is contained in:
61
src/components/GregCoreRandomSnippet.tsx
Normal file
61
src/components/GregCoreRandomSnippet.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import React, {useMemo} from 'react';
|
||||||
|
import BrowserOnly from '@docusaurus/BrowserOnly';
|
||||||
|
import {GREG_CORE_SNIPPETS} from '../data/gregCoreSnippets';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
/** e.g. "Random sample from gregCore" */
|
||||||
|
caption: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Picks one snippet per client mount (stable for that visit; no SSR/CSR mismatch).
|
||||||
|
*/
|
||||||
|
function GregCoreRandomSnippetInner({caption}: Props): JSX.Element {
|
||||||
|
const snippet = useMemo(
|
||||||
|
() => GREG_CORE_SNIPPETS[Math.floor(Math.random() * GREG_CORE_SNIPPETS.length)],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-xl border border-outline-variant/20 bg-surface-container-highest p-1 shadow-2xl">
|
||||||
|
<div className="rounded-lg bg-surface-container-lowest p-6 font-mono text-sm">
|
||||||
|
<div className="mb-3 flex flex-wrap items-center justify-between gap-2 border-b border-outline-variant/15 pb-3">
|
||||||
|
<div>
|
||||||
|
<div className="text-xs font-bold uppercase tracking-wider text-primary">{caption}</div>
|
||||||
|
<div className="mt-1 text-xs text-on-surface-variant">
|
||||||
|
<span className="font-semibold text-on-surface">{snippet.title}</span>
|
||||||
|
<span className="mx-1.5 text-outline-variant">·</span>
|
||||||
|
<code className="text-[11px] text-tertiary">{snippet.sourcePath}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2" aria-hidden>
|
||||||
|
<div className="h-3 w-3 rounded-full bg-error-dim" />
|
||||||
|
<div className="h-3 w-3 rounded-full bg-tertiary" />
|
||||||
|
<div className="h-3 w-3 rounded-full bg-primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<pre className="max-h-[min(420px,55vh)] overflow-auto whitespace-pre pr-1 leading-relaxed text-on-surface/90">
|
||||||
|
{snippet.code.trimEnd()}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GregCoreRandomSnippet(props: Props): JSX.Element {
|
||||||
|
return (
|
||||||
|
<BrowserOnly
|
||||||
|
fallback={
|
||||||
|
<div
|
||||||
|
className="rounded-xl border border-outline-variant/20 bg-surface-container-highest p-1 shadow-2xl"
|
||||||
|
aria-busy
|
||||||
|
>
|
||||||
|
<div className="flex min-h-[280px] items-center justify-center rounded-lg bg-surface-container-lowest p-6">
|
||||||
|
<span className="text-sm text-on-surface-variant">Loading code sample…</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}>
|
||||||
|
{() => <GregCoreRandomSnippetInner {...props} />}
|
||||||
|
</BrowserOnly>
|
||||||
|
);
|
||||||
|
}
|
||||||
118
src/data/gregCoreSnippets.ts
Normal file
118
src/data/gregCoreSnippets.ts
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Short excerpts from gregCore (gregFramework monorepo) for the homepage.
|
||||||
|
* Kept in sync conceptually with Core/ and Hooks/ — adjust if APIs move.
|
||||||
|
*/
|
||||||
|
export type GregCoreSnippet = {
|
||||||
|
/** Short title for the card header */
|
||||||
|
title: string;
|
||||||
|
/** Path inside the gregFramework repo */
|
||||||
|
sourcePath: string;
|
||||||
|
code: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GREG_CORE_SNIPPETS: readonly GregCoreSnippet[] = [
|
||||||
|
{
|
||||||
|
title: 'GregHookName',
|
||||||
|
sourcePath: 'gregCore/Core/GregHookName.cs',
|
||||||
|
code: `public static string Create(GregDomain domain, string action)
|
||||||
|
=> Create(domain, action, null);
|
||||||
|
|
||||||
|
public static string Create(GregDomain domain, string action, string subject)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(action))
|
||||||
|
throw new ArgumentException("Action ist erforderlich.", nameof(action));
|
||||||
|
// → greg.PLAYER.MoneyChanged, greg.EMPLOYEE.Hired, …
|
||||||
|
var domainPart = DomainToSegment(domain);
|
||||||
|
// …
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GregDomain',
|
||||||
|
sourcePath: 'gregCore/Core/GregDomain.cs',
|
||||||
|
code: `public enum GregDomain
|
||||||
|
{
|
||||||
|
Gameplay,
|
||||||
|
Player,
|
||||||
|
Employee,
|
||||||
|
Customer,
|
||||||
|
Server,
|
||||||
|
Rack,
|
||||||
|
Network,
|
||||||
|
Power,
|
||||||
|
Ui,
|
||||||
|
System
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GregEventDispatcher',
|
||||||
|
sourcePath: 'gregCore/Core/GregEventDispatcher.cs',
|
||||||
|
code: `public static void On(string hookName, Action<object> handler)
|
||||||
|
{
|
||||||
|
if (!_handlers.TryGetValue(hookName, out var list))
|
||||||
|
_handlers[hookName] = list = new List<Action<object>>();
|
||||||
|
list.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Emit(string hookName, object payload = null)
|
||||||
|
{
|
||||||
|
if (!_handlers.TryGetValue(hookName, out var list)) return;
|
||||||
|
foreach (var handler in list.ToArray())
|
||||||
|
try { handler(payload); }
|
||||||
|
catch (Exception ex) { /* MelonLogger.Warning … */ }
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'GregPayload',
|
||||||
|
sourcePath: 'gregCore/Core/GregPayload.cs',
|
||||||
|
code: `public static T Get<T>(object payload, string fieldName, T fallback = default)
|
||||||
|
{
|
||||||
|
if (payload == null) return fallback;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var prop = payload.GetType().GetProperty(
|
||||||
|
fieldName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||||
|
if (prop == null) return fallback;
|
||||||
|
var val = prop.GetValue(payload);
|
||||||
|
return val is T typed ? typed : fallback;
|
||||||
|
}
|
||||||
|
catch { return fallback; }
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Harmony → greg hook',
|
||||||
|
sourcePath: 'gregCore/Hooks/GregPlayerHooks.cs',
|
||||||
|
code: `[HarmonyPatch(typeof(Player), nameof(Player.UpdateCoin))]
|
||||||
|
[HarmonyPostfix]
|
||||||
|
private static void OnMoneyChanged(Player __instance, float amount, bool withoutSound)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GregEventDispatcher.Emit(
|
||||||
|
GregHookName.Create(GregDomain.Player, "MoneyChanged"),
|
||||||
|
new
|
||||||
|
{
|
||||||
|
Amount = amount,
|
||||||
|
NewBalance = __instance.money,
|
||||||
|
WithoutSound = withoutSound
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MelonLogger.Warning($"[gregCore] Hook failed: {ex.Message}");
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Mod subscription',
|
||||||
|
sourcePath: 'mods/GregShowcaseMod (example)',
|
||||||
|
code: `GregEventDispatcher.On(
|
||||||
|
GregHookName.Create(GregDomain.Player, "MoneyChanged"),
|
||||||
|
payload =>
|
||||||
|
{
|
||||||
|
var delta = GregPayload.Get<float>(payload, "Amount");
|
||||||
|
var balance = GregPayload.Get<float>(payload, "NewBalance");
|
||||||
|
MelonLogger.Msg($"Money: {delta:+0.00;-0.00} → {balance}");
|
||||||
|
});`,
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
@@ -49,6 +49,7 @@ export const homepageShellEn: Pick<
|
|||||||
| 'workflowSectionTitle'
|
| 'workflowSectionTitle'
|
||||||
| 'codeSectionTitle'
|
| 'codeSectionTitle'
|
||||||
| 'codeSectionLead'
|
| 'codeSectionLead'
|
||||||
|
| 'codeSnippetCaption'
|
||||||
| 'ctaDiscordTitle'
|
| 'ctaDiscordTitle'
|
||||||
| 'ctaDiscordLead'
|
| 'ctaDiscordLead'
|
||||||
| 'ctaDiscordButton'
|
| 'ctaDiscordButton'
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export type HomepageContent = {
|
|||||||
workflowSectionTitle: string;
|
workflowSectionTitle: string;
|
||||||
codeSectionTitle: string;
|
codeSectionTitle: string;
|
||||||
codeSectionLead: string;
|
codeSectionLead: string;
|
||||||
|
/** Homepage #code — random gregCore excerpt */
|
||||||
|
codeSnippetCaption: string;
|
||||||
ctaDiscordTitle: string;
|
ctaDiscordTitle: string;
|
||||||
ctaDiscordLead: string;
|
ctaDiscordLead: string;
|
||||||
ctaDiscordButton: string;
|
ctaDiscordButton: string;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
FaLifeRing,
|
FaLifeRing,
|
||||||
FaShop,
|
FaShop,
|
||||||
} from 'react-icons/fa6';
|
} from 'react-icons/fa6';
|
||||||
|
import {GregCoreRandomSnippet} from '../components/GregCoreRandomSnippet';
|
||||||
|
|
||||||
/** Always resolves to the newest GitHub release (redirect). */
|
/** Always resolves to the newest GitHub release (redirect). */
|
||||||
const GREG_MODMANAGER_LATEST =
|
const GREG_MODMANAGER_LATEST =
|
||||||
@@ -304,13 +305,13 @@ export default function HomePage(): JSX.Element {
|
|||||||
<span className="material-symbols-outlined mt-0.5 text-primary">
|
<span className="material-symbols-outlined mt-0.5 text-primary">
|
||||||
check_circle
|
check_circle
|
||||||
</span>
|
</span>
|
||||||
<span>MelonLoader + Harmony patches with typed hook names</span>
|
<span>MelonLoader + Harmony: gregCore emits typed <code className="text-xs">greg.*</code> events</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-3">
|
<li className="flex items-start gap-3">
|
||||||
<span className="material-symbols-outlined mt-0.5 text-primary">
|
<span className="material-symbols-outlined mt-0.5 text-primary">
|
||||||
check_circle
|
check_circle
|
||||||
</span>
|
</span>
|
||||||
<span>FFM plugins and FMF mods documented beside release metadata</span>
|
<span>Plugins and mods documented beside release metadata</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex items-start gap-3">
|
<li className="flex items-start gap-3">
|
||||||
<span className="material-symbols-outlined mt-0.5 text-primary">
|
<span className="material-symbols-outlined mt-0.5 text-primary">
|
||||||
@@ -320,35 +321,7 @@ export default function HomePage(): JSX.Element {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-xl border border-outline-variant/20 bg-surface-container-highest p-1 shadow-2xl">
|
<GregCoreRandomSnippet caption={t.codeSnippetCaption} />
|
||||||
<div className="rounded-lg bg-surface-container-lowest p-6 font-mono text-sm">
|
|
||||||
<div className="mb-4 flex gap-2">
|
|
||||||
<div className="h-3 w-3 rounded-full bg-error-dim" />
|
|
||||||
<div className="h-3 w-3 rounded-full bg-tertiary" />
|
|
||||||
<div className="h-3 w-3 rounded-full bg-primary" />
|
|
||||||
</div>
|
|
||||||
<pre className="leading-relaxed text-on-surface/90">
|
|
||||||
<span className="text-error-dim">using</span> MelonLoader;
|
|
||||||
{'\n\n'}
|
|
||||||
<span className="text-tertiary">public</span>{' '}
|
|
||||||
<span className="text-tertiary">sealed</span>{' '}
|
|
||||||
<span className="text-tertiary">class</span>{' '}
|
|
||||||
<span className="text-primary">MyMod</span> : MelonMod
|
|
||||||
{' {\n'}
|
|
||||||
{' '}
|
|
||||||
<span className="text-tertiary">public</span>{' '}
|
|
||||||
<span className="text-tertiary">override</span>{' '}
|
|
||||||
<span className="text-tertiary">void</span>{' '}
|
|
||||||
<span className="text-primary-dim">OnInitializeMelon</span>
|
|
||||||
{'() {\n'}
|
|
||||||
{' '}
|
|
||||||
<span className="text-on-surface-variant">
|
|
||||||
// gregFramework — attach hooks, log, ship.
|
|
||||||
</span>
|
|
||||||
{'\n }\n}'}
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.section>
|
</motion.section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user