feat(desktop): send meeting transcript segments and preset prompt edits to the phone

The phone draws a meeting from its transcript segments before the edited
transcript, so desktop edits, auto-polish and diarization never showed there.
Every desktop transcript change now rebuilds the meeting's segments from its
[MM:SS] [speaker] lines and trims the rest; the line parser moves to
@d3ro/core/meeting-transcript and the meeting view uses it too.

Prompt edits of the four desktop presets that exist on the phone update the
server preset row (a reset restores its default; {{targetLanguage}} is sent
as English, the only target on both sides), and edits made on another desktop
come back. The free-prompt preset has no phone counterpart and stays local.
This commit is contained in:
Yun Chan 2026-09-27 16:24:25 +09:00
parent 08c6504589
commit 2aac10fc5d
14 changed files with 552 additions and 28 deletions

View file

@ -0,0 +1,33 @@
import { describe, expect, it } from 'vitest'
import { formatTranscriptLines, parseTranscriptLines } from '../src/meeting-transcript'
describe('meeting transcript lines', () => {
it('parses timestamps and speakers written by the desktop recorder and diarization', () => {
const text = '[00:05] 안녕하세요\n\n[01:12] [화자 2] 다음 안건입니다\n[12:03] [Speaker 1] 끝'
expect(parseTranscriptLines(text)).toEqual([
{ timestampMs: 5_000, timed: true, speaker: null, text: '안녕하세요' },
{ timestampMs: 72_000, timed: true, speaker: '화자 2', text: '다음 안건입니다' },
{ timestampMs: 723_000, timed: true, speaker: 'Speaker 1', text: '끝' },
])
})
it('keeps untimed lines (phone transcripts, manual edits) and carries the last time forward', () => {
expect(parseTranscriptLines('first line\n[00:30] timed\nfollow-up')).toEqual([
{ timestampMs: 0, timed: false, speaker: null, text: 'first line' },
{ timestampMs: 30_000, timed: true, speaker: null, text: 'timed' },
{ timestampMs: 30_000, timed: false, speaker: null, text: 'follow-up' },
])
})
it('drops empty and timestamp-only lines and round-trips through the formatter', () => {
const text = '[00:01] [화자 1] a\n[00:02]\nb'
const lines = parseTranscriptLines(text)
expect(lines).toHaveLength(2)
expect(formatTranscriptLines(lines)).toBe('[00:01] [화자 1] a\nb')
})
it('returns nothing for empty input', () => {
expect(parseTranscriptLines(null)).toEqual([])
expect(parseTranscriptLines(' \n ')).toEqual([])
})
})