Skip to content

fix(web-e2e): Human Chat Panel E2E navigation without Playwright baseURL#2209

Merged
webguru-hypha merged 76 commits into
mainfrom
cursor/fix-human-chat-panel-e2e-navigation-5377
May 4, 2026
Merged

fix(web-e2e): Human Chat Panel E2E navigation without Playwright baseURL#2209
webguru-hypha merged 76 commits into
mainfrom
cursor/fix-human-chat-panel-e2e-navigation-5377

Conversation

@webguru-hypha

@webguru-hypha webguru-hypha commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Includes CodeRabbit autofix batch in commit 2bba3a1 (pnpm override upper bounds, ICE pool clamp, join watchdog guard, deep-link host, mention inbox a11y + profile parity, URL paren trimming).

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Dismissable remote-media-stall banner; aggregated cross-room mentions; improved mention resolution and composer insertion; separate Join Audio / Join Video CTAs; shorter message deep-links; added call-related translations (en/de/es/fr/pt).
  • Bug Fixes

    • Fixed horizontal overflow in chat panels; improved deep-link scroll/retry behavior; more reliable tab scrolling.
  • Improvements

    • Auto-leave idle calls after 5 minutes; always-visible camera control; richer call diagnostics, telemetry and connection-recovery measures; small layout/padding adjustments.

@coderabbitai

coderabbitai Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Centralizes Playwright navigation with a new gotoApp helper; implements composer mention token format and resolved-mention plumbing across picker, insertion, and send-time conversion; adds group-call stall detection, WebRTC diagnostics, env-driven client settings and telemetry; updates matrix-provider room power-levels; plus various UI/layout, i18n, E2E, auth/device-id, and tooling changes.

Changes

E2E navigation

