feat: Adjust VAD sensitivity, enable global auto-stop, update docs

This commit is contained in:
michael.borak
2026-01-21 11:09:54 +01:00
parent 79db6adf45
commit b848154942
5 changed files with 110 additions and 58 deletions

View File

@@ -64,9 +64,14 @@ const EmailPreviewModal: React.FC<EmailPreviewModalProps> = ({
const [activeTab, setActiveTab] = useState<'preview' | 'source'>('preview');
const generateHtmlBody = (content: string, title: string) => {
// Simple heuristic: if it looks like HTML, treat as HTML. Otherwise, markdown.
const isHtml = /^\s*<(!DOCTYPE|html|div|p|table)/i.test(content);
const formattedBody = isHtml ? content : formatMarkdownToHtml(content);
// Check if it's a full HTML document
if (/^\s*<!DOCTYPE html/i.test(content) || /^\s*<html/i.test(content)) {
return content;
}
// Simple heuristic: if it looks like HTML fragment (div, p, table), treat as HTML. Otherwise, markdown.
const isHtmlFragment = /^\s*<(div|p|table|section|header|footer)/i.test(content);
const formattedBody = isHtmlFragment ? content : formatMarkdownToHtml(content);
return `
<!DOCTYPE html>
@@ -111,14 +116,17 @@ const EmailPreviewModal: React.FC<EmailPreviewModalProps> = ({
// Replace placeholders
const dateStr = new Date().toLocaleDateString();
let newSub = tmpl.subject.replace(/{{date}}/g, dateStr).replace(/{{subject}}/g, "Meeting");
// Note: We don't have the original 'recordingSubject' here easily without more prop drilling,
// so we default to "Meeting" or user can edit.
// Actually, initialSubject usually contains "Meeting Summary", so we could parse it, but for now date/summary is most important.
// Clean up JSON if necessary (e.g. remove markdown code blocks ```json ... ```)
let cleanSummary = initialBody;
if (initialBody.trim().startsWith('```')) {
cleanSummary = initialBody.replace(/^```(json)?/i, '').replace(/```$/, '').trim();
}
let newBody = tmpl.body
.replace(/{{date}}/g, dateStr)
.replace(/{{subject}}/g, "the meeting")
.replace(/{{summary}}/g, initialBody);
.replace(/{{summary}}/g, cleanSummary);
setSubject(newSub);
setBody(generateHtmlBody(newBody, newSub));
@@ -242,7 +250,7 @@ const EmailPreviewModal: React.FC<EmailPreviewModalProps> = ({
srcDoc={body}
className="w-full h-full border-none"
title="Email Preview"
sandbox="allow-same-origin"
sandbox="allow-same-origin allow-scripts"
/>
</div>
) : (

View File

@@ -252,17 +252,14 @@ const Recorder: React.FC<RecorderProps> = ({
const interval = setInterval(() => {
const now = Date.now();
const diff = (now - lastSpeechTimeRef.current) / 1000;
setSilenceDuration(diff);
const timeSinceSpeech = (now - lastSpeechTimeRef.current) / 1000;
setSilenceDuration(timeSinceSpeech);
// Different timeouts based on mode:
// Voice Memo: 20 seconds of silence
// Meeting: Disabled (no auto-stop to avoid cutting off long meetings)
const timeoutSeconds = recordingMode === 'voice' ? 20 : 9999; // 9999 = effectively disabled
if (diff > timeoutSeconds && !isStoppingRef.current) {
console.log(`Auto-stopping (${recordingMode} mode) due to ${timeoutSeconds}s silence`);
addToast(`Auto-stopping (${Math.floor(diff)}s silence detected)`, "info", 3000);
// AUTO STOP after 20 seconds of silence (ALL MODES)
if (timeSinceSpeech > 20 && !isStoppingRef.current) {
console.log("Auto-stopping due to silence...");
isStoppingRef.current = true;
addToast('Auto-stopped due to silence', 'info');
stopRecording();
}
}, 1000);