|
1 | 1 | // src/utils/chatUtils.ts |
2 | 2 | import l2d from '@/utils/json/l2d.json' |
3 | 3 |
|
4 | | -// Helper to identify speaker labels |
5 | | -const isSpeakerLabel = (s: string) => /^\s*(?:\*\*)?[^*]+?(?:\*\*)?\s*:\s*$/.test(s) |
| 4 | +// Helper to identify speaker labels. |
| 5 | +// Supports plain "Name:" and bolded "**Name:**" (colon inside the bold). |
| 6 | +const isSpeakerLabel = (s: string) => { |
| 7 | + const normalized = (s || '').replace(/\u00A0/g, ' ').trim() |
| 8 | + return /^\s*(?:\*\*)?\s*[^*:\n]+?\s*:\s*(?:\*\*)?\s*$/.test(normalized) |
| 9 | +} |
6 | 10 |
|
7 | 11 | // Helper to check if a word appears as a whole word in text (case-insensitive) |
8 | 12 | export const isWholeWordPresent = (text: string, word: string): boolean => { |
@@ -277,13 +281,18 @@ export const sanitizeActions = (actions: any[]): any[] => { |
277 | 281 | while ((match = dialogueRegex.exec(text)) !== null) { |
278 | 282 | // Add any narration before this dialogue |
279 | 283 | const narrationBefore = text.substring(lastIndex, match.index).trim() |
280 | | - if (narrationBefore) { |
| 284 | + // If this chunk is ONLY a speaker label (e.g. "**Chime:**"), merge it into the dialogue. |
| 285 | + // This prevents creating a separate narration action for the label. |
| 286 | + const isLabelOnly = !!narrationBefore && isSpeakerLabel(narrationBefore) |
| 287 | + |
| 288 | + if (narrationBefore && !isLabelOnly) { |
281 | 289 | parts.push({ text: narrationBefore, isDialogue: false }) |
282 | 290 | } |
283 | | - |
| 291 | + |
284 | 292 | // Add the dialogue (with quotes) |
285 | 293 | const dialogue = match[0] // Full match including quotes |
286 | | - parts.push({ text: dialogue, isDialogue: true }) |
| 294 | + const mergedDialogue = isLabelOnly ? `${narrationBefore} ${dialogue}`.trim() : dialogue |
| 295 | + parts.push({ text: mergedDialogue, isDialogue: true }) |
287 | 296 |
|
288 | 297 | lastIndex = match.index + match[0].length |
289 | 298 | } |
|
0 commit comments