feat(play): add history dialog
Signed-off-by: baizhi958216 <1475289190@qq.com>
This commit is contained in:
+74
-1
@@ -11,7 +11,11 @@ import {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { PlayCanvas, type Phase } from "@/components/PlayCanvas";
|
||||
import {
|
||||
PlayCanvas,
|
||||
type Phase,
|
||||
} from "@/components/PlayCanvas";
|
||||
import type { DialogueHistoryItem } from "@/components/DialogueHistoryModal";
|
||||
import { TtsKeyModal } from "@/components/TtsKeyModal";
|
||||
import { annotateClick } from "@/lib/annotateClient";
|
||||
import { loadClientTtsConfig } from "@/lib/clientTtsConfig";
|
||||
@@ -262,6 +266,63 @@ type ScenePathStep = {
|
||||
exit: { choiceId: string; label: string; nextSceneSeed: string };
|
||||
};
|
||||
|
||||
function buildDialogueHistory(
|
||||
session: Session | null,
|
||||
currentSceneId: string | undefined,
|
||||
currentVisitedBeatIds: string[],
|
||||
): DialogueHistoryItem[] {
|
||||
if (!session) return [];
|
||||
|
||||
return session.history.flatMap((entry, sceneIndex) => {
|
||||
const beatsById = new Map(entry.scene.beats.map((b) => [b.id, b]));
|
||||
const visitedBeatIds =
|
||||
entry.scene.id === currentSceneId
|
||||
? currentVisitedBeatIds
|
||||
: entry.visitedBeatIds;
|
||||
|
||||
return visitedBeatIds.flatMap((beatId, beatIndex) => {
|
||||
const beat = beatsById.get(beatId);
|
||||
if (!beat) return [];
|
||||
|
||||
const nextVisitedBeatId = visitedBeatIds[beatIndex + 1];
|
||||
const choice =
|
||||
beat.next.type === "choice"
|
||||
? beat.next.choices.find((c) => {
|
||||
if (c.effect.kind === "advance-beat") {
|
||||
return c.effect.targetBeatId === nextVisitedBeatId;
|
||||
}
|
||||
return (
|
||||
beatIndex === visitedBeatIds.length - 1 &&
|
||||
entry.exit?.kind === "choice" &&
|
||||
c.id === entry.exit.choiceId
|
||||
);
|
||||
})
|
||||
: undefined;
|
||||
const freeformAction =
|
||||
beatIndex === visitedBeatIds.length - 1 &&
|
||||
entry.exit?.kind === "freeform"
|
||||
? entry.exit.action
|
||||
: undefined;
|
||||
|
||||
const body = beat.speaker ? beat.line : beat.narration;
|
||||
const narration = beat.speaker ? beat.narration : undefined;
|
||||
if (!body && !narration && !choice && !freeformAction) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
id: `${sceneIndex}:${beatId}:${beatIndex}`,
|
||||
sceneIndex: sceneIndex + 1,
|
||||
speaker: beat.speaker,
|
||||
body,
|
||||
narration,
|
||||
selectedChoice: choice?.label,
|
||||
freeformAction,
|
||||
},
|
||||
];
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pathKey(steps: ScenePathStep[]): string {
|
||||
return steps.map((s) => s.exit.choiceId).join("/");
|
||||
}
|
||||
@@ -549,6 +610,16 @@ function PlayInner() {
|
||||
return currentScene.beats.find((b) => b.id === currentBeatId) ?? null;
|
||||
}, [currentScene, currentBeatId]);
|
||||
|
||||
const dialogueHistory = useMemo<DialogueHistoryItem[]>(
|
||||
() =>
|
||||
buildDialogueHistory(
|
||||
session,
|
||||
currentScene?.id,
|
||||
visitedBeatsRef.current,
|
||||
),
|
||||
[session, currentScene?.id, currentBeatId],
|
||||
);
|
||||
|
||||
const audioSrc = (currentBeat ? beatAudioMap[currentBeat.id] : undefined) ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1369,6 +1440,7 @@ function PlayInner() {
|
||||
onSelectChoice={onSelectChoice}
|
||||
orientation={orientation}
|
||||
fullViewport
|
||||
dialogueHistory={dialogueHistory}
|
||||
/>
|
||||
{orientation === "portrait" && (
|
||||
<div
|
||||
@@ -1442,6 +1514,7 @@ function PlayInner() {
|
||||
onAdvance={onAdvance}
|
||||
onSelectChoice={onSelectChoice}
|
||||
orientation={orientation}
|
||||
dialogueHistory={dialogueHistory}
|
||||
aboveCanvas={
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user