meeting summary notes

This commit is contained in:
Arjun 2026-03-17 17:10:16 +05:30
parent 537ca08fe5
commit 49c004b53e
5 changed files with 80 additions and 2 deletions

View file

@ -41,6 +41,7 @@ import { search } from '@x/core/dist/search/search.js';
import { versionHistory, voice } from '@x/core';
import { classifySchedule } from '@x/core/dist/knowledge/inline_tasks.js';
import { getBillingInfo } from '@x/core/dist/billing/billing.js';
import { summarizeMeeting } from '@x/core/dist/knowledge/summarize_meeting.js';
/**
* Convert markdown to a styled HTML document for PDF/DOCX export.
@ -701,6 +702,10 @@ export function setupIpcHandlers() {
return { success: false, error: 'Unknown format' };
},
'meeting:summarize': async (_event, args) => {
const notes = await summarizeMeeting(args.transcript);
return { notes };
},
'inline-task:classifySchedule': async (_event, args) => {
const schedule = await classifySchedule(args.instruction);
return { schedule };

View file

@ -3343,12 +3343,46 @@ function App() {
navigateToFile(notePath)
}, [loadDirectory, navigateToFile, fileTabs])
const meetingNotePathRef = useRef<string | null>(null)
const handleToggleMeeting = useCallback(async () => {
if (meetingTranscription.state === 'recording') {
await meetingTranscription.stop()
// Read the final transcript and generate meeting notes via LLM
const notePath = meetingNotePathRef.current
if (notePath) {
try {
const result = await window.ipc.invoke('workspace:readFile', { path: notePath, encoding: 'utf8' })
const fileContent = result.data
if (fileContent && fileContent.trim()) {
// Call LLM to summarize the transcript
const { notes } = await window.ipc.invoke('meeting:summarize', { transcript: fileContent })
if (notes) {
// Prepend meeting notes below the title but above the transcript
const { raw: fm, body: transcriptBody } = splitFrontmatter(fileContent)
// Strip the "# Meeting note" title from transcript body — we'll put it first
const bodyWithoutTitle = transcriptBody.replace(/^#\s+Meeting note\s*\n*/, '')
const newBody = '# Meeting note\n\n' + notes + '\n\n---\n\n## Raw transcript\n\n' + bodyWithoutTitle
const newContent = fm ? `${fm}\n${newBody}` : newBody
await window.ipc.invoke('workspace:writeFile', {
path: notePath,
data: newContent,
opts: { encoding: 'utf8' },
})
// Refresh the file view
await handleVoiceNoteCreated(notePath)
}
}
} catch (err) {
console.error('[meeting] Failed to generate meeting notes:', err)
}
meetingNotePathRef.current = null
}
} else if (meetingTranscription.state === 'idle') {
const notePath = await meetingTranscription.start()
if (notePath) {
meetingNotePathRef.current = notePath
await handleVoiceNoteCreated(notePath)
}
}

View file

@ -48,11 +48,11 @@ function formatTranscript(entries: TranscriptEntry[], date: string): string {
'---',
'type: meeting',
'source: rowboat',
'title: Meeting Transcription',
'title: Meeting note',
`date: "${date}"`,
'---',
'',
'# Meeting Transcription',
'# Meeting note',
'',
];
for (let i = 0; i < entries.length; i++) {