Add dictation and streamed read-aloud to the chat
- A mic in the composer records, transcribes with the bot's transcription model (api/admin/bots/transcribe) and writes the text into the input at the cursor to edit before sending. Escape cancels; recordings aren't stored. - Every bot message has a read-aloud button that plays GET api/admin/bots/speak-message, streamed from the provider as the audio is made, so it starts in a couple of seconds. One message plays at a time; only the user's own sessions can be read. - Both buttons stay visible but disabled while the bot has no model for them (bot_voice page prop). - Transcription and speech no longer pick free models automatically: they're not available until a model is picked, and Settings says so. Transcription lists speech-to-text models only, so the chat-model transcript path and its missing-audio check are gone. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -1,11 +1,14 @@
|
||||
import Markdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import Row from "@moduletrace/twui/layout/Row";
|
||||
import Stack from "@moduletrace/twui/layout/Stack";
|
||||
import linkComputerRefs from "@/src/utils/link-computer-refs";
|
||||
import { MessageMarkdownComponents } from "../(data)/message-markdown-components";
|
||||
import NeedsYouChip from "./needs-you-chip";
|
||||
import SpeakMessageButton from "./speak-message-button";
|
||||
|
||||
type Props = {
|
||||
message_id?: number | null;
|
||||
text: string;
|
||||
needs_you?: boolean;
|
||||
};
|
||||
@@ -13,17 +16,30 @@ type Props = {
|
||||
/**
|
||||
* One bot message, rendered from markdown (GFM; raw HTML is never
|
||||
* rendered). Computers it names (`computerRef`) show as buttons that
|
||||
* switch the bot to them.
|
||||
* switch the bot to them. A play button under it reads it aloud,
|
||||
* disabled while the bot has no speech model.
|
||||
*/
|
||||
export default function BotMessageLine({ text, needs_you }: Props) {
|
||||
export default function BotMessageLine({ message_id, text, needs_you }: Props) {
|
||||
const can_speak = Boolean(message_id && text.trim());
|
||||
|
||||
return (
|
||||
<Stack className="gap-1.5! items-start max-w-full">
|
||||
<Stack className="w-full gap-2.5! items-start break-words">
|
||||
<Markdown remarkPlugins={[remarkGfm]} components={MessageMarkdownComponents}>
|
||||
<Markdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={MessageMarkdownComponents}
|
||||
>
|
||||
{linkComputerRefs({ text })}
|
||||
</Markdown>
|
||||
</Stack>
|
||||
{needs_you ? <NeedsYouChip /> : null}
|
||||
{needs_you || can_speak ? (
|
||||
<Row className="gap-2! items-center">
|
||||
{needs_you ? <NeedsYouChip /> : null}
|
||||
{can_speak ? (
|
||||
<SpeakMessageButton message_id={message_id!} />
|
||||
) : null}
|
||||
</Row>
|
||||
) : null}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user