Skip to content

Commit 8748119

Browse files
committed
fix: fix some bugs
1 parent 762ede3 commit 8748119

6 files changed

Lines changed: 217 additions & 305 deletions

File tree

examples/web_ui/frontend/src/components/chat/MessageBubble.tsx

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import type {
88
import {
99
ArrowDown,
1010
ArrowUp,
11-
AudioLines,
1211
Bot,
1312
CalendarClock,
1413
CheckCircle,
1514
ChevronDownIcon,
16-
CirclePause,
1715
CirclePlay,
1816
Copy,
1917
Loader2,
@@ -148,19 +146,62 @@ function groupToolCalls(content: ContentBlock[]): ExtendedContentBlock[] {
148146
return result;
149147
}
150148

149+
const AUDIO_WAVE_LINES: Array<{ x: number; y1: number; y2: number }> = [
150+
{ x: 2, y1: 10, y2: 13 },
151+
{ x: 6, y1: 6, y2: 17 },
152+
{ x: 10, y1: 3, y2: 21 },
153+
{ x: 14, y1: 8, y2: 15 },
154+
{ x: 18, y1: 5, y2: 18 },
155+
{ x: 22, y1: 10, y2: 13 },
156+
];
157+
158+
function AudioWave({ isPlaying = true, className }: { isPlaying?: boolean; className?: string }) {
159+
return (
160+
<>
161+
{isPlaying && (
162+
<style>{`
163+
@keyframes audioWave {
164+
0%, 100% { transform: scaleY(1); }
165+
50% { transform: scaleY(0.3); }
166+
}
167+
`}</style>
168+
)}
169+
<svg
170+
xmlns="http://www.w3.org/2000/svg"
171+
width="24"
172+
height="24"
173+
viewBox="0 0 24 24"
174+
fill="none"
175+
stroke="currentColor"
176+
strokeWidth={2}
177+
strokeLinecap="round"
178+
strokeLinejoin="round"
179+
className={className}
180+
>
181+
{AUDIO_WAVE_LINES.map(({ x, y1, y2 }, i) => (
182+
<line
183+
key={x}
184+
x1={x}
185+
x2={x}
186+
y1={y1}
187+
y2={y2}
188+
style={{
189+
transformOrigin: `${x}px 12px`,
190+
animation: isPlaying
191+
? `audioWave 0.8s ease-in-out ${i * 0.12}s infinite`
192+
: 'none',
193+
}}
194+
/>
195+
))}
196+
</svg>
197+
</>
198+
);
199+
}
200+
151201
/**
152202
* Inline audio control rendered *inside* the time/usage Badge so the play
153203
* icon visually merges into the same chip rather than floating as its own
154204
* pill.
155-
*
156-
* While the block is still being streamed by the assistant we show a pulsing
157-
* {@link AudioLines} icon — actual playback during that window is driven by
158-
* {@link useAudioBlock}'s live player. Once the stream finishes the manager
159-
* publishes an Object URL and we render a clickable Circle Play/Pause icon
160-
* for replay.
161-
*
162-
* For historical messages loaded from the server the block isn't tracked by
163-
* the manager — we fall back to a data URL built from the accumulated base64.
164205
*/
165206
function AudioInlineControl({ block }: { block: DataBlock }) {
166207
const { t } = useTranslation();
@@ -207,13 +248,7 @@ function AudioInlineControl({ block }: { block: DataBlock }) {
207248
}, [interruptCount]);
208249

209250
if (isStreaming) {
210-
return (
211-
<AudioLines
212-
data-icon="inline-start"
213-
className="ml-1 animate-pulse"
214-
aria-label={t('messageBubble.audioGenerating')}
215-
/>
216-
);
251+
return <AudioWave isPlaying className="ml-1" />;
217252
}
218253

219254
if (!src) return null;
@@ -234,7 +269,6 @@ function AudioInlineControl({ block }: { block: DataBlock }) {
234269
}
235270
};
236271

237-
const Icon = isPlaying ? CirclePause : CirclePlay;
238272
return (
239273
<>
240274
<button
@@ -245,7 +279,11 @@ function AudioInlineControl({ block }: { block: DataBlock }) {
245279
}
246280
className="ml-1 inline-flex cursor-pointer items-center transition-opacity hover:opacity-70"
247281
>
248-
<Icon className="size-3" />
282+
{isPlaying ? (
283+
<AudioWave isPlaying className="size-3" />
284+
) : (
285+
<CirclePlay className="size-3" />
286+
)}
249287
</button>
250288
<audio
251289
ref={audioRef}

0 commit comments

Comments
 (0)