Update TTS providers and story markup

This commit is contained in:
2026-05-20 22:13:31 +02:00
parent b911c40d89
commit 8258ea2321
36 changed files with 1482 additions and 197 deletions
+69 -2
View File
@@ -129,6 +129,7 @@ class InkEngine {
const paragraphs = [];
const globalTags = [];
const turnTags = [];
let pendingParagraphTags = [];
while (this.story.canContinue) {
const rawText = this.story.Continue();
const text = String(rawText || '').trim();
@@ -138,12 +139,26 @@ class InkEngine {
.filter((tag) => tag.key === 'title' || tag.key === 'author')
.forEach((tag) => globalTags.push(tag));
if (text) {
paragraphs.push({ text, tags });
const paragraphTags = this.reassignTrailingGlossTags(text, [...pendingParagraphTags, ...tags], paragraphs);
pendingParagraphTags = [];
paragraphs.push({ text, tags: paragraphTags });
}
else {
tags.forEach((tag) => globalTags.push(tag));
const paragraphTags = this.reassignTrailingGlossTags('', tags, paragraphs);
paragraphTags.forEach((tag) => {
if (this.isParagraphScopedTag(tag)) {
pendingParagraphTags.push(tag);
}
else {
globalTags.push(tag);
}
});
}
}
if (pendingParagraphTags.length > 0) {
globalTags.push(...pendingParagraphTags);
pendingParagraphTags = [];
}
const choices = this.story.currentChoices.map((choice) => {
const tags = this.getChoiceTags(choice);
const category = (0, tag_parser_1.getTagValue)(tags, 'action');
@@ -195,6 +210,58 @@ class InkEngine {
gameState: Object.keys(gameState).length > 0 ? gameState : undefined,
};
}
isParagraphScopedTag(tag) {
const key = String(tag?.key || '').toLowerCase();
return ['chapter', 'heading', 'section', 'textblock', 'image', 'music', 'sfx', 'sound', 'audio', 'gloss', 'tts']
.includes(key) || key.startsWith('tts-');
}
reassignTrailingGlossTags(text, tags, paragraphs) {
if (!Array.isArray(tags) || tags.length === 0)
return [];
const previous = paragraphs.length > 0 ? paragraphs[paragraphs.length - 1] : null;
if (!previous)
return tags;
const currentText = this.normalizeGlossMatchText(text);
const previousText = this.normalizeGlossMatchText(previous.text);
const remainingTags = [];
tags.forEach((tag) => {
if (tag.key === 'tts' || tag.key.startsWith('tts-')) {
if (!currentText) {
previous.tags.push(tag);
}
else {
remainingTags.push(tag);
}
return;
}
if (tag.key !== 'gloss') {
remainingTags.push(tag);
return;
}
const term = this.normalizeGlossMatchText(tag.value || '');
if (!term) {
remainingTags.push(tag);
return;
}
const matchesCurrent = currentText.includes(term);
const matchesPrevious = previousText.includes(term);
if (!matchesCurrent && matchesPrevious) {
previous.tags.push(tag);
}
else {
remainingTags.push(tag);
}
});
return remainingTags;
}
normalizeGlossMatchText(value) {
return String(value || '')
.normalize('NFC')
.toLocaleLowerCase('de-DE')
.replace(/[.,;:!?()[\]{}"'„“”‚‘’»«]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
getChoiceTags(choice) {
const directTags = (0, tag_parser_1.parseTags)(choice?.tags || []);
const previewTags = this.extractChoicePreviewTags(choice);