refactor(desktop): session-scoped voice runtime, STT/LLM ports, caption ownership and meeting export fixes
This commit is contained in:
parent
0adedf5e7b
commit
83cf9133ac
39 changed files with 4787 additions and 1061 deletions
|
|
@ -27,6 +27,17 @@ function stripThinkTags(text: string): string {
|
|||
return result.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* main 이 실어 보낸 sessionId 가 이 패널의 회의인지 확인한다.
|
||||
* sessionId 가 없는(구버전) 이벤트는 그대로 받아들인다.
|
||||
*/
|
||||
function isForSession(payload: object, sessionId: string): boolean {
|
||||
if ('sessionId' in payload && typeof payload.sessionId === 'string') {
|
||||
return payload.sessionId === sessionId
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 타이핑 인디케이터 점 3개 애니메이션
|
||||
function TypingIndicator(): React.ReactElement {
|
||||
return (
|
||||
|
|
@ -74,21 +85,38 @@ export function MeetingChatPanel({ sessionId }: MeetingChatPanelProps): React.Re
|
|||
scrollToBottom()
|
||||
}, [messages, streamingContent, scrollToBottom])
|
||||
|
||||
// 이벤트 구독
|
||||
// 회의가 바뀌면(같은 패널 인스턴스 재사용 포함) 이전 회의의 대화 표시와 진행 중 답변을 버린다.
|
||||
// main 은 회의마다 히스토리를 따로 보관하므로 여기서 지우지 않는다.
|
||||
useEffect(() => {
|
||||
setMessages([])
|
||||
setStreaming(false)
|
||||
setStreamingContent('')
|
||||
setError(null)
|
||||
isSendingChatRef.current = false
|
||||
return () => {
|
||||
// 떠나는 회의의 답변 스트림을 끊는다 — 다음 회의 패널에 섞이지 않게
|
||||
void window.electronAPI.meetingChat.cancel()
|
||||
}
|
||||
}, [sessionId])
|
||||
|
||||
// 이벤트 구독 — 다른 회의로 향한 이벤트는 무시한다
|
||||
useEffect(() => {
|
||||
const unsubDelta = window.electronAPI.meetingChat.onDelta((data) => {
|
||||
if (!isForSession(data, sessionId)) return
|
||||
setStreaming(true)
|
||||
setStreamingContent((prev) => prev + data.token)
|
||||
})
|
||||
|
||||
const unsubMessage = window.electronAPI.meetingChat.onMessage((msg) => {
|
||||
setMessages((prev) => [...prev, msg])
|
||||
if (!isForSession(msg, sessionId)) return
|
||||
setMessages((prev) => [...prev, { role: msg.role, content: msg.content, timestamp: msg.timestamp }])
|
||||
setStreaming(false)
|
||||
setStreamingContent('')
|
||||
isSendingChatRef.current = false
|
||||
})
|
||||
|
||||
const unsubError = window.electronAPI.meetingChat.onError((err) => {
|
||||
if (!isForSession(err, sessionId)) return
|
||||
setStreaming(false)
|
||||
setStreamingContent('')
|
||||
setError(err.message)
|
||||
|
|
@ -139,7 +167,7 @@ export function MeetingChatPanel({ sessionId }: MeetingChatPanelProps): React.Re
|
|||
setMessages([])
|
||||
setStreamingContent('')
|
||||
setStreaming(false)
|
||||
window.electronAPI.meetingChat.clear({ sessionId })
|
||||
window.electronAPI.meetingChat.clear()
|
||||
}, [sessionId])
|
||||
|
||||
const handleToggleCollapse = useCallback(() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue