feat: Placeholder farbig markieren in Freigabe-View und Pair-Modal

Objekt-Placeholder indigo, Wort-Placeholder grün, geleakte ⟦PHn⟧-Tokens rot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 22:43:46 +02:00
parent 96c436e0e9
commit e78be430c7
4 changed files with 56 additions and 4 deletions

View File

@@ -10,6 +10,26 @@ export function strip(text) {
});
}
// Zerlegt einen Satz in Segmente für die farbige Placeholder-Anzeige:
// { text, kind: 'object' | 'word' | 'broken' | null, id? }
// 'broken' = geleakte ⟦PHn:label⟧-Tokens aus der Übersetzung (sollten nicht vorkommen).
const SEGMENT_RE = /\{\{([^.{}]+)\.(w|o):([0-9a-f-]{36})\}\}|⟦PH\d+:([^⟧]*)⟧/g;
export function parsePlaceholderSegments(text) {
if (!text) return [];
const str = String(text);
const segs = [];
let last = 0;
for (const m of str.matchAll(SEGMENT_RE)) {
if (m.index > last) segs.push({ text: str.slice(last, m.index), kind: null });
if (m[1] !== undefined) segs.push({ text: m[1], kind: m[2] === 'o' ? 'object' : 'word', id: m[3] });
else segs.push({ text: m[4], kind: 'broken' });
last = m.index + m[0].length;
}
if (last < str.length) segs.push({ text: str.slice(last), kind: null });
return segs;
}
// Baut die Anzeigezeilen je nach answer_type. Jede Zeile ist entweder
// - { kind: 'lang', label, color, cell(l) } → eine Zelle pro Sprache (übersetzbar)
// - { kind: 'single', label, color, value } → ein einzelner Wert (nicht sprachabhängig)
@@ -19,13 +39,14 @@ export function buildRows(content) {
const rows = [];
const wordsCell = (stmt) => (l) =>
(stmt?.words || []).map(w => w[`titel_${l}`] || '—').join(', ');
// Roher Text inkl. {{…}}-Placeholder — die Anzeige läuft über <PlaceholderText>.
const sentenceCell = (stmt, prefix) => (l) =>
strip(stmt?.sentence?.[`${prefix}_${l}`] || '');
stmt?.sentence?.[`${prefix}_${l}`] || '';
// Frage (yes_no / question / word)
if (content.question) {
rows.push({ kind: 'lang', label: 'Frage', color: 'text-slate-700',
cell: l => strip(content.question[`sentence_${l}`] || '') });
cell: l => content.question[`sentence_${l}`] || '' });
}
if (type === 'yes_no') {