"use client"; import { useMemo, useState } from "react"; import { AppShell } from "@/components/AppShell"; import { CharacterGenerator } from "@/components/generation/CharacterGenerator"; import { WorldGenerator } from "@/components/generation/WorldGenerator"; import type { CharacterCardView, ProjectResponse, WorldEntityCardView, } from "@/lib/api/types"; import { mergeCharacterCards, worldEntityRules, } from "@/lib/generation/cards"; type CodexTab = "characters" | "world" | "timeline"; interface CodexPageProps { project: ProjectResponse; // 后端读端点(GET .../characters / .../world_entities)拉来的跨会话全量真源。 initialCharacters: CharacterCardView[]; initialWorldEntities: WorldEntityCardView[]; // 进页可经 ?gen=character|world 直接打开对应生成器(命令面板动作跳转)。 initialTab?: CodexTab; } const TABS: { key: CodexTab; label: string }[] = [ { key: "characters", label: "人物" }, { key: "world", label: "世界观" }, { key: "timeline", label: "时间线" }, ]; // 设定库 Codex(UX §6.5):管理人物 / 世界观 / 时间线。 // 初始列表来自后端读端点(跨会话全量真源);本会话新入库的角色卡再合并补显, // 故刷新后不再为空、且不与真源重复(按 name 去重,见 mergeCharacterCards)。 // 世界观本期无入库端点(仅生成预览)→ 展示真源 + 生成器预览。时间线为 P2/派生,暂占位。 export function CodexPage({ project, initialCharacters, initialWorldEntities, initialTab = "characters", }: CodexPageProps) { const [tab, setTab] = useState(initialTab); // 本会话内新入库的角色(即时回显,无需刷新即见)。 const [sessionCards, setSessionCards] = useState([]); // 真源(初始全量)+ 本会话新卡,按 name 去重合并。 const characters = useMemo( () => mergeCharacterCards(initialCharacters, sessionCards), [initialCharacters, sessionCards], ); return (
{TABS.map((t) => ( ))}
{tab === "characters" ? (

已入库人物({characters.length})

{characters.length > 0 ? (
    {characters.map((c, i) => (
  • {c.name}({c.role})
  • ))}
) : (

暂无入库人物,先在下方生成并入库。

)}
setSessionCards((prev) => [...prev, ...cards]) } />
) : null} {tab === "world" ? (

已入库世界观({initialWorldEntities.length})

{initialWorldEntities.length > 0 ? (
{initialWorldEntities.map((entity, i) => (
{entity.name} {entity.type}
{worldEntityRules(entity).length > 0 ? (
    {worldEntityRules(entity).map((rule, j) => (
  • {rule}
  • ))}
) : (

(无显式硬规则)

)}
))}
) : (

暂无入库世界观,可在下方生成预览参考。

)}
) : null} {tab === "timeline" ? (
时间线为派生视图(P2),将由章节摘要 + 伏笔窗口自动汇总,敬请期待。
) : null}
); }