import { ArrowLeft, Loader2, Play } from 'lucide-react'; import { useEffect, useMemo, useState } from 'react'; import type { BarkBattleConfigEditorPayload } from '../../../packages/shared/src/contracts/barkBattle'; import type { BarkBattleDifficultyPreset } from '../../../packages/shared/src/contracts/barkBattle'; import { buildBarkBattleDefaultOnomatopoeia } from '../../games/bark-battle/application/BarkBattleConfig'; import { BarkBattlePreviewCard } from './BarkBattlePreviewCard'; export type BarkBattleConfigEditorProps = { isBusy?: boolean; error?: string | null; onPreview: (payload: BarkBattleConfigEditorPayload) => void | Promise; onBack?: () => void; showBackButton?: boolean; title?: string | null; }; const DIFFICULTY_OPTIONS: Array<{ value: BarkBattleDifficultyPreset; label: string }> = [ { value: 'easy', label: '轻松' }, { value: 'normal', label: '标准' }, { value: 'hard', label: '硬核' }, ]; const FIELD_LABEL_CLASS = 'mb-2 inline-flex rounded-full px-2 py-0.5 text-sm font-black text-[var(--platform-text-strong)]'; const ACCENT_FIELD_LABEL_CLASS = 'mb-2 inline-flex rounded-full border border-rose-200/70 bg-rose-50/88 px-2.5 py-1 text-sm font-black text-rose-700 shadow-sm'; const DEFAULT_THEME_DESCRIPTION = '阳光草坪上的圆形声浪擂台'; const DEFAULT_PLAYER_IMAGE_DESCRIPTION = '戴红色围巾的勇敢小狗'; const DEFAULT_OPPONENT_IMAGE_DESCRIPTION = '戴蓝色头带的活力小狗'; function buildDefaultOnomatopoeiaText(params: { themeDescription: string; playerImageDescription: string; opponentImageDescription: string; }) { return buildBarkBattleDefaultOnomatopoeia(params).join('\n'); } export function BarkBattleConfigEditor({ isBusy = false, error: externalError = null, onPreview, onBack, showBackButton = true, title: headingTitle = '汪汪声浪大作战', }: BarkBattleConfigEditorProps) { const [title, setTitle] = useState('我的声浪竞技场'); const [description, setDescription] = useState(''); const [themeDescription, setThemeDescription] = useState( DEFAULT_THEME_DESCRIPTION, ); const [playerImageDescription, setPlayerImageDescription] = useState( DEFAULT_PLAYER_IMAGE_DESCRIPTION, ); const [opponentImageDescription, setOpponentImageDescription] = useState( DEFAULT_OPPONENT_IMAGE_DESCRIPTION, ); const [isOnomatopoeiaCustomized, setIsOnomatopoeiaCustomized] = useState(false); const [onomatopoeiaText, setOnomatopoeiaText] = useState(() => buildDefaultOnomatopoeiaText({ themeDescription: DEFAULT_THEME_DESCRIPTION, playerImageDescription: DEFAULT_PLAYER_IMAGE_DESCRIPTION, opponentImageDescription: DEFAULT_OPPONENT_IMAGE_DESCRIPTION, }), ); const [difficultyPreset, setDifficultyPreset] = useState('normal'); const [localError, setLocalError] = useState(null); useEffect(() => { if (isOnomatopoeiaCustomized) { return; } setOnomatopoeiaText( buildDefaultOnomatopoeiaText({ themeDescription, playerImageDescription, opponentImageDescription, }), ); }, [ isOnomatopoeiaCustomized, themeDescription, playerImageDescription, opponentImageDescription, ]); const onomatopoeia = useMemo( () => onomatopoeiaText .split(/[\n,,、/|]+/u) .map((word) => word.trim()) .filter(Boolean) .slice(0, 24), [onomatopoeiaText], ); const payload = useMemo( () => ({ title: title.trim(), description: description.trim(), themeDescription: themeDescription.trim(), playerImageDescription: playerImageDescription.trim(), opponentImageDescription: opponentImageDescription.trim(), onomatopoeia, difficultyPreset, }), [ title, description, themeDescription, playerImageDescription, opponentImageDescription, onomatopoeia, difficultyPreset, ], ); const runValidatedAction = ( action: (payload: BarkBattleConfigEditorPayload) => void | Promise, ) => { if (!payload.title) { setLocalError('请先填写作品标题'); return; } if (!payload.themeDescription) { setLocalError('请先填写主题/场景描述'); return; } if (!payload.playerImageDescription || !payload.opponentImageDescription) { setLocalError('请先填写双方形象描述'); return; } setLocalError(null); void action(payload); }; const visibleError = localError ?? externalError; return (
{showBackButton && onBack ? (
) : null}
{headingTitle ? (

{headingTitle}

轻配置
) : null}