123 lines
4.4 KiB
TypeScript
123 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
import { useNarrative } from "@/app/game-engine/narrative-provider.tsx";
|
|
import ChevronDownIcon from "@bureaudouble/icons/radix/ChevronDownIcon";
|
|
import { SearchParamController } from "@bureaudouble/react-search-parameter-controller";
|
|
import { cva } from "class-variance-authority";
|
|
import BreathingText from "../ui/breathing-text.tsx";
|
|
import { personaFontVariationsVariants } from "../../game-engine/persona-styles.ts";
|
|
import { useClientLocale } from "../../game-engine/locale-provider.tsx";
|
|
|
|
const statusBarVariants = cva(
|
|
"z-10 flex-none text-xs",
|
|
{
|
|
variants: {
|
|
variant: {
|
|
terminal:
|
|
"p-1 flex flex-wrap gap-x-3 items-center uppercase border-b border-border",
|
|
classical:
|
|
"px-2 pt-2 grid grid-cols-2 auto-rows-min gap-x-1 gap-y-0.5 transition-all duration-500 group-data-[scroll-direction=up]/html:opacity-100 group-data-[scroll-direction=up]/html:h-[7rem] group-data-[scroll-direction=down]/html:opacity-0 group-data-[scroll-direction=down]/html:h-0 *:even:text-right *:even:ml-auto **:data-value:uppercase **:data-short-title:line-clamp-2 **:data-short-title:text-xl **:data-short-title:text-center **:data-short-title:pt-2 **:data-short-title:px-4 **:data-short-title:font-light **:data-short-title:col-span-full",
|
|
},
|
|
},
|
|
},
|
|
);
|
|
|
|
export function StatusBar(
|
|
{ models, modelId }: {
|
|
models: { id: string; name: string }[];
|
|
modelId?: string | null | undefined;
|
|
},
|
|
) {
|
|
const { t } = useClientLocale();
|
|
const { gameState, uiVariant, id } = useNarrative();
|
|
const statusItems = useMemo(() =>
|
|
[
|
|
{
|
|
labelKey: "statusBarStep",
|
|
value: gameState.progressionStep,
|
|
variant: ["terminal"],
|
|
},
|
|
{
|
|
labelKey: uiVariant === "terminal"
|
|
? "statusBarCtxTerminal"
|
|
: "statusBarCtxClassical",
|
|
value: gameState.personaId || "N/A",
|
|
},
|
|
{
|
|
labelKey: "statusBarModality",
|
|
value: gameState.modality?.toUpperCase() || "N/A",
|
|
variant: ["terminal"],
|
|
},
|
|
{
|
|
labelKey: uiVariant === "terminal"
|
|
? "statusBarLustLevelTerminal"
|
|
: "statusBarLustLevelClassical",
|
|
value: gameState.lustExperienceLevel?.toUpperCase() ?? "beginner",
|
|
},
|
|
{
|
|
labelKey: "statusBarClimaxPeak",
|
|
value: gameState.arousalPeakPotential?.toFixed(2),
|
|
variant: ["terminal"],
|
|
},
|
|
{
|
|
labelKey: "statusBarConsent",
|
|
value: gameState.aiConsentLevel?.toFixed(2),
|
|
variant: ["terminal"],
|
|
},
|
|
{ labelKey: "statusBarSid", value: `${id.substring(0, 8)}...` },
|
|
]
|
|
.filter((v) => !v.variant || v.variant.includes(uiVariant)), [
|
|
gameState,
|
|
uiVariant,
|
|
id,
|
|
]);
|
|
|
|
const translatedTitle = gameState.shortTitleSummary === "defaultShortTitle"
|
|
? t("defaultShortTitle")
|
|
: gameState.shortTitleSummary;
|
|
|
|
return (
|
|
<div className={statusBarVariants({ variant: uiVariant })}>
|
|
{statusItems.map((item) => (
|
|
<span key={item.labelKey}>
|
|
<span data-label>{t(item.labelKey as any)}</span>
|
|
{uiVariant === "classical" ? <span className="mx-1" /> : "::"}
|
|
<span data-value>{item.value}</span>
|
|
</span>
|
|
))}
|
|
<label className="text-inherit flex flex-wrap items-center group">
|
|
<span data-label>
|
|
{t("statusBarModel")}
|
|
</span>
|
|
{uiVariant === "classical" ? <span className="mx-1" /> : "::"}
|
|
<SearchParamController
|
|
paramName="model-id"
|
|
currentValue={modelId}
|
|
key={modelId}
|
|
>
|
|
<select
|
|
data-value
|
|
className="max-w-30 truncate text-inherit group-hover:text-primary text-xs rounded-none focus:border-transparent focus:ring-0 focus:outline-none appearance-none cursor-pointer"
|
|
>
|
|
{models.map((model) => (
|
|
<option key={model.id} value={model.id}>
|
|
{model.name.toUpperCase()}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</SearchParamController>
|
|
<ChevronDownIcon className="size-3" />
|
|
</label>
|
|
<BreathingText
|
|
staggerDuration={0.08}
|
|
fromFontVariationSettings={personaFontVariationsVariants(gameState)
|
|
.from}
|
|
toFontVariationSettings={personaFontVariationsVariants(gameState).to}
|
|
data-short-title
|
|
>
|
|
{translatedTitle}
|
|
</BreathingText>
|
|
</div>
|
|
);
|
|
}
|