feat: add editor multiline mode and many edge case fixes#476
Merged
Conversation
Prevents mobile voice recording glitch where holding the record button would select text from the 'following the conversation' row underneath.
When recording on mobile, if the pointerup listener was lost (e.g., dragging off the button), there was no way to stop recording. Now the stop button's onClick handler works on mobile when recording is active, providing a fallback.
…ecording Prevents layout shift by rendering the recorder in the editor's main content area instead of in the after buttons section. Uses replacementContent prop to swap the editor with the recorder while maintaining proper padding.
7078f73 to
f22a764
Compare
Contributor
functional testingfunctions well:
couldn't be tested rn:
|
dozro
approved these changes
Mar 22, 2026
Contributor
Deploying with
|
| Status | Preview URL | Commit | Alias | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! | https://pr-476-sable.raspy-dream-bb1d.workers.dev | e5d5230 | pr-476 |
Sun, 22 Mar 2026 18:21:53 GMT |
dozro
approved these changes
Mar 22, 2026
7w1
approved these changes
Mar 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #350
This adds a dynamic multiline layout to
CustomEditor. When the message input grows past a single line (word wrap, explicit newlines, or multiple blocks), the editor shifts from a single-row layout into a two-row grid, before/textarea on top, with responsive content spanning the full width below. It drops back to single-row once the text fits on one line again.The old
replacementContentprop (which swapped out the textarea on mobile during voice recording) is gone. It's replaced byresponsiveAfter, which renders inline next to the send button in single-line mode and moves down to the footer row in multiline mode. There's also aforceMultilineLayoutprop for when you want to push into multiline immediately, which is what we use when the audio recorder opens. This means the recorder now takes the same code path on desktop and mobile instead of having a separate mobile-only branch.A few other things got fixed along the way:
userSelect: none. (related to voice message recording not possible on phone bc ui design #350)onClickwas being skipped on mobile entirely, so ifpointerupgot lost mid-drag there was no way to stop recording. NowonClickworks as a fallback when recording is already active. (a form of recovery in case more situations like voice message recording not possible on phone bc ui design #350 happen again)ResizeObserverinstead of a hardcoded number.cleanupMediaRecorderwasn't being called in all the stop/delete/restart/error/unmount paths, which could leave the mic track open. That's fixed.audio/webmdoesn't include a seek index. Codec preference now putsaudio/ogg;codecs=opusfirst so Firefox records seekable blobs. Also added aseekTohelper that waits forloadedmetadataif the audio element isn't ready yet, andaudio.preload = 'auto'+audio.load()on mount so Firefox parses metadata right away.requestAnimationFrameand uses aResizeObserveron the scroll container to cut down on flicker and extra re-renders.Also sorry for the cursed line height measurer, there was legit no other way to detect it that didn't cause other issues.
Good amount of this was created with help of AI (like figuring out options on how to fix the edge cases), especially the edge case unit tests, but It had to be fixed by hand in many places and frankly I can't tell you exactly where was what. I've been working on this for couple days now.. (losing my mind over it)
Screenshots:
Recording.2026-03-22.182018.mp4
Recording.2026-03-22.182144.mp4
Type of change
Checklist:
AI disclosure: