fix(templates): route dictation results into active template sessions
This commit is contained in:
parent
11bf995409
commit
115e8488ac
7 changed files with 386 additions and 15 deletions
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue