"use client"; import { useEffect, useState } from "react"; import { fetchPersonalityDrift, listCanonWorks, listCharacters, proposeEpilogueMilestone, setTimeFlow, type CanonWork, type CharacterSummary, type DriftedPersonality, type MilestoneProposalResult, type TimeFlowMode, } from "../../lib/api"; const TIME_FLOW_LABELS: Record = { FAST: "快轉", REALTIME: "現實同步(1:1)", SLOW: "放慢", FROZEN: "凍結", }; // N-6 後日談畫面:時間流速切換、人格漂移檢視、里程碑提案——三者都依「推演深淺」(storyOrder)運作, // 預設值抓作品目前的 anchorStoryOrder,使用者可自行調整。 export function EpilogueView() { const [works, setWorks] = useState([]); const [workId, setWorkId] = useState(""); const [work, setWork] = useState(null); const [characters, setCharacters] = useState([]); const [characterId, setCharacterId] = useState(""); const [storyOrder, setStoryOrder] = useState(0); const [timeFlowError, setTimeFlowError] = useState(null); const [drift, setDrift] = useState(null); const [milestoneResult, setMilestoneResult] = useState(null); const [pending, setPending] = useState(false); useEffect(() => { void listCanonWorks().then(setWorks); }, []); useEffect(() => { const selected = works.find((w) => w.id === workId) ?? null; setWork(selected); setStoryOrder(selected?.anchorStoryOrder ?? 0); setDrift(null); setMilestoneResult(null); setTimeFlowError(null); if (workId) { void listCharacters().then((all) => setCharacters(all.filter((c) => c.workId === workId))); } else { setCharacters([]); } }, [workId, works]); async function handleSetTimeFlow(mode: TimeFlowMode) { if (!workId || pending) return; setPending(true); setTimeFlowError(null); try { const updated = await setTimeFlow(workId, mode); setWork(updated); setWorks((prev) => prev.map((w) => (w.id === updated.id ? updated : w))); } catch (err) { // N-6 驗收:受限情境要顯示被拒原因,這裡把 setTimeFlow() 丟出的後端 message 原樣呈現。 setTimeFlowError(err instanceof Error ? err.message : "時間流速設定失敗"); } finally { setPending(false); } } async function handleViewDrift() { if (!characterId || pending) return; setPending(true); try { setDrift(await fetchPersonalityDrift(characterId, storyOrder)); } catch { setDrift(null); } finally { setPending(false); } } async function handleProposeMilestone() { if (!characterId || pending) return; setPending(true); try { setMilestoneResult(await proposeEpilogueMilestone(characterId, storyOrder)); } catch { setMilestoneResult(null); } finally { setPending(false); } } return (
{work && ( <>

時間流速

目前:{TIME_FLOW_LABELS[work.timeFlowMode]} {work.inEpilogue ? ".已進入後日談" : ".尚未進入後日談"}

{(Object.keys(TIME_FLOW_LABELS) as TimeFlowMode[]).map((mode) => ( ))}
{timeFlowError &&

{timeFlowError}

}
{characterId && ( <>
setStoryOrder(Number(event.target.value))} className="w-24 rounded-full border bg-transparent px-3 py-1" style={{ borderColor: "var(--line)", color: "var(--ink)" }} />

人格漂移

{drift && (

性格核心:{drift.archetype}(不變).外顯度:{drift.expressiveness.toFixed(2)}(位移 {drift.driftAmount >= 0 ? "+" : ""} {drift.driftAmount.toFixed(2)}).情緒衰減倍率:{drift.emotionHalfLifeMultiplier.toFixed(2)}

)}

人生里程碑

{milestoneResult && (

{milestoneResult.label ? `提案成功:${milestoneResult.label}` : "此時沒有里程碑可提案(可能離上次太近,或角色目標裡沒有對應的正史伏筆)"}

)}
)} )}
); }