feat(play): add history dialog
Signed-off-by: baizhi958216 <1475289190@qq.com>
This commit is contained in:
@@ -52,6 +52,42 @@
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.32em;
|
||||
}
|
||||
|
||||
.vn-scrollbar {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: rgba(195, 155, 75, 0.58) rgba(20, 14, 8, 0.32);
|
||||
}
|
||||
|
||||
.vn-scrollbar::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.vn-scrollbar::-webkit-scrollbar-track {
|
||||
background:
|
||||
linear-gradient(
|
||||
to right,
|
||||
transparent 0,
|
||||
transparent 3px,
|
||||
rgba(20, 14, 8, 0.46) 3px,
|
||||
rgba(20, 14, 8, 0.46) 5px,
|
||||
transparent 5px
|
||||
);
|
||||
}
|
||||
|
||||
.vn-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: rgba(195, 155, 75, 0.52);
|
||||
border: 2px solid rgba(14, 10, 6, 0.88);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.vn-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(220, 180, 95, 0.76);
|
||||
}
|
||||
|
||||
.vn-scrollbar::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes infiplot-ripple {
|
||||
|
||||
+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