Add project dialogue library and related functions

This commit is contained in:
2026-04-14 19:53:23 +02:00
parent a086fe62c1
commit aa96b35aa5

View File

@@ -0,0 +1,143 @@
import { createOpaqueId } from "../core/ids";
export interface ProjectDialogueLine {
id: string;
speakerName: string | null;
text: string;
}
export interface ProjectDialogue {
id: string;
title: string;
lines: ProjectDialogueLine[];
}
export interface ProjectDialogueLibrary {
dialogues: Record<string, ProjectDialogue>;
}
export function createEmptyProjectDialogueLibrary(): ProjectDialogueLibrary {
return {
dialogues: {}
};
}
export function createProjectDialogueLine(
overrides: Partial<ProjectDialogueLine> = {}
): ProjectDialogueLine {
return {
id: overrides.id ?? createOpaqueId("dialogue-line"),
speakerName: overrides.speakerName?.trim() || null,
text: overrides.text ?? "..."
};
}
export function createProjectDialogue(
overrides: Partial<Pick<ProjectDialogue, "id" | "title">> & {
lines?: ProjectDialogueLine[];
} = {}
): ProjectDialogue {
return {
id: overrides.id ?? createOpaqueId("dialogue"),
title: overrides.title?.trim() || "Untitled Dialogue",
lines:
overrides.lines?.map(cloneProjectDialogueLine) ?? [
createProjectDialogueLine()
]
};
}
export function cloneProjectDialogueLine(
line: ProjectDialogueLine
): ProjectDialogueLine {
return {
id: line.id,
speakerName: line.speakerName,
text: line.text
};
}
export function cloneProjectDialogue(
dialogue: ProjectDialogue
): ProjectDialogue {
return {
id: dialogue.id,
title: dialogue.title,
lines: dialogue.lines.map(cloneProjectDialogueLine)
};
}
export function cloneProjectDialogueLibrary(
library: ProjectDialogueLibrary
): ProjectDialogueLibrary {
return {
dialogues: Object.fromEntries(
Object.entries(library.dialogues).map(([dialogueId, dialogue]) => [
dialogueId,
cloneProjectDialogue(dialogue)
])
)
};
}
export function areProjectDialogueLinesEqual(
left: ProjectDialogueLine,
right: ProjectDialogueLine
): boolean {
return (
left.id === right.id &&
left.speakerName === right.speakerName &&
left.text === right.text
);
}
export function areProjectDialoguesEqual(
left: ProjectDialogue,
right: ProjectDialogue
): boolean {
if (
left.id !== right.id ||
left.title !== right.title ||
left.lines.length !== right.lines.length
) {
return false;
}
return left.lines.every((line, index) =>
areProjectDialogueLinesEqual(line, right.lines[index]!)
);
}
export function areProjectDialogueLibrariesEqual(
left: ProjectDialogueLibrary,
right: ProjectDialogueLibrary
): boolean {
const leftIds = Object.keys(left.dialogues);
const rightIds = Object.keys(right.dialogues);
if (leftIds.length !== rightIds.length) {
return false;
}
return leftIds.every((dialogueId) => {
const rightDialogue = right.dialogues[dialogueId];
return (
rightDialogue !== undefined &&
areProjectDialoguesEqual(left.dialogues[dialogueId]!, rightDialogue)
);
});
}
export function getProjectDialogues(
library: ProjectDialogueLibrary
): ProjectDialogue[] {
return Object.values(library.dialogues).sort((left, right) => {
const titleCompare = left.title.localeCompare(right.title);
if (titleCompare !== 0) {
return titleCompare;
}
return left.id.localeCompare(right.id);
});
}