初始仓库迁移
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-04 23:57:06 +08:00
parent 80986b790d
commit c49c64896a
18446 changed files with 532435 additions and 2 deletions

View File

@@ -0,0 +1,239 @@
import { AnimatePresence, motion } from 'motion/react';
import type { ReactNode } from 'react';
import { CHROME_ICONS, getNineSliceStyle, UI_CHROME } from '../uiAssets';
import { PixelIcon } from './PixelIcon';
interface CustomWorldCreatorModalProps {
isOpen: boolean;
draft: string;
onDraftChange: (value: string) => void;
onClose: () => void;
onSubmit: () => void;
isGenerating: boolean;
progress: number;
progressLabel: string;
error: string | null;
}
interface CharacterDraftModalProps {
isOpen: boolean;
characterLabel: string;
draftName: string;
draftBackstory: string;
onNameChange: (value: string) => void;
onBackstoryChange: (value: string) => void;
onClose: () => void;
onConfirm: () => void;
error: string | null;
}
function ModalShell({
isOpen,
title,
subtitle,
onClose,
disableClose = false,
children,
}: {
isOpen: boolean;
title: string;
subtitle?: string;
onClose: () => void;
disableClose?: boolean;
children: ReactNode;
}) {
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[90] flex items-center justify-center bg-black/72 p-4 backdrop-blur-sm"
onClick={disableClose ? undefined : onClose}
>
<motion.div
initial={{ opacity: 0, scale: 0.96, y: 8 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.96, y: 8 }}
transition={{ duration: 0.18, ease: 'easeOut' }}
className="pixel-nine-slice pixel-modal-shell flex w-full max-w-2xl flex-col overflow-hidden shadow-[0_24px_80px_rgba(0,0,0,0.55)]"
style={getNineSliceStyle(UI_CHROME.modalPanel)}
onClick={event => event.stopPropagation()}
>
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
<div className="min-w-0">
<div className="text-sm font-semibold text-white">{title}</div>
{subtitle ? (
<div className="mt-1 text-xs leading-relaxed text-zinc-400">{subtitle}</div>
) : null}
</div>
<button
type="button"
onClick={onClose}
disabled={disableClose}
className={`rounded-full border border-white/10 bg-black/20 p-2 text-zinc-400 transition-colors hover:text-white ${disableClose ? 'cursor-not-allowed opacity-40' : ''}`}
>
<PixelIcon src={CHROME_ICONS.close} className="h-4 w-4" />
</button>
</div>
<div className="p-5">{children}</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
}
export function CustomWorldCreatorModal({
isOpen,
draft,
onDraftChange,
onClose,
onSubmit,
isGenerating,
progress,
progressLabel,
error,
}: CustomWorldCreatorModalProps) {
return (
<ModalShell
isOpen={isOpen}
title="创建自定义世界"
onClose={onClose}
disableClose={isGenerating}
>
<div className="space-y-4">
<label className="block">
<div className="mb-3 text-xs font-bold tracking-[0.16em] text-white"></div>
<textarea
value={draft}
onChange={event => onDraftChange(event.target.value)}
disabled={isGenerating}
placeholder="例如:一个被古老机关城与修真宗门共同争夺的边境世界,灵气潮汐会周期性改写地形,玩家需要在多个势力之间周旋,寻找导致世界裂缝扩大的真正原因。"
className="min-h-[22rem] w-full resize-none rounded-[1.75rem] border border-transparent bg-black/18 px-5 py-4 text-sm leading-7 text-zinc-100 outline-none transition-[background-color,box-shadow] placeholder:text-zinc-500 focus:bg-black/24 focus:shadow-[inset_0_0_0_1px_rgba(125,211,252,0.22)]"
/>
</label>
{(isGenerating || progress > 0) && (
<div className="rounded-2xl border border-sky-400/18 bg-sky-500/10 px-4 py-4">
<div className="flex items-center justify-between gap-3">
<div className="text-sm font-semibold text-white">{progressLabel}</div>
<div className="text-xs text-sky-100">{Math.round(progress)}%</div>
</div>
<div className="mt-3 h-3 overflow-hidden rounded-full border border-white/10 bg-black/35">
<div
className="h-full bg-[linear-gradient(90deg,#38bdf8_0%,#67e8f9_48%,#fef08a_100%)] transition-[width] duration-300"
style={{ width: `${Math.max(0, Math.min(100, progress))}%` }}
/>
</div>
</div>
)}
{error ? (
<div className="rounded-2xl border border-rose-400/18 bg-rose-500/10 px-4 py-3 text-sm leading-6 text-rose-100">
{error}
</div>
) : null}
<div className="flex justify-end gap-3">
<button
type="button"
onClick={onClose}
disabled={isGenerating}
className={`inline-flex min-w-24 items-center justify-center rounded-xl border border-white/10 bg-white/5 px-5 py-2.5 text-sm text-zinc-300 transition-colors hover:bg-white/10 hover:text-white ${isGenerating ? 'cursor-not-allowed opacity-40' : ''}`}
>
</button>
<button
type="button"
onClick={onSubmit}
disabled={isGenerating}
className={`pixel-nine-slice pixel-pressable text-left ${isGenerating ? 'cursor-wait opacity-60' : ''}`}
style={getNineSliceStyle(UI_CHROME.choiceButton, { paddingX: 16, paddingY: 10 })}
>
<div className="flex items-center justify-between gap-4">
<span className="text-sm font-semibold text-white">{isGenerating ? '正在生成世界...' : '确认并开始生成'}</span>
<span className="text-white/60">{isGenerating ? '...' : '→'}</span>
</div>
</button>
</div>
</div>
</ModalShell>
);
}
export function CharacterDraftModal({
isOpen,
characterLabel,
draftName,
draftBackstory,
onNameChange,
onBackstoryChange,
onClose,
onConfirm,
error,
}: CharacterDraftModalProps) {
return (
<ModalShell
isOpen={isOpen}
title="自定义角色背景"
subtitle={`你正在修改 ${characterLabel} 的角色名称与背景故事。`}
onClose={onClose}
>
<div className="space-y-4">
<div className="rounded-2xl border border-white/8 bg-black/18 px-4 py-3 text-sm leading-7 text-zinc-300">
</div>
<label className="block">
<div className="mb-2 text-xs font-bold tracking-[0.14em] text-white"></div>
<input
value={draftName}
onChange={event => onNameChange(event.target.value)}
placeholder="输入新的角色名称"
className="w-full rounded-2xl border border-white/10 bg-black/25 px-4 py-3 text-sm text-zinc-100 outline-none transition-colors placeholder:text-zinc-500 focus:border-sky-300/35"
/>
</label>
<label className="block">
<div className="mb-2 text-xs font-bold tracking-[0.14em] text-white"></div>
<textarea
value={draftBackstory}
onChange={event => onBackstoryChange(event.target.value)}
placeholder="写下这名角色进入世界前后的经历、动机、执念、秘密或人与人之间的纠葛。"
className="min-h-44 w-full rounded-2xl border border-white/10 bg-black/25 px-4 py-3 text-sm leading-7 text-zinc-100 outline-none transition-colors placeholder:text-zinc-500 focus:border-sky-300/35"
/>
</label>
{error ? (
<div className="rounded-2xl border border-rose-400/18 bg-rose-500/10 px-4 py-3 text-sm leading-6 text-rose-100">
{error}
</div>
) : null}
<div className="flex justify-end gap-3">
<button
type="button"
onClick={onClose}
className="rounded-full border border-white/10 bg-black/20 px-4 py-2 text-sm text-zinc-300 transition-colors hover:text-white"
>
</button>
<button
type="button"
onClick={onConfirm}
className="pixel-nine-slice pixel-pressable text-left"
style={getNineSliceStyle(UI_CHROME.choiceButton, { paddingX: 16, paddingY: 10 })}
>
<div className="flex items-center justify-between gap-4">
<span className="text-sm font-semibold text-white"></span>
<span className="text-white/60"></span>
</div>
</button>
</div>
</div>
</ModalShell>
);
}