storytellurx-v1/app/components/chat/chat-actions.tsx

80 lines
2.9 KiB
TypeScript

import cn from "@bureaudouble/outils/cn.ts";
import type { CallToEroticAction } from "../../game-engine/game-schemas.ts";
import { markdownParse } from "../../utils/markdown-parser.ts";
import {
eroticSuggestionContainerVariants,
eroticSuggestionsVariants,
} from "../../game-engine/persona-styles.ts";
import { gameStateToVariantState } from "../../game-engine/persona-styles.ts";
import { PositionalScroller } from "../utils/positional-scroller.tsx";
import { ZipperSVG } from "../vectors/zipper.tsx";
import { LoveLetterSVG } from "../vectors/love-letter.tsx";
export const EroticActionSuggestions: React.FC<{
actions: CallToEroticAction[] | undefined;
variantState: ReturnType<typeof gameStateToVariantState>;
onActionClick: (promptValue: string) => void;
disabled?: boolean;
}> = ({ actions, onActionClick, variantState, disabled }) => {
const parsePartialAction = (
action?: {
displayText?: string | null;
promptValue?: string | null;
style?: string | null;
},
) => {
const [displayText, promptValue, o] = [
action?.promptValue,
action?.displayText,
(action as any)?.value ?? (action as any)?.mode,
].toSorted((a, b) => a?.length - b?.length);
return {
displayText: displayText ?? promptValue ?? o ?? "",
promptValue: promptValue ?? displayText ?? o ?? "",
};
};
return (
<PositionalScroller
className={eroticSuggestionContainerVariants(variantState)}
>
{actions?.map?.((action, index) => (
<button
key={index}
type="button"
onClick={(e) => {
(e.target as HTMLButtonElement).scrollIntoView({
block: "center",
inline: "center",
});
onActionClick(
parsePartialAction(action)?.promptValue,
);
}}
disabled={disabled} // Already checked above, but good for safety
data-suggestion
title={parsePartialAction(action)?.promptValue} // Show persona's rationale on hover
className={cn(
"first:ml-auto last:mr-auto",
"transition-colors duration-150 leading-tight disabled:opacity-60 disabled:cursor-not-allowed",
"data-[intersect-position=center]:z-10 *:line-clamp-4",
eroticSuggestionsVariants({ ...variantState, type: action.style }),
)}
>
<div
dangerouslySetInnerHTML={{
__html: markdownParse(
parsePartialAction(action).displayText,
),
}}
/>
{variantState.personaId === "polaire"
? <ZipperSVG className="size-full inset-0 absolute" />
: variantState.personaId === "cybelle"
? <LoveLetterSVG className="size-full inset-0 absolute hover:opacity-0" />
: null}
</button>
))}
</PositionalScroller>
);
};