mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-06-24 20:28:16 +02:00
fix voice memos and move metadata to properties
- Fix duplicate tab bug via onNoteCreatedRef (stale closure in recorder.onstop) - Fix transcription not showing: read file from disk and update editor directly - Move voice memo type/recorded/path from body to YAML frontmatter (Properties) - Update note creation agent to detect voice memos via frontmatter Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2992196bb3
commit
98dfd0f159
3 changed files with 62 additions and 26 deletions
|
|
@ -3263,9 +3263,38 @@ function App() {
|
||||||
return newSet
|
return newSet
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select the file to show it in the editor
|
// If tab already exists for this path (e.g. second call after transcription),
|
||||||
|
// force a content reload instead of creating a duplicate tab.
|
||||||
|
const existingTab = fileTabs.find(tab => tab.path === notePath)
|
||||||
|
if (existingTab) {
|
||||||
|
setActiveFileTabId(existingTab.id)
|
||||||
|
// Read fresh content from disk and update the editor
|
||||||
|
try {
|
||||||
|
const result = await window.ipc.invoke('workspace:readFile', { path: notePath, encoding: 'utf8' })
|
||||||
|
const { raw: fm, body } = splitFrontmatter(result.data)
|
||||||
|
frontmatterByPathRef.current.set(notePath, fm)
|
||||||
|
setFileContent(body)
|
||||||
|
setEditorContent(body)
|
||||||
|
editorContentRef.current = body
|
||||||
|
editorPathRef.current = notePath
|
||||||
|
initialContentRef.current = body
|
||||||
|
initialContentByPathRef.current.set(notePath, body)
|
||||||
|
setEditorContentByPath(prev => ({ ...prev, [notePath]: body }))
|
||||||
|
editorContentByPathRef.current.set(notePath, body)
|
||||||
|
// Bump editor session to force TipTap to pick up the new content
|
||||||
|
setEditorSessionByTabId(prev => ({
|
||||||
|
...prev,
|
||||||
|
[existingTab.id]: (prev[existingTab.id] ?? 0) + 1,
|
||||||
|
}))
|
||||||
|
} catch {
|
||||||
|
// File read failed — ignore
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// First call — open the file in a tab
|
||||||
navigateToFile(notePath)
|
navigateToFile(notePath)
|
||||||
}, [loadDirectory, navigateToFile])
|
}, [loadDirectory, navigateToFile, fileTabs])
|
||||||
|
|
||||||
const ensureWikiFile = useCallback(async (wikiPath: string) => {
|
const ensureWikiFile = useCallback(async (wikiPath: string) => {
|
||||||
const resolvedPath = toKnowledgePath(wikiPath)
|
const resolvedPath = toKnowledgePath(wikiPath)
|
||||||
|
|
|
||||||
|
|
@ -608,6 +608,9 @@ function VoiceNoteButton({ onNoteCreated }: { onNoteCreated?: (path: string) =>
|
||||||
const notePathRef = React.useRef<string | null>(null)
|
const notePathRef = React.useRef<string | null>(null)
|
||||||
const timestampRef = React.useRef<string | null>(null)
|
const timestampRef = React.useRef<string | null>(null)
|
||||||
const relativePathRef = React.useRef<string | null>(null)
|
const relativePathRef = React.useRef<string | null>(null)
|
||||||
|
// Keep a ref to always call the latest onNoteCreated (avoids stale closure in recorder.onstop)
|
||||||
|
const onNoteCreatedRef = React.useRef(onNoteCreated)
|
||||||
|
React.useEffect(() => { onNoteCreatedRef.current = onNoteCreated }, [onNoteCreated])
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
window.ipc.invoke('workspace:readFile', {
|
window.ipc.invoke('workspace:readFile', {
|
||||||
|
|
@ -642,11 +645,12 @@ function VoiceNoteButton({ onNoteCreated }: { onNoteCreated?: (path: string) =>
|
||||||
recursive: true,
|
recursive: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const initialContent = `# Voice Memo
|
const initialContent = `---
|
||||||
|
type: voice memo
|
||||||
**Type:** voice memo
|
recorded: "${now.toISOString()}"
|
||||||
**Recorded:** ${now.toLocaleString()}
|
path: ${relativePath}
|
||||||
**Path:** ${relativePath}
|
---
|
||||||
|
# Voice Memo
|
||||||
|
|
||||||
## Transcript
|
## Transcript
|
||||||
|
|
||||||
|
|
@ -659,7 +663,7 @@ function VoiceNoteButton({ onNoteCreated }: { onNoteCreated?: (path: string) =>
|
||||||
})
|
})
|
||||||
|
|
||||||
// Select the note so the user can see it
|
// Select the note so the user can see it
|
||||||
onNoteCreated?.(notePath)
|
onNoteCreatedRef.current?.(notePath)
|
||||||
|
|
||||||
// Start actual recording
|
// Start actual recording
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
|
|
@ -707,11 +711,12 @@ function VoiceNoteButton({ onNoteCreated }: { onNoteCreated?: (path: string) =>
|
||||||
const currentNotePath = notePathRef.current
|
const currentNotePath = notePathRef.current
|
||||||
const currentRelativePath = relativePathRef.current
|
const currentRelativePath = relativePathRef.current
|
||||||
if (currentNotePath && currentRelativePath) {
|
if (currentNotePath && currentRelativePath) {
|
||||||
const transcribingContent = `# Voice Memo
|
const transcribingContent = `---
|
||||||
|
type: voice memo
|
||||||
**Type:** voice memo
|
recorded: "${new Date().toISOString()}"
|
||||||
**Recorded:** ${new Date().toLocaleString()}
|
path: ${currentRelativePath}
|
||||||
**Path:** ${currentRelativePath}
|
---
|
||||||
|
# Voice Memo
|
||||||
|
|
||||||
## Transcript
|
## Transcript
|
||||||
|
|
||||||
|
|
@ -728,21 +733,23 @@ function VoiceNoteButton({ onNoteCreated }: { onNoteCreated?: (path: string) =>
|
||||||
const transcript = await transcribeWithDeepgram(blob)
|
const transcript = await transcribeWithDeepgram(blob)
|
||||||
if (currentNotePath && currentRelativePath) {
|
if (currentNotePath && currentRelativePath) {
|
||||||
const finalContent = transcript
|
const finalContent = transcript
|
||||||
? `# Voice Memo
|
? `---
|
||||||
|
type: voice memo
|
||||||
**Type:** voice memo
|
recorded: "${new Date().toISOString()}"
|
||||||
**Recorded:** ${new Date().toLocaleString()}
|
path: ${currentRelativePath}
|
||||||
**Path:** ${currentRelativePath}
|
---
|
||||||
|
# Voice Memo
|
||||||
|
|
||||||
## Transcript
|
## Transcript
|
||||||
|
|
||||||
${transcript}
|
${transcript}
|
||||||
`
|
`
|
||||||
: `# Voice Memo
|
: `---
|
||||||
|
type: voice memo
|
||||||
**Type:** voice memo
|
recorded: "${new Date().toISOString()}"
|
||||||
**Recorded:** ${new Date().toLocaleString()}
|
path: ${currentRelativePath}
|
||||||
**Path:** ${currentRelativePath}
|
---
|
||||||
|
# Voice Memo
|
||||||
|
|
||||||
## Transcript
|
## Transcript
|
||||||
|
|
||||||
|
|
@ -755,7 +762,7 @@ ${transcript}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Re-select to trigger refresh
|
// Re-select to trigger refresh
|
||||||
onNoteCreated?.(currentNotePath)
|
onNoteCreatedRef.current?.(currentNotePath)
|
||||||
|
|
||||||
if (transcript) {
|
if (transcript) {
|
||||||
toast('Voice note transcribed', 'success')
|
toast('Voice note transcribed', 'success')
|
||||||
|
|
|
||||||
|
|
@ -165,8 +165,8 @@ workspace-readFile({ path: "{source_file}" })
|
||||||
- Email signature
|
- Email signature
|
||||||
|
|
||||||
**Voice memo indicators:**
|
**Voice memo indicators:**
|
||||||
- Has \`**Type:** voice memo\` field
|
- Has YAML frontmatter with \`type: voice memo\`
|
||||||
- Has \`**Path:**\` field with path like \`Voice Memos/YYYY-MM-DD/...\`
|
- Has frontmatter \`path:\` field like \`Voice Memos/YYYY-MM-DD/...\`
|
||||||
- Has \`## Transcript\` section
|
- Has \`## Transcript\` section
|
||||||
|
|
||||||
**Set processing mode:**
|
**Set processing mode:**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue