feat: Adjust VAD sensitivity, enable global auto-stop, update docs
This commit is contained in:
@@ -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>
|
||||
) : (
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user