🎤 fix: Keep Microphone Icon Visible On Initial Chat Render - #13788
Conversation
AudioRecorder returned null while the parent ChatForm's textAreaRef was still null on first paint, hiding the mic icon until an unrelated re-render. Render the button disabled instead so the icon is always present. Closes #13786
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb5787e7c2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| aria-label={localize('com_ui_use_micrphone')} | ||
| onClick={isListening === true ? handleStopRecording : handleStartRecording} | ||
| disabled={disabled} | ||
| disabled={disabled || !textAreaRef.current} |
There was a problem hiding this comment.
Stop deriving the disabled state from a ref
On an initial chat render where textAreaRef.current is still null, this renders the microphone button with disabled set, but the later ref assignment does not trigger a React re-render. That means users who try to click the now-visible mic immediately after loading a chat can still get a permanently disabled button until some unrelated state/prop change occurs, preserving the same first-render usability regression in a different form. Since the handlers do not depend on the textarea ref, the button should not be disabled based on this ref value.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — fixed in d39b6b9. You're right that the ref assignment never triggers a re-render, so deriving disabled from textAreaRef.current could leave the button stuck disabled. Since neither the STT hook nor the start/stop handlers read the ref, I removed the dependency entirely: disabled={disabled}, and dropped the now-unused textAreaRef prop from the component and its call site in ChatForm.
Per Codex review: deriving the button's disabled state from textAreaRef.current could leave the mic permanently disabled until an unrelated re-render, since assigning a ref does not trigger one. The handlers never read the ref, so remove the dependency entirely along with the now-unused prop.
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
GitNexus: ❌ deploy failedThe deploy failed — the previous index (if any) continues to be served. |
…-AI#13788) * 🎤 fix: Keep Microphone Icon Visible On Initial Chat Render AudioRecorder returned null while the parent ChatForm's textAreaRef was still null on first paint, hiding the mic icon until an unrelated re-render. Render the button disabled instead so the icon is always present. Closes LibreChat-AI#13786 * 🎤 refactor: Drop Unused textAreaRef Dependency From AudioRecorder Per Codex review: deriving the button's disabled state from textAreaRef.current could leave the mic permanently disabled until an unrelated re-render, since assigning a ref does not trigger one. The handlers never read the ref, so remove the dependency entirely along with the now-unused prop.
…-AI#13788) * 🎤 fix: Keep Microphone Icon Visible On Initial Chat Render AudioRecorder returned null while the parent ChatForm's textAreaRef was still null on first paint, hiding the mic icon until an unrelated re-render. Render the button disabled instead so the icon is always present. Closes LibreChat-AI#13786 * 🎤 refactor: Drop Unused textAreaRef Dependency From AudioRecorder Per Codex review: deriving the button's disabled state from textAreaRef.current could leave the mic permanently disabled until an unrelated re-render, since assigning a ref does not trigger one. The handlers never read the ref, so remove the dependency entirely along with the now-unused prop.
Summary
AudioRecorderreturnednullwhenever the parentChatForm'stextAreaRefwas still unset, which is always the case on the first paint of a new chat or a page refresh. Because populating a ref does not trigger a re-render andAudioRecorderismemo-wrapped, the microphone icon stayed missing until some unrelated prop change (disabled,isSubmitting,ask) forced a re-render.This removes the early-return guard and instead renders the button in a disabled state while the ref is unset, so the icon is always visible.
Changes
client/src/components/Chat/Input/AudioRecorder.tsxif (!textAreaRef.current) { return null; }guard.disabledprop todisabled={disabled || !textAreaRef.current}.Why this is safe
textAreaRefwas only ever read by that guard — the STT hook (useSpeechToText) and bothhandleStartRecording/handleStopRecordingnever touchtextAreaRef.current, so there is no functional dependency on the ref being populated.Closes #13786