AI
useAudioRecorder
Hook MediaRecorder com level meter — apresentacional por padrão, com transcribe opcional.
useAudioRecorder cuida do ciclo de vida da MediaRecorder API: pede permissão, grava chunks, expõe level (0–1) para a barra de volume e devolve o Blob final. Não transcreve por padrão — você passa um transcribe(blob) => Promise<string> quando quiser texto direto, ou usa o blob como anexo (attachOnStop).
Este exemplo precisa de mais altura para rolar e interagir como em um app real. Abra em tela cheia.
'use client';
import { CodeBlock, Composer } from '@uranus-workspace/ai';
const code = `'use client';
import { Composer } from '@uranus-workspace/ai';
export function VoiceComposer() {
return (
<Composer.Root status="idle" onSubmit={() => {}}>
<Composer.Textarea />
<Composer.Toolbar>
<Composer.RecordButton
transcribe={async (blob) => {
const fd = new FormData();
fd.append('audio', blob);
const res = await fetch('/api/transcribe', { method: 'POST', body: fd });
const { text } = await res.json();
return text;
}}
/>
</Composer.Toolbar>
</Composer.Root>
);
}`;
export default function UseAudioRecorderDefault() {
return (
<div className="mx-auto flex w-full max-w-3xl flex-col gap-6">
<div className="rounded-lg border bg-background p-4">
<Composer.Root status="idle" onSubmit={() => {}}>
<Composer.Textarea placeholder="Grave pelo botão ou digite aqui…" />
<Composer.Toolbar>
<Composer.RecordButton
transcribe={async () =>
'(simulado) Texto que viria da sua API de transcrição — em produção envie o Blob ao servidor.'
}
/>
</Composer.Toolbar>
</Composer.Root>
</div>
<CodeBlock language="tsx" code={code} />
</div>
);
}
Uso direto
'use client';
import { useAudioRecorder } from '@uranus-workspace/ai';
const recorder = useAudioRecorder({
transcribe: async (blob) => {
const fd = new FormData();
fd.append('audio', blob);
const res = await fetch('/api/transcribe', { method: 'POST', body: fd });
const { text } = await res.json();
return text;
},
});
<button onClick={recorder.start}>Gravar</button>
<button onClick={recorder.stop}>Parar</button>
<span>{recorder.level.toFixed(2)}</span>Via Composer.RecordButton
Composer.RecordButton consome este hook internamente. Você só precisa decidir entre:
{/* Anexa o blob — você processa no submit */}
<Composer.RecordButton attachOnStop />
{/* Transcreve para o textarea */}
<Composer.RecordButton transcribe={transcribeAudio} />Server route (opcional)
export async function POST(req: Request) {
const form = await req.formData();
const audio = form.get('audio') as File;
const transcript = await fetch('https://api.openai.com/v1/audio/transcriptions', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
body: form,
}).then((r) => r.json());
return Response.json({ text: transcript.text });
}A11y
- O botão de gravação altera
aria-pressedenquanto está ativo. - Em
prefers-reduced-motion: reduce, o waveform pulsa de forma reduzida e a barra usa só fade.