// src/renderer/components/meeting/EditableSegment.tsx // Phase 14.5: 전사 세그먼트 인라인 편집 컴포넌트 import { useState, useRef, useCallback } from 'react' import { Box, TextField, Tooltip, Chip } from '@mui/material' import { d3roPalette, d3roFontMono } from '../../theme' import { useI18n } from '../../i18n' // Phase 15.5: 화자별 색상 매핑 (d3roPalette SSOT) const SPEAKER_COLORS = [ d3roPalette.tag.purple, d3roPalette.tag.green, d3roPalette.tag.orange, d3roPalette.tag.red, '#3b82f6', ] function getSpeakerColor(speaker: string): string { // "화자 1" → 0, "화자 2" → 1, ... const match = /(\d+)$/.exec(speaker) const idx = match ? (parseInt(match[1], 10) - 1) : 0 return SPEAKER_COLORS[idx % SPEAKER_COLORS.length] } interface EditableSegmentProps { segmentId: string timestamp: number // ms, 상대 시간 text: string edited: boolean // 수정 여부 onEdit: (segmentId: string, newText: string) => void readOnly?: boolean speaker?: string } function formatTimestamp(ms: number): string { const totalSec = Math.floor(ms / 1000) const min = Math.floor(totalSec / 60) const sec = totalSec % 60 return `[${String(min).padStart(2, '0')}:${String(sec).padStart(2, '0')}]` } export function EditableSegment({ segmentId, timestamp, text, edited, onEdit, readOnly = false, speaker, }: EditableSegmentProps): React.ReactElement { const { t } = useI18n() const [editing, setEditing] = useState(false) const [draft, setDraft] = useState(text) const inputRef = useRef(null) const handleClick = useCallback(() => { if (readOnly) return setDraft(text) setEditing(true) }, [readOnly, text]) const handleCommit = useCallback(() => { const trimmed = draft.trim() if (trimmed && trimmed !== text) { onEdit(segmentId, trimmed) } setEditing(false) }, [draft, text, segmentId, onEdit]) const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() handleCommit() } else if (e.key === 'Escape') { setEditing(false) } }, [handleCommit], ) const speakerColor = speaker ? getSpeakerColor(speaker) : undefined if (editing) { return ( {formatTimestamp(timestamp)} setDraft(e.target.value)} onKeyDown={handleKeyDown} onBlur={handleCommit} autoFocus size="small" fullWidth multiline sx={{ '& .MuiInputBase-root': { fontFamily: d3roFontMono, fontSize: 13, bgcolor: d3roPalette.bg.input, }, }} /> ) } const textNode = ( {text} ) return ( {formatTimestamp(timestamp)} {speaker && ( )} {edited ? ( {textNode} ) : ( textNode )} ) }