fix(templates): route dictation results into active template sessions

This commit is contained in:
Yun Chan 2026-09-28 02:16:19 +09:00
parent 11bf995409
commit 115e8488ac
7 changed files with 386 additions and 15 deletions

View file

@ -21,6 +21,8 @@ export function TemplateSection(): React.ReactElement {
const [formOutput, setFormOutput] = useState('')
const [formFields, setFormFields] = useState<TemplateField[]>([])
const [session, setSession] = useState<TemplateSessionInfo | null>(null)
/** 마지막으로 완성된 템플릿 결과 — 활성 앱 삽입이 꺼져 있거나 실패해도 복사할 수 있게 보여 준다 */
const [lastOutput, setLastOutput] = useState<string | null>(null)
const [saving, setSaving] = useState(false)
const isSavingRef = useRef(false)
@ -50,8 +52,9 @@ export function TemplateSection(): React.ReactElement {
const unsub = window.electronAPI.dictationTemplate.onSessionStateChanged((data) => {
setSession(data)
})
const unsubComplete = window.electronAPI.dictationTemplate.onSessionCompleted(() => {
const unsubComplete = window.electronAPI.dictationTemplate.onSessionCompleted((data) => {
setSession(null)
setLastOutput(data.outputText.length > 0 ? data.outputText : null)
loadData()
})
return () => { unsub(); unsubComplete() }
@ -118,9 +121,14 @@ export function TemplateSection(): React.ReactElement {
}
const handleStartSession = async (templateId: string) => {
setLastOutput(null)
await window.electronAPI.dictationTemplate.startSession({ templateId })
}
const handleCopyOutput = () => {
if (lastOutput) void navigator.clipboard.writeText(lastOutput)
}
const handleCancelSession = async () => {
await window.electronAPI.dictationTemplate.cancelSession()
setSession(null)
@ -176,6 +184,29 @@ export function TemplateSection(): React.ReactElement {
{t('template.cancelSession')}
</PhysicalButton>
</Box>
{/* 현재 필드의 음성 프롬프트 — 받아쓰기 결과가 이 필드 값으로 들어간다 */}
{session.currentField?.promptText && (
<PhosphorText variant="dim" sx={{ mt: 1 }}>
{session.currentField.promptText}
</PhosphorText>
)}
</MetalCard>
)}
{/* 마지막 완성 결과 */}
{!session && lastOutput && (
<MetalCard sx={{ mb: 2 }}>
<PhosphorText variant="body" sx={{ whiteSpace: 'pre-wrap' }}>
{lastOutput}
</PhosphorText>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1, mt: 1 }}>
<PhysicalButton size="small" onClick={handleCopyOutput}>
{t('common.copy')}
</PhysicalButton>
<PhysicalButton size="small" onClick={() => setLastOutput(null)}>
{t('common.close')}
</PhysicalButton>
</Box>
</MetalCard>
)}
@ -311,7 +342,7 @@ export function TemplateSection(): React.ReactElement {
{t('common.cancel')}
</Button>
<Button onClick={handleSave} variant="contained" disabled={!formName.trim() || formFields.length === 0 || saving}>
{saving ? t('common.saving') || '저장 중...' : t('common.save')}
{saving ? t('common.saving') : t('common.save')}
</Button>
</DialogActions>
</Dialog>