Layer / File(s) Summary
Data / util
apps/web-e2e/src/utils/nav-url.ts
Add resolveAppUrl and gotoApp(page, path) to normalize app-relative URLs and handle missing/whitespace BASE_URL.
Core page-object
apps/web-e2e/src/pages/base.page.ts
Add BasePage.gotoApp(path) delegating to resolveAppUrl.
Wiring / specs
apps/web-e2e/src/pages/*.page.ts, apps/web-e2e/src/*-*.spec.ts
Replace direct page.goto(...) with gotoApp(...); adjust some paths (e.g., add locale prefix /en/my-spaces).
Config
apps/web-e2e/playwright.config.ts
Trim/sanitize BASE_URL before using as use.baseURL.

Composer mention handling & picker

Layer / File(s) Summary
Token format / sanitizers
packages/epics/.../human-chat-display-mention.ts, .../human-chat-mention-token.ts
Introduce ZWSP-based composer mention token format, sanitization helpers, and parsing that strips ZWSP for query/termination logic.
Resolution hook
packages/epics/.../use-resolved-mention-candidate-label.ts
Add useResolvedMentionCandidateLabel returning { resolvedLabel, busy, avatarUrl?, pickDisabled }.
Picker row UI
packages/epics/.../human-chat-mention-candidate-row.tsx
Consume resolved label/avatar/busy/pickDisabled; make onPick optional and add onPickResolved.
Chat bar integration
packages/epics/.../human-chat-panel-chat-bar.tsx
Thread resolved labels into insertion flow, use formatComposerMentionToken, add onMergeMentionDisplayLabel/getMentionComposerLabel, and update keyboard/picker handling.
Send-time conversion
packages/epics/.../human-chat-display-mention.ts
Add wireComposerPlainForMatrixSend to produce Matrix wire text plus mention user-id list.

Mentions aggregation & inbox

Layer / File(s) Summary
Aggregate model / computation
packages/epics/.../matrix-chat-unread.ts
Add cross-room aggregated unread-mention computation and AggregatedMentionPreview model and helpers.
Inbox UI
packages/epics/.../human-chat-panel-mention-inbox.tsx
Add aggregatedMentions mode, aggregated-row rendering, keyboard activation, MentionInboxSenderName, and change onSelectMessage to accept optional fromRoomId.
Right-panel wiring
packages/epics/.../human-right-panel.tsx
Build sanitized-token→userId map, deduplicate tokens, persist roomId→slug mapping, and use wireComposerPlainForMatrixSend in send/edit flows; wire aggregated mention badge.

Message rendering & deep-linking

Layer / File(s) Summary
URL helpers / link shape
packages/epics/.../human-chat-message-link.ts
Shorten deep-link query to ?msg=<id>; add isHyphaDhoChatMessageUrl and hyphaDhoSlugFromUrl.
Renderer / pills
packages/epics/.../human-chat-panel-message-bubble.tsx
Introduce MxidMentionPill with async profile sync/skeleton and extend renderTextWithMentions to parse URLs and Hypha deep links as compact pill anchors.
Inbox / navigation
packages/epics/.../human-right-panel.tsx
Deep-link handler accepts fromRoomId, resolves slug via storage, navigates using shortened ?msg= form, and retries scroll after timeline growth.

Call UI, stall detection & telemetry

Layer / File(s) Summary
Hook core
packages/core/.../use-space-group-call.ts
Add join watchdog (CONNECT_STALL), joinEpoch guard, remote-media-stall detection + dismissal, delayed/paired outgoing-placement retries, periodic media snapshots, and debounced resync triggers.
Diagnostics & env
packages/core/.../group-call-webrtc-diagnostics.ts, packages/core/.../matrix-webrtc-env.ts
Add TURN/ICE summarization and readiness probe, attachable WebRTC diagnostics/stats forwarder, and env accessors for NEXT_PUBLIC_MATRIX_WEBRTC_* with typed parsing/defaults.
Telemetry types
packages/core/.../space-group-call-telemetry.ts
Expand telemetry event names and payload schema for call/ICE/WebRTC metrics and states.
UI banners / stage / controls
packages/epics/.../human-chat-panel-call-banner.tsx, .../call-stage.tsx, .../call-join-strip.tsx, .../in-call-controls.tsx
Add remoteMediaStall props and dismissal; split join strip into audio/video CTAs; always render camera toggle; adjust placeholders/aria/spinner and in-call control styling.
Right-panel wiring
packages/epics/.../human-right-panel.tsx
Expose remote-media-stall banner props, implement ghost-session auto-leave, and replace Radix Dialog full-view with draggable popup container.

Provider wiring & logging

Layer / File(s) Summary
Logger
packages/core/.../matrix-client-logger.ts
Add createHyphaMatrixClientLogger() respecting NEXT_PUBLIC_MATRIX_SDK_VERBOSE and forwarding SDK logs to console with namespace prefixing.
Provider wiring
packages/core/.../providers/matrix-provider.tsx
Pass custom logger and env-driven WebRTC options to Matrix client, set useE2eForGroupCall: false, seed m.room.power_levels in createRoom, call ensureRoomCallPowerLevels on create/join, and early-filter timeline event types.
Power-level helper
packages/core/.../providers/matrix-provider.tsx
Add ensureRoomCallPowerLevels reconciliation helper with permission-denied logging and safe swallow.

Layout, i18n, auth/device IDs, tooling & misc

Layer / File(s) Summary
Layout
apps/web/src/app/[lang]/dho/[id]/layout.tsx
Replace Container with a plain flex <div class="min-w-0 flex-1 px-4 sm:px-5"> and remove Container import.
Scroll bridge
packages/epics/.../panel-main-column-scroll-bridge.tsx
Add overflow-x-hidden to center scroll container.
i18n
packages/i18n/src/messages/{en,de,es,fr,pt}.json
Add call-related translation keys: callRemoteParticipantMediaStalled, callRemoteMediaStallHint, callErrorConnectStall, callLeftAudioIdle, callLeftVideoIdle.
Auth / device-id
packages/core/.../matrix-shared-secret.ts, apps/web/src/app/api/matrix/token/route.ts
Add deterministic device-id generation and include device_id in register/login; tighten existing-link validation to exclude legacy shared-secret device id.
Tooling / misc
package.json, .vscode/settings.json, apps/web-e2e/*, apps/web/.../breadcrumbs.tsx, .github/workflows/deploy-preview.yml
Update format scripts and pnpm.overrides; add Prettier VSCode settings; E2E path/nav tweaks; remove debug console.debug; safer Vercel deploy parsing and aliasing.

Sequence Diagram(s)

sequenceDiagram
    participant Composer as Composer (editor)
    participant TokenDetect as Token Detector
    participant Resolve as useResolvedMentionCandidateLabel
    participant Picker as Mention Picker Row
    participant ChatBar as HumanChatPanelChatBar
    participant RightPanel as HumanRightPanel
    participant Matrix as Matrix (sendMessage)

    Composer->>TokenDetect: Type "@<ZWSP>Name"
    TokenDetect->>Resolve: construct MentionPickCandidate
    Resolve->>Resolve: lookup Privy/person → resolvedLabel
    Resolve-->>Picker: return {resolvedLabel,busy,avatarUrl,pickDisabled}
    Picker->>ChatBar: onPickResolved(resolvedLabel)
    ChatBar->>ChatBar: formatComposerMentionToken(resolvedLabel)
    ChatBar->>RightPanel: onMergeMentionDisplayLabel(userId, displayLabel)
    RightPanel->>RightPanel: wireComposerPlainForMatrixSend(plain, map)
    RightPanel->>Matrix: sendMessage(wirePlain, mentionUserIds)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • plitzenberger
  • alexprate
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title follows the conventional commits format with type(scope): description. 'fix' is a valid type, 'web-e2e' is a clear scope, and the description accurately captures the main change: enabling E2E navigation for Human Chat Panel tests without relying on Playwright's baseURL configuration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/fix-human-chat-panel-e2e-navigation-5377

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown

🔗 Custom preview URL: https://pr-2209.preview-app.hypha.earth

cursoragent and others added 8 commits April 27, 2026 20:41
…baseURL

Relative page.goto paths failed when tests ran without apps/web-e2e
playwright.config.ts (wrong CWD), yielding "Cannot navigate to invalid URL".
Centralize BASE_URL-aligned absolute URLs via resolveAppUrl/gotoApp.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Drop the second-line Matrix ID / technical handle from mention rows.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Insert `@` + zero-width space + Hypha/profile label when picking a member,
strip `@` from labels for stable tokens, and rewrite to MXIDs plus
explicit m.mentions user_ids before sendMessage/editRoomMessage so Matrix
notifications and interoperability stay correct.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
When AI + Human panels wrap the layout, the main scroll root allowed
overflow-x; panning revealed empty space beside the fixed chat rail.
Add overflow-x-hidden on the scrollport so content cannot shift sideways.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Replace Container max-width centering with full-width plus horizontal
padding so hero and secondary chrome span the scroll column when panels
narrow the viewport. Clip horizontal overflow on the dual-panel main
scroll root to avoid dead gaps next to fixed side rails.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Share Matrix→Privy→Person resolution between picker rows and composer:
insert the same resolved label as display tokens; merge overrides into
mention maps so wire send + timeline pills match the dropdown (not
shortened MXID fallbacks).

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
…ll guard

- Pass resolveMentionMemberLabel into HumanChatPanelCallStage for roster/Person
  labels on video tiles (not Matrix shortened MXIDs).
- Auto-leave after 5 minutes when connected solo (othersInRoom === 0).
- Abort hung GroupCall.enter() after 90s with recoverable error + telemetry.
- i18n for idle-leave banner (en, de, es, fr, pt).

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Clear mention display overrides after roomId is declared. Widen
resolveMemberLabel typing to accept undefined for call tiles.
Gate keyboard mention pick until Hypha name resolution completes.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
@alexprate alexprate force-pushed the cursor/fix-human-chat-panel-e2e-navigation-5377 branch from 0f88b0e to ec51b68 Compare April 27, 2026 18:41
@alexprate alexprate marked this pull request as ready for review April 27, 2026 18:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/core/src/matrix/client/hooks/use-space-group-call.ts (1)

470-505: ⚠️ Potential issue | 🔴 Critical

Guard gc.enter() continuation against stale attempts.

If the stall watchdog fires (or a room switch/retry happens) while await gc.enter() is pending, this continuation can still run later and incorrectly set state to connected or interfere with a newer session. Add a stale-attempt check before both catch-path mutations and success-path state updates.

🐛 Proposed fix
@@
-      const joinT0 =
+      const joinT0 =
         typeof performance !== 'undefined' ? performance.now() : Date.now();
+      const joinAttemptStartedAt = joinT0;
       joinStartedAtRef.current = joinT0;
@@
       try {
         await gc.enter();
       } catch (e) {
         clearConnectingStallTimer();
+        if (
+          joinStartedAtRef.current !== joinAttemptStartedAt ||
+          groupCallRef.current !== gc
+        ) {
+          return;
+        }
         isJoiningRef.current = false;
@@
       }
 
       clearConnectingStallTimer();
+      if (
+        joinStartedAtRef.current !== joinAttemptStartedAt ||
+        groupCallRef.current !== gc
+      ) {
+        return;
+      }
 
       setCallState('connected');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts` around lines
470 - 505, The continuation after awaiting gc.enter() must bail out if the join
attempt became stale; capture the current joinStartedAtRef.current (e.g., const
attemptStartedAt = joinStartedAtRef.current) immediately before calling await
gc.enter(), then at the top of the catch block and again on the success path
check if joinStartedAtRef.current !== attemptStartedAt and if so return early
without mutating shared state. Apply this guard before any use of
isJoiningRef.current, setCallState, setCallKind, setThreadContext, runCleanup,
setIsMicrophoneMuted, setIsLocalVideoMuted, setActiveKeyFromGroupCall,
setErrorCode, or clearing timers so only the active join attempt updates state.
apps/web-e2e/src/menu-top-consistent-height.spec.ts (1)

39-63: ⚠️ Potential issue | 🟠 Major

Accessibility coverage is missing for the navigated pages in this spec.

After navigating to /en/dho/hypha/agreements and /en/network, add an axe-core assertion to keep accessibility regressions from slipping through this suite.

As per coding guidelines: "Accessibility assertions with axe-core where pages are visited".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web-e2e/src/menu-top-consistent-height.spec.ts` around lines 39 - 63,
After each navigation in the spec (the test that compares header heights and the
test '--menu-top-height CSS variable should be set and integer'), run the
axe-core accessibility check once the page is fully loaded and visible;
specifically, after the gotoApp(...) + page.waitForLoadState('domcontentloaded')
and after waiting for the header locator (menuSelector) to be visible for both
the '/en/dho/hypha/agreements' and '/en/network' visits, invoke your project’s
axe helper (e.g., expectA11y(page) or the equivalent runAxe helper) to assert no
a11y violations before continuing with boundingBox assertions so accessibility
regressions are caught.
apps/web-e2e/src/human-chat-panel-feature-flag.spec.ts (1)

66-77: ⚠️ Potential issue | 🟠 Major

Add an accessibility assertion after these page visits.

These tests now navigate via gotoApp(...), but they still lack an axe-core check on the visited page, so accessibility regressions can pass unnoticed in this path.

As per coding guidelines: "Accessibility assertions with axe-core where pages are visited".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web-e2e/src/human-chat-panel-feature-flag.spec.ts` around lines 66 - 77,
After each gotoApp(...) + page.waitForLoadState('domcontentloaded') call add an
axe-core accessibility assertion: import or call your project's axe helper
(e.g., runAxe or axeCheck) and invoke it against the Playwright page immediately
after page.waitForLoadState; update both the test containing the chatButton
visibility check and the 'should render page content normally' test so they each
run the accessibility check on the loaded page.
apps/web-e2e/src/coherence-chat-panel.spec.ts (1)

50-90: ⚠️ Potential issue | 🟠 Major

Baseline navigation tests need an accessibility assertion.

These active tests visit /en/dho/${SPACE_SLUG}/coherence but do not run an axe-core check, leaving a required accessibility gate uncovered in the updated path.

As per coding guidelines: "Accessibility assertions with axe-core where pages are visited".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web-e2e/src/coherence-chat-panel.spec.ts` around lines 50 - 90, These
tests that call gotoApp(page, `/en/dho/${SPACE_SLUG}/coherence`) are missing the
required axe-core accessibility assertion; after each navigation +
page.waitForLoadState('domcontentloaded') in the tests named "panel opens...",
"panel opens in space chat mode by default from coherence page", "panel in space
mode has no back button (hash icon is shown)", and "space chat panel has
functional chat input and send button", invoke the repository's axe helper (for
example runAxe(page) or the project's injectAxe + axe.run helper) and assert
there are no violations before interacting with chatPanel (i.e., add await
runAxe(page) / equivalent right after gotoApp(...) and
page.waitForLoadState('domcontentloaded')). Ensure you call the same helper used
elsewhere in tests to keep consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web-e2e/src/ai-chat-feature-flag.spec.ts`:
- Line 61: The test uses a non-locale route in the call to gotoApp(page,
'/my-spaces') which can cause redirect-dependent flakiness; update that call in
apps/web-e2e/src/ai-chat-feature-flag.spec.ts to use the explicit
locale-prefixed path '/en/my-spaces' (and ensure any other gotoApp calls in the
same suite follow the same '/en/...' pattern) so navigation no longer depends on
redirects.

In `@apps/web-e2e/src/utils/nav-url.ts`:
- Around line 10-12: The code trims BASE_URL in resolveBaseUrl() but
playwright.config.ts reads process.env['BASE_URL'] raw, causing inconsistent URL
resolution; update playwright.config.ts so it parses BASE_URL the same way (use
process.env['BASE_URL']?.trim() || DEFAULT_BASE_URL) to match resolveBaseUrl(),
ensuring both resolveBaseUrl() and the Playwright config use identical
trimming/fallback logic.

In
`@packages/epics/src/common/human-chat-panel/use-resolved-mention-candidate-label.ts`:
- Around line 48-61: busy currently trusts usePersonBySub's isLoading, but that
hook returns isLoading = false when JWT is missing (SWR key null), so
roster-backed candidates become pickable during auth bootstrap; update the
busy/pickDisabled logic to also consider JWT readiness for the privySub path:
when privySub/resolvedSub is present treat the candidate as busy if
loadingPerson OR the auth JWT is not yet available (e.g., check the auth/jwt
readiness value from your auth hook or context), so busy = Boolean(candidate) &&
( ((!privySub && loadingLink) || (Boolean(resolvedSub) && (loadingPerson ||
!jwtReady))) ). Ensure useMemo/deps include the jwtReady value and update
pickDisabled to use busy.

In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 887-896: The current mentionSanitizedLabelToUserId collapses
identical sanitized labels to the first MXID, causing ambiguous mentions; change
its creation so collisions are detected and handled: while iterating
mentionLabelByUserId and using sanitizeMentionDisplayLabel, track duplicates
(e.g., store a sentinel or multiple IDs for a key) and do NOT map a plain
sanitized label to a single MXID when a duplicate exists—instead mark that key
as collided so downstream code (wireComposerPlainForMatrixSend) will use a
fallback token containing an unambiguous identifier (raw MXID or a disambiguated
label) for those users; update wireComposerPlainForMatrixSend to check the
collision marker from mentionSanitizedLabelToUserId and emit the fallback token
for any collided/same-key entries.

---

Outside diff comments:
In `@apps/web-e2e/src/coherence-chat-panel.spec.ts`:
- Around line 50-90: These tests that call gotoApp(page,
`/en/dho/${SPACE_SLUG}/coherence`) are missing the required axe-core
accessibility assertion; after each navigation +
page.waitForLoadState('domcontentloaded') in the tests named "panel opens...",
"panel opens in space chat mode by default from coherence page", "panel in space
mode has no back button (hash icon is shown)", and "space chat panel has
functional chat input and send button", invoke the repository's axe helper (for
example runAxe(page) or the project's injectAxe + axe.run helper) and assert
there are no violations before interacting with chatPanel (i.e., add await
runAxe(page) / equivalent right after gotoApp(...) and
page.waitForLoadState('domcontentloaded')). Ensure you call the same helper used
elsewhere in tests to keep consistency.

In `@apps/web-e2e/src/human-chat-panel-feature-flag.spec.ts`:
- Around line 66-77: After each gotoApp(...) +
page.waitForLoadState('domcontentloaded') call add an axe-core accessibility
assertion: import or call your project's axe helper (e.g., runAxe or axeCheck)
and invoke it against the Playwright page immediately after
page.waitForLoadState; update both the test containing the chatButton visibility
check and the 'should render page content normally' test so they each run the
accessibility check on the loaded page.

In `@apps/web-e2e/src/menu-top-consistent-height.spec.ts`:
- Around line 39-63: After each navigation in the spec (the test that compares
header heights and the test '--menu-top-height CSS variable should be set and
integer'), run the axe-core accessibility check once the page is fully loaded
and visible; specifically, after the gotoApp(...) +
page.waitForLoadState('domcontentloaded') and after waiting for the header
locator (menuSelector) to be visible for both the '/en/dho/hypha/agreements' and
'/en/network' visits, invoke your project’s axe helper (e.g., expectA11y(page)
or the equivalent runAxe helper) to assert no a11y violations before continuing
with boundingBox assertions so accessibility regressions are caught.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts`:
- Around line 470-505: The continuation after awaiting gc.enter() must bail out
if the join attempt became stale; capture the current joinStartedAtRef.current
(e.g., const attemptStartedAt = joinStartedAtRef.current) immediately before
calling await gc.enter(), then at the top of the catch block and again on the
success path check if joinStartedAtRef.current !== attemptStartedAt and if so
return early without mutating shared state. Apply this guard before any use of
isJoiningRef.current, setCallState, setCallKind, setThreadContext, runCleanup,
setIsMicrophoneMuted, setIsLocalVideoMuted, setActiveKeyFromGroupCall,
setErrorCode, or clearing timers so only the active join attempt updates state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 62dbfa71-6d90-4577-a418-74a27faf702f

📥 Commits

Reviewing files that changed from the base of the PR and between 2e7b2b4 and ec51b68.

📒 Files selected for processing (27)
  • apps/web-e2e/src/ai-chat-feature-flag.spec.ts
  • apps/web-e2e/src/coherence-chat-panel.spec.ts
  • apps/web-e2e/src/human-chat-panel-feature-flag.spec.ts
  • apps/web-e2e/src/menu-top-consistent-height.spec.ts
  • apps/web-e2e/src/pages/ai-chat-panel.page.ts
  • apps/web-e2e/src/pages/base.page.ts
  • apps/web-e2e/src/pages/coherence-chat-panel.page.ts
  • apps/web-e2e/src/pages/coherence.page.ts
  • apps/web-e2e/src/pages/human-chat-panel.page.ts
  • apps/web-e2e/src/pages/layout.page.ts
  • apps/web-e2e/src/pages/my-spaces.page.ts
  • apps/web-e2e/src/panels-space-context.spec.ts
  • apps/web-e2e/src/utils/nav-url.ts
  • apps/web/src/app/[lang]/dho/[id]/layout.tsx
  • packages/core/src/matrix/client/hooks/use-space-group-call.ts
  • packages/epics/src/common/human-chat-panel/human-chat-display-mention.ts
  • packages/epics/src/common/human-chat-panel/human-chat-mention-candidate-row.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-mention-token.ts
  • packages/epics/src/common/human-chat-panel/human-chat-panel-chat-bar.tsx
  • packages/epics/src/common/human-chat-panel/use-resolved-mention-candidate-label.ts
  • packages/epics/src/common/human-right-panel.tsx
  • packages/epics/src/common/panel-main-column-scroll-bridge.tsx
  • packages/i18n/src/messages/de.json
  • packages/i18n/src/messages/en.json
  • packages/i18n/src/messages/es.json
  • packages/i18n/src/messages/fr.json
  • packages/i18n/src/messages/pt.json

Comment thread apps/web-e2e/src/ai-chat-feature-flag.spec.ts Outdated
Comment thread apps/web-e2e/src/utils/nav-url.ts
Comment thread packages/epics/src/common/human-right-panel.tsx Outdated
cursoragent and others added 5 commits April 27, 2026 22:34
Avoid redirect-dependent navigation per CodeRabbit review on PR #2209.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Align baseURL parsing with resolveBaseUrl() for consistent navigation.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Person lookup uses usePersonBySub which does not fetch until JWT exists;
keep busy until auth finishes loading the token when a privySub path is active.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
When sanitized labels collide, map keys and composer tokens include a shortened
MXID suffix so wire send resolves to the intended user.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Cold loads map history before `getUserId()` is available, so own rows were
stored as member with technical display labels. Reconcile role, senderName,
and avatar when currentUserId or profile avatar resolves.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 1800-1813: The optimistic pending row is using wirePlain (which
contains MXIDs) as captionPreview; change the code that constructs the pending
object (where pendingId is set and setSendingPending is called) to use the
original text variable for captionPreview instead of wirePlain, leaving
wirePlain (from wireComposerPlainForMatrixSend) only for the actual send logic;
update the setSendingPending call that creates the object with id: pendingId,
attachmentCount, uploadedCount, etc., to set captionPreview: text so the UI
shows the user-typed display names while wirePlain remains used by the send
function.
- Around line 894-904: The callback mergeMentionDisplayLabel shadows the outer
translation function t by declaring const t = displayLabel.trim();; rename the
local variable (e.g., to trimmed or trimmedLabel) inside
mergeMentionDisplayLabel so it no longer conflicts with the
useTranslations('HumanChatPanel') t and update all uses within that callback
(setMentionDisplayOverride logic) to use the new name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 97396cf9-0b3b-46d3-ba25-e579778ef1ab

📥 Commits

Reviewing files that changed from the base of the PR and between ec51b68 and c14ddb1.

📒 Files selected for processing (5)
  • apps/web-e2e/playwright.config.ts
  • apps/web-e2e/src/ai-chat-feature-flag.spec.ts
  • packages/epics/src/common/human-chat-panel/human-chat-panel-chat-bar.tsx
  • packages/epics/src/common/human-chat-panel/use-resolved-mention-candidate-label.ts
  • packages/epics/src/common/human-right-panel.tsx

Comment thread packages/epics/src/common/human-right-panel.tsx
Comment thread packages/epics/src/common/human-right-panel.tsx
@alexprate

Copy link
Copy Markdown
Contributor

@code rabbit please review

cursoragent and others added 5 commits April 27, 2026 22:58
Message body used synchronous resolveMatrixMemberLabel only; Matrixoften
shows bridged Privy MXIDs until roster/Hypha hydrate. Mirror sender pills:
use matrix_user_links + Person when label still looks technical.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
…s lag

Emit connected/media_snapshot/remote_media_stall telemetry in dev; detect
when participants exist without remote userMedia feeds for 45s and show an
inline banner with dismiss. Periodic snapshots every 12s while connected.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Prefer roster merge over raw Matrix member displaynames; resolve Person via
matrix_user_links when labels still look bridged. Apply same logic to
connecting placeholders.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Remove ring outline; use minimal horizontal padding and smaller corner radius
so the background sits close to the @ name.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Parse http(s) URLs in plaintext bodies, trim trailing punctuation from
hrefs, and wrap with anchor target _blank rel noopener noreferrer.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
cursoragent and others added 4 commits April 27, 2026 23:32
Copy link now omits redundant chat param (?msg only). Resolve msg-only deep
links when viewing the same space. Render Hypha chat URLs as compact # slug
chips instead of raw long URLs.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
CodeQL flagged repeated regex tests on user-controlled chat text.
Strip trailing URL punctuation with a constant-time character set and
scan http(s) URLs without a nested-quantifier regex.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
The first-unread marker used accent-tinted backgrounds that read as a
green strip next to messages (including reply context). Use neutral
border and muted row tint instead, aligned with mention-highlight styling.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
Address known advisories: bump hono to a patched 4.12.x, ensure
@hono/node-server stays on the 1.19.x line >=1.19.13 for static
middleware fixes, enforce dompurify >=3.4.0 for transitive mermaid
chains, and pin @xmldom/xmldom >=0.9.9 under mathjax tooling.

Co-authored-by: webguru-hypha <webguru-hypha@users.noreply.github.com>
n0umen0n added 14 commits April 29, 2026 11:26
- Added retry mechanism for outgoing call placements to handle rejoin and refresh scenarios.
- Implemented cleanup for stale timers and member state before entering a group call.
- Updated error handling for permission-related issues in group calls.
…ion handling

- Updated URL validation to correctly handle subdomains of 'hypha.earth'.
- Added a utility function to determine if an event originates from an interactive child element.
- Modified mention selection logic to prevent actions when triggered from interactive child elements.
…in mention inbox

- Simplified the logic for checking if an event originates from an interactive child element by consolidating the closest selector into a single line.
@n0umen0n n0umen0n disabled auto-merge April 30, 2026 07:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/web/src/app/api/matrix/token/route.ts (1)

204-268: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't make a valid token depend on a destructive recovery path.

For legacy device IDs, this code resets the user's Matrix password before the replacement token/deviceId are safely persisted. If loginUser or updateEncryptedAccessTokenAction fails after resetPassword(..., logout_devices: true), the request can revoke the user's working token and leave them locked out even though hasValidToken was already true. This needs an atomic or compensating rotation flow before merge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/api/matrix/token/route.ts` around lines 204 - 268, The code
currently calls matrixAuthClient.resetPassword(...) which may revoke the user's
active token before the new token/device are persisted; change the rotation flow
so revocation is only performed after a successful persistence: call
resetPassword in a non-destructive mode (do not logout devices) or use a "rotate
without logout" option if available, then call matrixAuthClient.loginUser(...)
to obtain the new accessToken/deviceId, persist them via
updateEncryptedAccessTokenAction(...), and only after successful persistence
perform a final destructive logout (or reset with logout_devices:true) if you
need to revoke old sessions; update the logic around resetPassword, loginUser,
and updateEncryptedAccessTokenAction to follow this order and add error
handling/compensation to avoid leaving the user locked out.
packages/epics/src/common/human-chat-panel/human-chat-panel-call-stage.tsx (1)

1466-1481: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remote screenshare audio is muted permanently.

The tile now always renders <video muted />, but the separate audible <audio> element is skipped for isShare. A remote screenshare that includes audio will therefore look connected while never producing sound. Render the audio element for remote share feeds too, or stop force-muting shares whose audio is not mirrored elsewhere.

Suggested fix
-      {!feed.isLocal() && !isShare ? (
+      {!feed.isLocal() ? (
         <audio ref={audioRef} autoPlay playsInline aria-hidden />
       ) : null}

Also applies to: 1597-1599

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/epics/src/common/human-chat-panel/human-chat-panel-call-stage.tsx`
around lines 1466 - 1481, The video element in human-chat-panel-call-stage.tsx
is always rendered with muted which mutes remote screenshare audio and the
separate audible <audio> element is currently skipped when isShare is true;
update the render logic in the component (look for hasVideo, isShare, the video
element using ref and ariaLabel) so that for remote share feeds you still
create/render an audio element bound to the same MediaStream (or stop forcing
muted on streams that have distinct audio tracks), ensuring shares with audio
produce sound while preserving autoplay/playsInline behavior.
♻️ Duplicate comments (2)
packages/core/src/matrix/client/hooks/use-space-group-call.ts (1)

818-824: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Re-check join liveness after camera-unmute await.

After Line 820, the join can already be invalidated (cleanup/watchdog/leave), but the code continues into diagnostics and setCallState('connected'). Add the same epoch/groupCall liveness guard here.

🔧 Suggested fix
       if (kind === 'video') {
         try {
           await gc.setLocalVideoMuted(false);
         } catch {
           /* camera permission / hardware — remain in call with video off */
         }
+        if (joinEpoch !== joinEpochRef.current || groupCallRef.current !== gc) {
+          isJoiningRef.current = false;
+          return;
+        }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts` around lines
818 - 824, After awaiting gc.setLocalVideoMuted(false) when kind === 'video',
re-check the join liveness using the same epoch/groupCall guard used elsewhere
in use-space-group-call (i.e., validate that the current epoch and groupCall are
still the same/valid) before proceeding to diagnostics or calling
setCallState('connected'); if the guard fails, abort further processing to avoid
setting state for a stale/left call.
packages/epics/src/common/human-chat-panel/human-chat-panel-mention-inbox.tsx (1)

104-111: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Row activation is blocked by the interactive-target guard.

eventComesFromInteractiveChild() matches the row itself (role="button" + tabIndex=0), so both onClick and onKeyDown exit early and the mention row never selects.

🔧 Suggested fix
-function eventComesFromInteractiveChild(target: EventTarget | null): boolean {
-  return (
-    target instanceof Element &&
-    Boolean(
-      target.closest('a,button,[role="link"],[role="button"],[tabindex="0"]'),
-    )
-  );
-}
+function eventComesFromInteractiveChild(
+  target: EventTarget | null,
+  currentTarget: HTMLElement,
+): boolean {
+  if (!(target instanceof Element)) return false;
+  const interactive = target.closest(
+    'a,button,[role="link"],input,select,textarea',
+  );
+  return Boolean(interactive && interactive !== currentTarget);
+}
- onClick={(e) => {
-   if (eventComesFromInteractiveChild(e.target)) return;
+ onClick={(e) => {
+   if (eventComesFromInteractiveChild(e.target, e.currentTarget)) return;
    onSelectMessage(row.eventId, row.roomId);
  }}
  onKeyDown={(e) => {
-   if (eventComesFromInteractiveChild(e.target)) return;
+   if (eventComesFromInteractiveChild(e.target, e.currentTarget)) return;
    selectMentionRowKeyDown(e, () =>
      onSelectMessage(row.eventId, row.roomId),
    );
  }}

Also applies to: 184-193, 243-250

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/epics/src/common/human-chat-panel/human-chat-panel-mention-inbox.tsx`
around lines 104 - 111, The guard is matching the row itself because
Element.closest includes the element, so change eventComesFromInteractiveChild
to only treat a match as interactive when the closest(...) result is a different
element than the event target element (i.e., cast target to Element, call
el.closest(...), then return Boolean(match && match !== el)); update the same
pattern in the other occurrences referenced (around the blocks at 184-193 and
243-250) so clicks/keys on the row element itself are not treated as
interactive-child events.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Around line 17-18: The npm scripts "format:check" and "format:fix" in
package.json use single-quoted glob arguments which break on Windows; update the
script values for "format:check" and "format:fix" to use JSON-escaped
double-quoted glob arguments instead of single quotes (i.e., replace the
single-quoted patterns passed to prettier with double-quoted patterns) so the
globs are recognized cross-platform.

In `@packages/core/src/matrix/client/hooks/space-group-call-utils.ts`:
- Around line 12-17: The permission-like error check removed 'NotReadableError'
and 'OverconstrainedError', which causes device-busy/constraint failures to be
misclassified; restore those two error names into the same conditional that
currently checks name === 'NotAllowedError' || name ===
'PermissionDismissedError' || name === 'SecurityError' so they return true like
the others (this affects the behavior relied upon by the tests in
use-space-group-call.test.ts and the PERMISSION_DENIED handling in
use-space-group-call.ts).

In `@packages/core/src/matrix/client/providers/matrix-provider.tsx`:
- Around line 1277-1280: The current implementation calls
ensureRoomCallPowerLevels(client, resolvedId) with void, causing a race where
callers can start a call before power-levels are reconciled; change this to
await the promise from ensureRoomCallPowerLevels so joinRoom waits for the
reconciliation to complete (use the resolvedId from joined.roomId and await
ensureRoomCallPowerLevels(client, resolvedId)), optionally surrounding the await
with try/catch if you want to surface or log reconciliation errors rather than
swallowing them.

In
`@packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx`:
- Line 47: The current showAudioButton logic hides the audio CTA whenever
hasDurable (or durableMessage) is true and deviceCount === 0, preventing the
callLeftAudioIdle restart path even though onJoinAudio is wired; update the
condition so the durable idle banner does not suppress the audio action—compute
showAudioButton using deviceCount and the presence of the onJoinAudio handler
(e.g., treat audio available if deviceCount > 0 OR onJoinAudio is defined) and
remove/ignore the hasDurable/durableMessage check; adjust any related UI
branches that rely on showAudioButton (callLeftAudioIdle, onJoinAudio) to use
the new boolean.

In
`@packages/epics/src/common/human-chat-panel/human-chat-panel-in-call-controls.tsx`:
- Around line 117-146: The compact (non-full) camera button can collapse because
neutralBtn currently only provides color classes; update the className logic in
the onToggleCamera button (inside human-chat-panel-in-call-controls.tsx) so the
non-full variant always gets the fixed-size button shell (e.g., include
'inline-flex items-center justify-center' plus width/height classes) rather than
relying on the conditional that only applies when isFull or isLocalVideoMuted;
you can either add the size classes to neutralBtn or ensure the ternary that
picks between neutralBtn/baseBtn/camOffBtn also appends the fixed-size shell
when !isFull so the button remains a consistent hit target (adjust classes near
neutralBtn, baseBtn, camOffBtn and keep icon sizing via the icon class).

In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 1799-1804: The branch currently blocks cross-room mention routing
by requiring `current` to be truthy; remove the `current &&` check so the
condition reads effectively "if targetRoom exists and window is defined and
targetRoom !== current" (this will allow navigation when `current` is null
during cold load/join). Update the conditional around `targetRoom`/`current` in
the handler (referencing the `targetRoom` and `current` variables in
human-right-panel) and ensure downstream navigation code handles a null
`current` gracefully.

---

Outside diff comments:
In `@apps/web/src/app/api/matrix/token/route.ts`:
- Around line 204-268: The code currently calls
matrixAuthClient.resetPassword(...) which may revoke the user's active token
before the new token/device are persisted; change the rotation flow so
revocation is only performed after a successful persistence: call resetPassword
in a non-destructive mode (do not logout devices) or use a "rotate without
logout" option if available, then call matrixAuthClient.loginUser(...) to obtain
the new accessToken/deviceId, persist them via
updateEncryptedAccessTokenAction(...), and only after successful persistence
perform a final destructive logout (or reset with logout_devices:true) if you
need to revoke old sessions; update the logic around resetPassword, loginUser,
and updateEncryptedAccessTokenAction to follow this order and add error
handling/compensation to avoid leaving the user locked out.

In `@packages/epics/src/common/human-chat-panel/human-chat-panel-call-stage.tsx`:
- Around line 1466-1481: The video element in human-chat-panel-call-stage.tsx is
always rendered with muted which mutes remote screenshare audio and the separate
audible <audio> element is currently skipped when isShare is true; update the
render logic in the component (look for hasVideo, isShare, the video element
using ref and ariaLabel) so that for remote share feeds you still create/render
an audio element bound to the same MediaStream (or stop forcing muted on streams
that have distinct audio tracks), ensuring shares with audio produce sound while
preserving autoplay/playsInline behavior.

---

Duplicate comments:
In `@packages/core/src/matrix/client/hooks/use-space-group-call.ts`:
- Around line 818-824: After awaiting gc.setLocalVideoMuted(false) when kind ===
'video', re-check the join liveness using the same epoch/groupCall guard used
elsewhere in use-space-group-call (i.e., validate that the current epoch and
groupCall are still the same/valid) before proceeding to diagnostics or calling
setCallState('connected'); if the guard fails, abort further processing to avoid
setting state for a stale/left call.

In
`@packages/epics/src/common/human-chat-panel/human-chat-panel-mention-inbox.tsx`:
- Around line 104-111: The guard is matching the row itself because
Element.closest includes the element, so change eventComesFromInteractiveChild
to only treat a match as interactive when the closest(...) result is a different
element than the event target element (i.e., cast target to Element, call
el.closest(...), then return Boolean(match && match !== el)); update the same
pattern in the other occurrences referenced (around the blocks at 184-193 and
243-250) so clicks/keys on the row element itself are not treated as
interactive-child events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: aaedef52-3734-4469-9f1d-e2eb2371e864

