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

@ -2,6 +2,7 @@
// Modern AI Meeting Studio: Dual-Canvas Split View (Transcript + AI Minutes & Granola Notepad)
import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'
import { parseTranscriptLines } from '@d3ro/core/meeting-transcript'
import {
Box,
Tabs,
@ -54,33 +55,14 @@ function parseSegments(
rawTranscript: string | null,
editedTranscript: string | null,
): Array<{ id: string; timestamp: number; text: string; edited: boolean; speaker?: string }> {
const source = editedTranscript || rawTranscript || ''
const regex = /^\[(\d{2}):(\d{2})\]\s*(?:\[([^\]]+)\]\s*)?(.+)$/
return source
.split('\n')
.filter((line) => line.trim())
.map((line, idx) => {
const m = regex.exec(line.trim())
if (m) {
const min = parseInt(m[1], 10)
const sec = parseInt(m[2], 10)
const speaker = m[3] || undefined
const text = m[4]
return {
id: `seg-${idx}`,
timestamp: (min * 60 + sec) * 1000,
text,
edited: false,
speaker,
}
}
return {
id: `seg-${idx}`,
timestamp: idx * 4000,
text: line.trim(),
edited: false,
}
})
// 줄 ↔ 구간 변환은 core 정본을 쓴다 — 서버 transcripts 구간도 같은 규칙으로 만들어진다.
return parseTranscriptLines(editedTranscript || rawTranscript).map((line, idx) => ({
id: `seg-${idx}`,
timestamp: line.timestampMs,
text: line.text,
edited: false,
speaker: line.speaker ?? undefined,
}))
}
// Extract Action items from documents