feat(gui): Abschnittstitel der Zusammenfassung fett

Die Section-Zusammenfassung kommt als "Titel:\nFliesstext"-Bloecke zurueck, wurde aber
als flache Textwand gerendert. renderSummary() setzt Titelzeilen jetzt fett.

Im Renderer statt in der DB, damit es sofort fuer alle vorhandenen Zusammenfassungen
gilt, ohne 22 Aufnahmen je 20 Minuten neu zu analysieren.

Regel /^[^.!?]{3,90}:$/ = kurze, alleinstehende Zeile mit Doppelpunkt am Ende. Gegen
die echten Zusammenfassungen #85/#79/#74 geprueft: 37 Titel erkannt, keine
Fliesstextzeile falsch fett, keine Doppelpunkt-Zeile uebersehen.
This commit is contained in:
2026-07-31 13:43:54 +00:00
parent d5b9733e78
commit 914ab2ddc8
3 changed files with 44 additions and 1 deletions
+17 -1
View File
@@ -754,7 +754,7 @@ async function openRecording(id) {
</div>`;
const mods = {
analyse: `
${r.summary ? `<p class="summary">${escapeHtml(r.summary)}</p>` : '<div class="muted">Noch keine Zusammenfassung.</div>'}
${r.summary ? `<p class="summary">${renderSummary(r.summary)}</p>` : '<div class="muted">Noch keine Zusammenfassung.</div>'}
${(r.participants && r.participants.length) ? `<h3>Teilnehmer</h3><ul class="parts">${r.participants.map(p => `<li>👤 ${escapeHtml(p)}</li>`).join('')}</ul>` : ''}
${markersHtml}`,
todos: (r.todos && r.todos.length) ? `<ul class="todos">${r.todos.map(t => `<li>${escapeHtml(t)}</li>`).join('')}</ul>` : '<div class="muted">Keine ToDos erkannt.</div>',
@@ -1143,6 +1143,22 @@ async function openRecording(id) {
function escapeHtml(s) { return (s || '').replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c])); }
// The section summary comes back as "Abschnittstitel:\nFließtext…" blocks (see
// llm.js analyzeLong). Flat, that reads as one wall of text, so the title lines
// get set in bold. Done at RENDER time on purpose, not stored in the database:
// it applies to every summary that already exists, without re-running a
// 20-minute analysis on 22 recordings.
function renderSummary(text) {
return escapeHtml(text || '').split('\n').map(line => {
const t = line.trim();
// A heading is a SHORT, STANDALONE line ending in a colon. Both conditions
// matter: prose like "…die Struktur der Projektberichterstattung: kurze
// Einordnung, aktueller Stand…" has the colon mid-line and keeps running,
// and excluding . ! ? stops a full sentence that happens to end in a colon.
return /^[^.!?]{3,90}:$/.test(t) ? `<strong class="sum-head">${t}</strong>` : line;
}).join('\n');
}
// ---- Konferenztisch-Ansicht ----
// Seat positions (person center, % of scene) per participant count, arranged
// around the rectangular table; each seat knows which bubble-arrow side points