Skip to content

Commit 6ecc56c

Browse files
committed
fix: calculate bar dimensions to fill container, use ResizeObserver, smoother animation
- Use ResizeObserver for reliable container width measurement - Calculate barWidth and gap dynamically based on container width - Increase smoothingTimeConstant to 0.8 for less jittery animation - Adjust decibel range for better sensitivity
1 parent 2a412b6 commit 6ecc56c

File tree

1 file changed

+35
-15
lines changed

1 file changed

+35
-15
lines changed

src/browser/components/ChatInput/RecordingOverlay.tsx

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ const MODE_COLORS = {
1717
exec: "hsl(268, 94%, 65%)", // Slightly lighter than --color-exec-mode for visibility
1818
} as const;
1919

20+
// FFT size determines number of frequency bins (fftSize / 2)
21+
// Higher = more bars but less responsive, lower = fewer bars but more responsive
22+
const FFT_SIZE = 128; // 64 bars
23+
const NUM_BARS = FFT_SIZE / 2;
24+
2025
interface RecordingOverlayProps {
2126
state: VoiceInputState;
2227
mode: UIMode;
@@ -28,22 +33,37 @@ export const RecordingOverlay: React.FC<RecordingOverlayProps> = (props) => {
2833
const isRecording = props.state === "recording";
2934
const isTranscribing = props.state === "transcribing";
3035
const containerRef = useRef<HTMLDivElement>(null);
31-
const [containerWidth, setContainerWidth] = useState(400);
36+
const [containerWidth, setContainerWidth] = useState(600);
3237

33-
// Measure container width for the canvas
38+
// Measure container width for the canvas using ResizeObserver
3439
useLayoutEffect(() => {
35-
const measure = () => {
36-
if (containerRef.current) {
37-
setContainerWidth(containerRef.current.offsetWidth);
40+
const container = containerRef.current;
41+
if (!container) return;
42+
43+
const observer = new ResizeObserver((entries) => {
44+
for (const entry of entries) {
45+
setContainerWidth(entry.contentRect.width);
3846
}
39-
};
40-
measure();
41-
window.addEventListener("resize", measure);
42-
return () => window.removeEventListener("resize", measure);
47+
});
48+
49+
observer.observe(container);
50+
// Initial measurement
51+
setContainerWidth(container.offsetWidth);
52+
53+
return () => observer.disconnect();
4354
}, []);
4455

4556
const modeColor = MODE_COLORS[props.mode];
4657

58+
// Calculate bar dimensions to fill the container width
59+
// Total width = numBars * barWidth + (numBars - 1) * gap
60+
// We want gap = barWidth / 2 for nice spacing
61+
// So: width = numBars * barWidth + (numBars - 1) * barWidth/2
62+
// = barWidth * (numBars + (numBars - 1) / 2)
63+
// = barWidth * (1.5 * numBars - 0.5)
64+
const barWidth = Math.max(2, Math.floor(containerWidth / (1.5 * NUM_BARS - 0.5)));
65+
const gap = Math.max(1, Math.floor(barWidth / 2));
66+
4767
// Border and background classes based on state
4868
const containerClasses = cn(
4969
"mb-1 flex w-full flex-col items-center justify-center gap-1 rounded-md border px-3 py-2 transition-all focus:outline-none",
@@ -69,13 +89,13 @@ export const RecordingOverlay: React.FC<RecordingOverlayProps> = (props) => {
6989
mediaRecorder={props.mediaRecorder}
7090
width={containerWidth}
7191
height={32}
72-
barWidth={2}
73-
gap={1}
92+
barWidth={barWidth}
93+
gap={gap}
7494
barColor={modeColor}
75-
smoothingTimeConstant={0.5}
76-
fftSize={256}
77-
minDecibels={-80}
78-
maxDecibels={-20}
95+
smoothingTimeConstant={0.8}
96+
fftSize={FFT_SIZE}
97+
minDecibels={-70}
98+
maxDecibels={-30}
7999
/>
80100
) : (
81101
<TranscribingAnimation />

0 commit comments

Comments
 (0)