📥 Commits

Reviewing files that changed from the base of the PR and between 650c8e9 and a8c98f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (30)
  • .vscode/settings.json
  • apps/web/src/app/[lang]/dho/[id]/_components/breadcrumbs.tsx
  • apps/web/src/app/api/matrix/token/route.ts
  • docs/requirements/matrix-voip-turn-server-setup.md
  • docs/requirements/voice-video-call-implementation-spec.md
  • docs/requirements/voice-video-call-matrix-tech-spec.md
  • docs/requirements/voice-video-call-phase-0-runbook.md
  • package.json
  • packages/core/src/coherence/lib/matrix-shared-secret.ts
  • packages/core/src/matrix/client/hooks/space-group-call-telemetry.ts
  • packages/core/src/matrix/client/hooks/space-group-call-utils.ts
  • packages/core/src/matrix/client/hooks/use-matrix-user-ids-by-privy-subs.ts
  • packages/core/src/matrix/client/hooks/use-space-group-call.ts
  • packages/core/src/matrix/client/matrix-client-logger.ts
  • packages/core/src/matrix/client/matrix-webrtc-env.ts
  • packages/core/src/matrix/client/providers/matrix-provider.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-message-link.ts
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-banner.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-stage.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-in-call-controls.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-mention-inbox.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-message-bubble.tsx
  • packages/epics/src/common/human-right-panel.tsx
  • packages/epics/src/spaces/components/space-search.tsx
  • packages/i18n/src/messages/de.json
  • packages/i18n/src/messages/en.json
  • packages/i18n/src/messages/es.json
  • packages/i18n/src/messages/fr.json
  • packages/i18n/src/messages/pt.json
💤 Files with no reviewable changes (2)
  • packages/epics/src/spaces/components/space-search.tsx
  • apps/web/src/app/[lang]/dho/[id]/_components/breadcrumbs.tsx

Comment thread package.json Outdated
Comment thread packages/core/src/matrix/client/hooks/space-group-call-utils.ts
Comment thread packages/core/src/matrix/client/providers/matrix-provider.tsx
Comment thread packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx Outdated
Comment thread packages/epics/src/common/human-right-panel.tsx
@n0umen0n n0umen0n requested a review from alexprate April 30, 2026 09:08
- Added additional error types to the group call permission validation.
- Updated the join audio button logic to handle optional audio joining more gracefully.
- Refactored the MatrixProvider to ensure proper handling of room call power levels.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
packages/epics/src/common/human-right-panel.tsx (1)

1306-1320: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Dispose draft attachments in the membership-denied reset path.

This branch clears the room state but leaves draftAttachments and their blob URLs alive. If a user had attached files before access is revoked, those URLs leak until unmount and the stale attachments reappear if access is restored.

Suggested fix
     if (roomId) {
       matrixRef.current.unregisterRoomListener(roomId);
     }
+    disposeDraftAttachmentUrls(draftAttachmentsRef.current);
+    setDraftAttachments([]);
     joinedRef.current = null;
     setRoomId(null);
     setMessages([]);
     setReplyDraft(null);
     setEditDraft(null);
     setInput('');
     setError(null);
+    setSendingPending(null);
     setCallFullViewOpen(false);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/epics/src/common/human-right-panel.tsx` around lines 1306 - 1320,
The reset branch inside the useEffect (the path that runs when mode === 'space'
and hasSpaceChatAccess is false) currently clears room state but leaves
draftAttachments and their blob URLs alive; update this branch to revoke any
Blob URLs and clear the draft attachments state: locate the draftAttachments
state variable and, before calling
setRoomId(null)/setMessages([])/setReplyDraft(null)/setEditDraft(null)/setInput('')
etc., iterate draftAttachments (or draftAttachmentsRef) and call
URL.revokeObjectURL(...) for each attachment blobUrl (safely checking
existence), then clear the attachments via setDraftAttachments([]) or
setDraftAttachments(null) to prevent leaks and stale re-appearance when access
is restored.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/matrix/client/providers/matrix-provider.tsx`:
- Around line 186-188: The current blanket catch around await
ensureRoomCallPowerLevels(...) swallows all failures; change it to only handle
known permission-denial errors and re-throw everything else so callers can act
on failures. In the catch block for ensureRoomCallPowerLevels, inspect the
caught error (e.g., check err.errcode === 'M_FORBIDDEN' or err.name/instance
indicating a Matrix permission error) and log a warning for that case, but for
any other error re-throw it (throw error) after logging; reference
ensureRoomCallPowerLevels and the surrounding try/catch in matrix-provider.tsx
when applying this change.

In
`@packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx`:
- Around line 84-99: The audio CTA's visual state doesn't match its DOM disabled
state when onJoinAudio is missing; update the button className logic in the
showAudioButton block (the button using onJoinAudio, disabled, busy) to include
the same predicate (!onJoinAudio) used for the disabled prop so the disabled
styling (cursor-not-allowed, opacity-50) is applied when onJoinAudio is absent
and the hover class (hover:bg-muted) is only applied when disabled, busy, and
onJoinAudio indicate the button is truly enabled.

In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 930-933: The computed drag offsets passed to setCallPopupOffset
(x: drag.startOffsetX + (e.clientX - drag.startClientX), y: drag.startOffsetY +
(e.clientY - drag.startClientY)) are unbounded; clamp both x and y to sane
viewport bounds before calling setCallPopupOffset so the popup cannot be dragged
completely off-screen. Calculate min/max using 0 as the top/left bounds and
(window.innerWidth - popupWidth) / (window.innerHeight - popupHeight) as the
right/bottom bounds (obtain popupWidth/popupHeight from a measured element or
constants), clamp the computed x/y to those limits, then call setCallPopupOffset
with the clamped values; keep references to setCallPopupOffset, callPopupOffset,
and the drag calculation expression to locate the change.
- Around line 2713-2723: The popup opens without moving keyboard focus into the
custom dialog, so when the trigger unmounts users lose their focus anchor; add a
ref for the dialog container (e.g. callFullViewDialogRef) and ensure the
container is programmatically focusable (tabIndex={-1} or role="dialog" with
focusable element), then when opening callFullViewDialogRef.current?.focus()
(use an effect or the same open handler where setCallFullViewOpen(true) is
invoked) and preserve the trigger element in callFullViewTriggerRef to restore
focus on close (the close button already does this). Update the dialog component
that renders in full-view to accept and focus that ref so keyboard users always
get focus inside the dialog when it opens.

