From 667544ce8fdbb2dd6f6116e57c24d6c83a49614d Mon Sep 17 00:00:00 2001 From: ElwinLiu Date: Sat, 11 Apr 2026 18:23:37 -0400 Subject: [PATCH 1/2] fix(overlay): stabilize hold-to-record streaming layout --- src/overlay/RecordingOverlay.css | 9 +++++++++ src/overlay/RecordingOverlay.tsx | 14 +++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/overlay/RecordingOverlay.css b/src/overlay/RecordingOverlay.css index bb3b3225..36ea0149 100644 --- a/src/overlay/RecordingOverlay.css +++ b/src/overlay/RecordingOverlay.css @@ -79,6 +79,15 @@ clip-path 280ms cubic-bezier(0.16, 1, 0.3, 1); } +/* Inline streaming text needs its final width immediately, otherwise the + first scrollHeight measurement is taken while the pill is still narrow. */ +.recording-overlay.has-inline-text, +.recording-overlay.fade-in.has-inline-text { + transition: + opacity 180ms ease-out, + transform 280ms cubic-bezier(0.34, 1.56, 0.64, 1); +} + /* Fade-in any new content block (exclude absolutely-positioned buttons) */ .recording-overlay > *:not(.overlay-btn):not(.postprocess-dismiss) { animation: content-enter 200ms ease-out; diff --git a/src/overlay/RecordingOverlay.tsx b/src/overlay/RecordingOverlay.tsx index 97a68676..7f432cfd 100644 --- a/src/overlay/RecordingOverlay.tsx +++ b/src/overlay/RecordingOverlay.tsx @@ -130,18 +130,18 @@ const RecordingOverlay: React.FC = () => { const buttonsExtra = showButtons ? BUTTON_AREA_WIDTH * 2 : 0; // Toggle mode: text floats above waveform bars instead of replacing them const showTextAboveBars = hasStreamingText && activationMode === "toggle"; + const showInlineStreamingText = hasStreamingText && !showTextAboveBars; // Compute overlay dimensions — pill never changes shape in toggle mode const overlayWidth = (() => { if (!isVisible) return 33; - if (hasStreamingText && !showTextAboveBars) - return STREAMING_WIDTH + buttonsExtra; + if (showInlineStreamingText) return STREAMING_WIDTH + buttonsExtra; return 70 + buttonsExtra; })(); const overlayHeight = (() => { if (!isVisible) return 33; - if (hasStreamingText && !showTextAboveBars && contentHeight > 0) { + if (showInlineStreamingText && contentHeight > 0) { const maxTextHeight = STREAMING_LINE_HEIGHT * MAX_LINES; const clampedHeight = Math.min(contentHeight, maxTextHeight); return Math.max(33, clampedHeight + OVERLAY_PADDING); @@ -149,8 +149,8 @@ const RecordingOverlay: React.FC = () => { return 33; })(); - const overlayRadius = hasStreamingText && !showTextAboveBars ? 14 : 999; - const buttonRadius = hasStreamingText && !showTextAboveBars ? 8 : 13; + const overlayRadius = showInlineStreamingText ? 14 : 999; + const buttonRadius = showInlineStreamingText ? 8 : 13; // Track words (runs before paint to avoid flicker) useLayoutEffect(() => { @@ -487,7 +487,7 @@ const RecordingOverlay: React.FC = () => { borderRadius: overlayRadius, clipPath: `inset(0 round ${overlayRadius}px)`, }} - className={`recording-overlay ${isVisible ? "fade-in" : ""} ${showButtons ? "has-buttons" : ""}`} + className={`recording-overlay ${isVisible ? "fade-in" : ""} ${showButtons ? "has-buttons" : ""} ${showInlineStreamingText ? "has-inline-text" : ""}`} > {state === "recording" && ( <> @@ -499,7 +499,7 @@ const RecordingOverlay: React.FC = () => { >