Fix/2249 - #375
Merged
Merged
Conversation
Pre-existing formatting drift surfaced by the pre-commit hook, which runs prettier across the whole codebase. Isolated here so it does not pollute the functional commits that follow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the message keys used by the voice call modal's status caption and live transcript band: call status states, speaker labels, the empty state and the jump-to-latest affordance. Translated across en, es, fr and zh. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Subscribes to RoomEvent.TranscriptionReceived and accumulates an ordered transcript for the lifetime of a call. Unlike the screen-sharing relay, which holds a single utterance and clears it five seconds after it finalises, this keeps the full session so the user can read back what was said. - segment ids update their entry in place as speech grows; unknown ids append - isFinal is sticky, so a late partial cannot reopen a finalised line - timestamps are first-seen, so entries never reorder while updating - speaker attribution compares against the local participant identity rather than falling back to the mentor name first, which would label every remote speaker as the mentor - segment joining tolerates both cumulative and disjoint server behaviour - every event is logged so an empty transcript can be diagnosed as agent silence rather than a UI fault Not yet wired into the modal; the screen-sharing relay is left untouched and marked as a convergence opportunity. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nscript A single isMuted flag drove both setMicrophoneEnabled and the muted prop on RoomAudioRenderer, so muting your own microphone also silenced the mentor, with nothing in the UI to say so. Splits it into isMicMuted and isMentorAudioMuted with independent controls. RoomAudioRenderer's muted prop is used alone rather than also calling setVolume: it maps over subscribed remote tracks via useTracks, which is reactive, so late joiners are covered without a re-apply. The animated blob had the same conflation in visual form. It keyed off !isMicMuted, so muting yourself made a live call look dead, and it never reflected the mentor at all. It now animates whenever connected: the wave bars track the user's voice, a violet ring tracks the mentor's, and both can show at once. Adds the live transcript band beneath the blob, backed by useLiveKitTranscription. Newest line is prominent, older lines recede, in-progress lines carry a caret until they finalise. Auto-scroll pins to the latest line but yields when the user scrolls up to read back. The two stacked status rows from the first pass are gone; they duplicated both the blob and the button icons. One role=status caption replaces them, and the transcript is a polite role=log so new lines are announced without re-reading the history. Regression tests assert each control leaves the other untouched, and that the animation survives muting the microphone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The voice call close handler was identical to the screen sharing one except that screen sharing calls refetchChats() on teardown and voice call did not. Both modes pass the same session_id when minting call credentials — the same id the chat thread renders — so the backend has what it needs to append the conversation to that session. Screen sharing already refetches to pick it up. Unverified: we have not confirmed the backend persists voice sessions. This is symmetric with screen sharing and harmless if nothing new comes back, but it needs checking against a live call before we rely on it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replaces the violet and indigo accents on the mentor-speaking ring, the agent's transcript label, the in-progress caret and the live pill with blues. Speakers stay distinguishable by shade rather than hue: the mentor takes the deeper blue that matches its ring, the user the lighter blue of the blob. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The call interface had grown busy: the orb, a mentor ring, a status caption and an always-on scrolling transcript all competing in one dialog. ChatGPT Advanced Voice, Gemini Live and Google Meet converge on the same shape — one animated indicator carrying call state, and captions opt-in behind a CC button that is off by default. Gemini persists the choice across calls and shows three fixed lines rather than a scrollback, on the basis that the full record belongs to the transcript you read after hanging up. This adopts that shape: - adds a captions button as a fourth peer control, off by default, with the preference persisted in localStorage and guarded for SSR and for Safari private mode, where storage access throws - when on, renders only the last three entries, newest at the bottom, at a fixed height with no scrolling - removes the auto-scroll, the scroll-to-suspend behaviour, the jump-to-latest control and the live pill, along with the state, refs and translation keys that existed only to serve them - moves the status caption to sr-only, preserving its precedence and live region so assistive tech still hears what changed, while sighted users read state off the orb The transcription hook is unchanged and still accumulates the whole call; it is only the rendering that is bounded. Known gap: an absent ring now reads the same whether the mentor is silent or muted. The desaturated orb and the agent button's own state are the only remaining cues for sighted users. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Captions intermittently pushed the modal wider than its own max width. The cause was `truncate` on the older caption lines: it expands to white-space: nowrap, and a nowrap box's intrinsic minimum width is its entire unwrapped line. DialogContent is a CSS grid whose item had no min-w-0, and grid items default to min-width: auto, so that intrinsic width propagated up and sized the dialog past max-w-lg. That explains why it only sometimes happened: a short line stayed under the limit, a five-hundred-character agent turn did not. The band's own overflow-hidden clipped the text visually while doing nothing to the min-width chain, which is why it looked contained until it wasn't. Replaces truncate with line-clamp, which wraps rather than forcing one line, and adds min-w-0 to every box between the dialog's grid item and the caption lines, plus break-words so an unbroken URL cannot reproduce it by the other route. The band was also capped at max-w-xl, wider than the dialog's own max-w-lg and therefore unreachable; it is now max-w-md. Reshapes the captions at the same time. Real agent turns are paragraphs, not one-liners, so three lines competing for one box left the oldest as a truncated sliver. Captions now show the exchange: the newest line, plus the most recent line from the other speaker, selected by speaker rather than position so consecutive turns from one voice cannot hide the reply. Each gets its own slot — the answered turn clamped to two rows, the live line in its own viewport clipping against the bottom — so a long reply can no longer crowd out its own question. Shortens the captions tooltip to one word; the aria-label still describes the action. Adds collision padding to the control tooltips, though they portal to body and so resolve against the viewport rather than the dialog. Regression tests use a four-hundred-character multi-paragraph turn and an unbreakable URL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The live caption sat about fifty pixels below the line above it. The live window was flex-1, so it claimed all the band's leftover height and its own justify-end then dropped a short reply to the bottom of that claim. The dead air was invisible only when the agent's turn happened to be long enough to fill the box. Replaces the grow with a ceiling of 3.6875rem — the band's 6rem less the two-row worst case of the answered turn, so the live box can never starve the line above it. That height also holds exactly three text-sm rows, so the cap falls on a whole line rather than clipping a fourth in half. Both blocks now size to their content and stack flush at the bottom. Short replies sit directly beneath the question; long ones grow to the cap and clip inside their own box, which keeps the guarantee that a paragraph cannot push the exchange off the top. Drops the bottom margin from the newest line, which was floating the pair off the band's lower edge and, with it, pushing three live rows past the cap. The band stays 6rem. With growth gone the slack collects above the pair, where there is no background or border to reveal it, and a fixed height keeps the orb from shifting as captions change length. Tests pin the intent — no grow class, the cap present, margins on the right line — but jsdom has no layout engine, so the arithmetic above is the actual guarantee. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The call was a full-viewport dialog holding three small things, so most of it was white space and the captions band inside it kept looking crushed however it was sized. It is a card now, sized to what it holds, and the mentor is the subject of it. Presence: the mentor's own avatar, with a halo while the line is open and a ring while they speak, replaces the abstract orb. That retires ten drifting particles, five sound-wave bars and two competing pulses — nine animations that decorated rather than informed. The caller's own voice moved off the mentor's face and onto the microphone button they control. Status: one pill (shared Badge) with a state dot and a running call clock. "Listening…" / "Agent speaking" said what the ring, the dot and the controls already show, and changed every few seconds doing it, so the words are now sr-only — there is no ring to look at with a screen reader. The connection message stays visible while connecting, since nothing else conveys it. Transcript: the whole conversation rather than the last exchange, rendered as the same bubbles as the chat thread the call belongs to, minus its action toolbar — those actions need a saved message and a caption is not one until the call ends. Consecutive segments from one speaker are folded into a turn: LiveKit flushes an utterance in pieces, and a bubble per piece splits sentences wherever the recogniser paused. The panel follows the live turn and releases the moment the reader scrolls back. Captions are on by default now; only an explicit stored "false" turns them off, so a first call is captioned and storage failures fall back to on. Throughout: shared Avatar/Badge/Button/Tooltip and the theme's own tokens instead of hardcoded white, grey and red. The dialog's own close button is gone — hanging up is the way out of a call, and two controls that both end it is one too many; Escape and the overlay still work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaiooFFF4z3yMLHqsdKMBk
…e row Search now sits directly above the chats it searches, below the divider rather than up with New Chat, and takes the same row treatment as its new neighbours: same colour, radius, focus ring and label truncation. It had been styled after the New Chat pill it used to sit under, which is no longer where it lives. "Search chats" is now "Search" and "Chats" is now "Recents", in all four locales. The dialog's placeholder keeps the longer wording, which is what a placeholder is for. Neither group inside Recents is headed any more. "Recent" was a caps label repeating the panel's own name back at the reader, and with it gone "Pinned" had nothing to distinguish itself from — so pinned rows say so on the row: the pin sits in the three-dot slot and steps aside on hover for the menu that can unpin it. An unpinned row's slot stays empty until you reach for it. The pin also gives way while the row menu is open or an action is in flight, neither of which hover can see. Screen readers get the word, since they cannot see the pin or where the row sits. Fixes a pre-existing hover bug in passing: the row menus used the unnamed `group-hover` variant, and the shared Sidebar wrapper is itself a `.group`, so a pointer anywhere in the sidebar revealed every row's menu at once. They are scoped to `group/chat-row` now. The same mistake is still live on the projects rows (index.tsx), left alone here. E2E page objects follow the labels, and the recent/pinned lists carry testids because the headings they used to be located by are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaiooFFF4z3yMLHqsdKMBk
The slot sat at `right-1.5`, so its 24px box centred 6px inboard of the chevrons on the Agents/Recents/Projects triggers above it — two icon columns down the same edge, neither aligned to the other. Flush with the row's own edge, the slot centres on the same axis: measured 231 for every chevron, the pin and the three-dot icon alike. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaiooFFF4z3yMLHqsdKMBk
Member
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
Contributor
PR E2E - OKFull report, traces and screenshots
|
pr-e2e-tests.yml:
- Fail the summary gate when build-app-image fails/cancels. It only
checked e2e + unit-tests; a failed image build makes e2e resolve to
`skipped` (not `failure`), so a broken build went green with no e2e
run at all. build-app-image is already in the job's `needs`.
- Offline-detection guard: count only in_progress/completed jobs as
"started" (not transient waiting/pending), and widen the busy-probe
window to the 20 most recent runs (matching the env picker) so a run
queued behind a long suite isn't misdiagnosed as an offline runner.
pnpm-workspace.yaml:
- Correct the brace-expansion override comment: minimatch@3 is pulled by
eslint@9 + plugins (not just rollup-plugin-copy), so pinning
brace-expansion to the non-callable 5.0.8 is a latent `pnpm lint` crash
if a `{}` brace glob is ever added to the eslint config. Document the
hazard and the (untested) minimatch@3 -> ^10 mitigation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # .github/workflows/pr-e2e-tests.yml
sonegillis1
approved these changes
Jul 31, 2026
The dedup test (TC2) failed twice in run 30658029299 because the mentor it was driving got deleted mid-test. beforeEach read the mentor id straight out of the landing redirect via getPlatformContext(). That redirect follows recently-accessed-mentors on the shared test account, so under parallel workers it hands back whichever disposable mentor another spec created most recently. Both attempts adopted one: "E2E Mentor 1785527199152", then journey 63's "E2E Eval Mentor- 1785527203140". Each spec's cleanup then deleted its own mentor on schedule, settings/ and public-settings/ started 404ing, the chat never mounted and .chat-user-message-query timed out at 90s. TC2 is the only test in the file long enough to overlap a delete (3.0m and 2.5m, against 17-30s for the rest), which is why it alone failed. Create and own a mentor per test instead, reaped per suite via MentorTracker so the delete cannot race a retry reusing the worker. Timeout goes to 300s to cover creation on top of the existing two page loads and two AI round trips. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQ7pVFebzkWMaey1z3tMKK
bnsoni
self-requested a review
July 31, 2026 22:55
bnsoni
approved these changes
Jul 31, 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.
Checklist
Changes
Screenshots
Screencast.from.2026-07-29.19-44-34.mp4
Screencast.from.2026-07-29.19-44-08.mp4