---

Duplicate comments:
In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 1306-1320: The reset branch inside the useEffect (the path that
runs when mode === 'space' and hasSpaceChatAccess is false) currently clears
room state but leaves draftAttachments and their blob URLs alive; update this
branch to revoke any Blob URLs and clear the draft attachments state: locate the
draftAttachments state variable and, before calling
setRoomId(null)/setMessages([])/setReplyDraft(null)/setEditDraft(null)/setInput('')
etc., iterate draftAttachments (or draftAttachmentsRef) and call
URL.revokeObjectURL(...) for each attachment blobUrl (safely checking
existence), then clear the attachments via setDraftAttachments([]) or
setDraftAttachments(null) to prevent leaks and stale re-appearance when access
is restored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5790116e-1fe8-464e-81fd-176245bb5a8a

📥 Commits

Reviewing files that changed from the base of the PR and between a8c98f3 and 13612d9.

📒 Files selected for processing (6)
  • package.json
  • packages/core/src/matrix/client/hooks/space-group-call-utils.ts
  • packages/core/src/matrix/client/providers/matrix-provider.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-in-call-controls.tsx
  • packages/epics/src/common/human-right-panel.tsx

Comment thread packages/core/src/matrix/client/providers/matrix-provider.tsx
Comment thread packages/epics/src/common/human-right-panel.tsx
Comment thread packages/epics/src/common/human-right-panel.tsx
n0umen0n and others added 3 commits April 30, 2026 12:39
… features

- Added detailed error handling for permission issues in Matrix call power levels.
- Implemented focus management for the call full view dialog when opened.
- Refined the join audio button logic to account for additional states and improve user experience.
- Adjusted formatting for popup width calculation for better clarity.
- Enhanced readability of the conditional check for active button element in full view dialog handling.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/matrix/client/providers/matrix-provider.tsx`:
- Around line 570-571: The call to ensureRoomCallPowerLevels is being awaited
inside createRoom/joinRoom which causes callers to fail if reconciliation
(network/transient errors) throws even though the room operation already
succeeded; change the flow so createRoom/joinRoom returns success immediately
after the server operation and run ensureRoomCallPowerLevels asynchronously
(fire-and-forget) with its own try/catch that logs errors (use processLogger or
existing logger) so reconciliation failures do not cause the caller to see a
room creation/join failure; locate the logic around ensureRoomCallPowerLevels
and the createRoom/joinRoom functions to implement async invocation and internal
error handling.

In `@packages/epics/src/common/human-right-panel.tsx`:
- Around line 211-223: The chat history session key is only room-scoped which
can leak messages across accounts; update key generation to include the current
user id so the storage is user-scoped. Locate usages around
CHAT_HISTORY_SESSION_PREFIX, readPersistedChatHistory and the writer counterpart
(e.g., wherever sessionStorage.setItem is used to persist chat history) and
change the key format to `${CHAT_HISTORY_SESSION_PREFIX}${userId}:${roomId}` (or
equivalent) and ensure all read and write calls (including the restore logic
that runs before fresh timeline data arrives) use the same user-scoped key so
cached rows are only returned for the active user.
- Around line 1330-1351: The effect that runs on membership denial currently
clears room, messages, reply/edit drafts, input and errors (joinedRef, roomId,
setMessages, setReplyDraft, setEditDraft, setInput, setError,
setCallFullViewOpen) but leaves draft attachments, pending-send row, and
composer/reaction/delete errors behind; update this cleanup to also reset
attachment drafts, any pending-send queue/row, and composer/reaction/delete
error state by calling the corresponding setters (e.g. setAttachmentDrafts,
clearPendingSends or setPendingSendRow, setComposerError, setReactionError,
setDeleteError or their actual names in the file) inside the same useEffect so
all UI/memory state is fully cleared on access loss.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0919aebc-a572-4503-8e9f-c9b98fc65d4b

📥 Commits

Reviewing files that changed from the base of the PR and between 13612d9 and 0f67db5.

📒 Files selected for processing (3)
  • packages/core/src/matrix/client/providers/matrix-provider.tsx
  • packages/epics/src/common/human-chat-panel/human-chat-panel-call-join-strip.tsx
  • packages/epics/src/common/human-right-panel.tsx

Comment thread packages/core/src/matrix/client/providers/matrix-provider.tsx
Comment thread packages/epics/src/common/human-right-panel.tsx
Comment thread packages/epics/src/common/human-right-panel.tsx
Multiline stdout from `vercel deploy` broke single-line GITHUB_OUTPUT writes,
leaving preview_url empty and causing `vercel alias set ""` to fail.

Extract the *.vercel.app URL explicitly; fail with logs if missing. Use the
globally installed Vercel CLI for alias to match deploy and avoid npx tar noise.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/deploy-preview.yml (1)

190-200: ⚠️ Potential issue | 🟠 Major

Pin the Vercel CLI to a specific version.

Line 129 installs Vercel with vercel@latest, which is not pinned. This means a future Vercel release can change the alias set command behavior or break compatibility without any workflow change, breaking reproducibility. Pin the installation to a specific version (e.g., vercel@33.0.0).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-preview.yml around lines 190 - 200, The workflow
currently installs the Vercel CLI with an unpinned tag (vercel@latest); update
the install step to pin a specific, tested version (e.g., vercel@33.0.0) so the
subsequent steps that call vercel alias set (and use envs VERCEL_TOKEN,
PREVIEW_ALIAS_SUFFIX, PREVIEW_URL) remain reproducible and won't break when the
CLI changes; locate the step that runs the Vercel installation and replace the
unpinned reference with the chosen exact version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In @.github/workflows/deploy-preview.yml:
- Around line 190-200: The workflow currently installs the Vercel CLI with an
unpinned tag (vercel@latest); update the install step to pin a specific, tested
version (e.g., vercel@33.0.0) so the subsequent steps that call vercel alias set
(and use envs VERCEL_TOKEN, PREVIEW_ALIAS_SUFFIX, PREVIEW_URL) remain
reproducible and won't break when the CLI changes; locate the step that runs the
Vercel installation and replace the unpinned reference with the chosen exact
version.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 65516d78-5b09-4b35-accc-3a7090124d92

📥 Commits

Reviewing files that changed from the base of the PR and between 0f67db5 and 0ab9371.

📒 Files selected for processing (1)
  • .github/workflows/deploy-preview.yml

@webguru-hypha webguru-hypha merged commit 452430f into main May 4, 2026
10 checks passed
@webguru-hypha webguru-hypha deleted the cursor/fix-human-chat-panel-e2e-navigation-5377 branch May 4, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants