fix(harmonyos): make the conversation screen usable during a run - #2214
Open
wgqqqqq wants to merge 3 commits into
Open
fix(harmonyos): make the conversation screen usable during a run#2214wgqqqqq wants to merge 3 commits into
wgqqqqq wants to merge 3 commits into
Conversation
Six things the session screen got wrong on device, all reported together. They land in one commit because they overlap in the same few files. The timeline never followed streaming output. `scrollEdge(Edge.Bottom)` only existed inside the floating button's `onClick`, and the list was explicitly `stackFromEnd(false)`, so a bubble growing every 40ms grew below the viewport. Flipping `stackFromEnd` handles the growth case; a `timelineRevision` monitor covers whole-message inserts, and a `stickToBottom` flag reads `isAtEnd()` so scrolling back through history is not fought by the follow. The composer locked to Stop for the whole run. `isStopping` folded dictation and a running turn into one flag, but they sit on opposite sides of the draft: dictation outranks it because the draft is still being spoken, a running turn only outranks it where the draft has nowhere to go. Split into `isVoiceListening` and `isTurnRunning`; remote sessions can hand a message over mid-run, so a draft now wins there and the relay queues it. Stop did not stop. Between sending a message and the server's turn_id arriving, `remoteActiveTurnId()` sliced the local `active-pending-<id>` into `pending-<id>` and sent it as a turn_id. The server read that as `StaleRequestedTurn` and kept running. It now returns '' in that window, which the server resolves as CancelCurrent. The failure was also invisible — it went to `statusText`, which only renders while disconnected — so the stop path now reports through the existing toast channel. AskUserQuestion options were cut off. `OptionRow` was pinned to 36px with no `maxLines` on the label and no `layoutWeight` on the description, so a long option lost its tail with nothing to indicate it. The row now grows with its content and label/description stack instead of competing for one line. Picked images rendered outside the input. They now sit in a strip inside the composer card, left-aligned — `Scroll` centres content narrower than its viewport, which parked a lone thumbnail mid-composer. The plus button opened an attachment panel offering quick prompts and a single image entry. A message can only carry images (`RemoteCommand::SendMessage` has no file channel), so a menu there was a menu of one; plus now opens the album directly, matching local chat. The panel and its orphaned strings are gone. Image encoding also got a size budget, since the transport doubles what it is handed: base64 into the command JSON, then base64 again after AES-GCM.
`is_valid_encrypted_payload` caps the ciphertext at 48 MB, but the route inherited Axum's 2 MB `DefaultBodyLimit`, which rejects the body before the handler — including its auth check — ever runs. A phone attaching a photo got a bare 413 from a route that claimed to accept 24x that. The limit is derived from `MAX_ENCRYPTED_PAYLOAD_BYTES` so the two cannot drift apart again, with slack for the JSON envelope and the base64 nonce. Two tests pin both ends: a 3 MB body reaches the handler, and one past the route ceiling is still refused.
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.
Six things the HarmonyOS session screen got wrong on device, reported together after a round of real-device use, plus the relay-side cause of one of them.
What was broken
The timeline never followed streaming output.
scrollEdge(Edge.Bottom)only existed inside the floating button'sonClick, and the list was explicitlystackFromEnd(false)— so a bubble growing every 40ms grew below the viewport and the agent's reply was written where nobody could see it.The composer locked to Stop for the whole run.
isStoppingfolded dictation and a running turn into one flag, but they sit on opposite sides of the draft: dictation outranks it because the draft is still being spoken, a running turn only outranks it where the draft has nowhere to go. Remote sessions can hand a message over mid-run (the server already queues it —remote_server.rs), so keeping the button on Stop there trapped what was typed.Stop did not stop. Between sending a message and the server's
turn_idarriving,remoteActiveTurnId()sliced the localactive-pending-<id>intopending-<id>and sent that as aturn_id. The server read it asStaleRequestedTurnand kept running. The error was also invisible — it went tostatusText, which only renders while disconnected — so the user saw nothing happen, twice.AskUserQuestion options were cut off.
OptionRowwas pinned to 36px with nomaxLineson the label and nolayoutWeighton the description, so a long option lost its tail with nothing to indicate it had one.Picked images rendered outside the input box instead of inside the composer card.
The plus button opened an attachment panel offering quick prompts and a single image entry.
RemoteCommand::SendMessagecarries no file channel, so a menu there was a menu of one.Attaching a photo failed with a bare 413.
is_valid_encrypted_payloadcaps the ciphertext at 48 MB, but/api/devices/:id/rpcinherited Axum's 2 MBDefaultBodyLimit, which rejects the body before the handler — including its auth check — ever runs.What changed
ChatTimelineflips tostackFromEnd(true)for the growth case, adds atimelineRevisionmonitor for whole-message inserts, and gates both on astickToBottomflag driven byisAtEnd()so scrolling back through history is not fought by the follow.ChatComposerPolicy.primaryAction()splitsisStoppingintoisVoiceListeningandisTurnRunning, with a draft outranking a running turn on surfaces that support mid-run send. The policy itself is centralized here (first commit) so both the button glyph and its click branch read the same decision.remoteActiveTurnId()returns''during the pending window, which the server resolves asCancelCurrent; the stop path reports failures through the existing toast channel instead of the disconnected-only status bar.OptionRowgrows with its content; label and description stack rather than compete for one line.Scrollcentres content narrower than its viewport, which parked a lone thumbnail mid-composer). Plus opens the album directly, matching local chat; the attachment panel and its orphaned strings are removed. Image encoding gets a size budget, since the transport doubles what it is handed — base64 into the command JSON, then base64 again after AES-GCM.DefaultBodyLimitderived fromMAX_ENCRYPTED_PAYLOAD_BYTES, so the ceiling and the validator cannot drift apart again.Scope note: mid-run send is remote-only. Local general chat has no queue behind it and keeps its current behaviour.
Verification
assembleHap— BUILD SUCCESSFULLocalTest— 239 passed, 0 failedcargo test -p bitfun-relay-service --lib routes::devices— 6 passed, including two new cases pinning both ends of the body limit (a 3 MB body reaches the handler; one past the ceiling is still refused)The relay fix needs a deploy before phones stop seeing 413.