mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-25 23:47:20 +00:00
fix(gateway): show /tts audio in Control UI webchat (#61598) (thanks @neeravmakwana)
This commit is contained in:
@@ -305,6 +305,20 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Embedded audio (e.g. gateway-injected TTS from slash commands) */
|
||||
.chat-message-audio {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
max-width: min(420px, 100%);
|
||||
}
|
||||
|
||||
.chat-message-audio-el {
|
||||
width: 100%;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
/* Compose input row - horizontal layout */
|
||||
.chat-compose__row {
|
||||
display: flex;
|
||||
|
||||
@@ -23,6 +23,10 @@ type ImageBlock = {
|
||||
alt?: string;
|
||||
};
|
||||
|
||||
type AudioClip = {
|
||||
url: string;
|
||||
};
|
||||
|
||||
function extractImages(message: unknown): ImageBlock[] {
|
||||
const m = message as Record<string, unknown>;
|
||||
const content = m.content;
|
||||
@@ -60,6 +64,32 @@ function extractImages(message: unknown): ImageBlock[] {
|
||||
return images;
|
||||
}
|
||||
|
||||
function extractAudioClips(message: unknown): AudioClip[] {
|
||||
const m = message as Record<string, unknown>;
|
||||
const content = m.content;
|
||||
const clips: AudioClip[] = [];
|
||||
if (!Array.isArray(content)) {
|
||||
return clips;
|
||||
}
|
||||
for (const block of content) {
|
||||
if (typeof block !== "object" || block === null) {
|
||||
continue;
|
||||
}
|
||||
const b = block as Record<string, unknown>;
|
||||
if (b.type !== "audio") {
|
||||
continue;
|
||||
}
|
||||
const source = b.source as Record<string, unknown> | undefined;
|
||||
if (source?.type === "base64" && typeof source.data === "string") {
|
||||
const data = source.data;
|
||||
const mediaType = (source.media_type as string) || "audio/mpeg";
|
||||
const url = data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
||||
clips.push({ url });
|
||||
}
|
||||
}
|
||||
return clips;
|
||||
}
|
||||
|
||||
export function renderReadingIndicatorGroup(assistant?: AssistantIdentity, basePath?: string) {
|
||||
return html`
|
||||
<div class="chat-group assistant">
|
||||
@@ -580,6 +610,25 @@ function renderMessageImages(images: ImageBlock[]) {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderMessageAudio(clips: AudioClip[]) {
|
||||
if (clips.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<div class="chat-message-audio">
|
||||
${clips.map(
|
||||
(clip) =>
|
||||
html`<audio
|
||||
class="chat-message-audio-el"
|
||||
controls
|
||||
preload="metadata"
|
||||
src=${clip.url}
|
||||
></audio>`,
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/** Render tool cards inside a collapsed `<details>` element. */
|
||||
function renderCollapsedToolCards(
|
||||
toolCards: ToolCard[],
|
||||
@@ -688,6 +737,8 @@ function renderGroupedMessage(
|
||||
const hasToolCards = toolCards.length > 0;
|
||||
const images = extractImages(message);
|
||||
const hasImages = images.length > 0;
|
||||
const audioClips = extractAudioClips(message);
|
||||
const hasAudio = audioClips.length > 0;
|
||||
|
||||
const extractedText = extractTextCached(message);
|
||||
const extractedThinking =
|
||||
@@ -711,7 +762,7 @@ function renderGroupedMessage(
|
||||
|
||||
// Suppress empty bubbles when tool cards are the only content and toggle is off
|
||||
const visibleToolCards = hasToolCards && (opts.showToolCalls ?? true);
|
||||
if (!markdown && !visibleToolCards && !hasImages) {
|
||||
if (!markdown && !visibleToolCards && !hasImages && !hasAudio) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
@@ -747,7 +798,7 @@ function renderGroupedMessage(
|
||||
: nothing}
|
||||
</summary>
|
||||
<div class="chat-tool-msg-body">
|
||||
${renderMessageImages(images)}
|
||||
${renderMessageImages(images)} ${renderMessageAudio(audioClips)}
|
||||
${reasoningMarkdown
|
||||
? html`<div class="chat-thinking">
|
||||
${unsafeHTML(toSanitizedMarkdownHtml(reasoningMarkdown))}
|
||||
@@ -771,7 +822,7 @@ function renderGroupedMessage(
|
||||
</details>
|
||||
`
|
||||
: html`
|
||||
${renderMessageImages(images)}
|
||||
${renderMessageImages(images)} ${renderMessageAudio(audioClips)}
|
||||
${reasoningMarkdown
|
||||
? html`<div class="chat-thinking">
|
||||
${unsafeHTML(toSanitizedMarkdownHtml(reasoningMarkdown))}
|
||||
|
||||
Reference in New Issue
Block a user