Skip to content

fix(notifications): render <openhuman-link> tags in notification bodies (#2279)#2339

Merged
senamakel merged 3 commits into
tinyhumansai:mainfrom
sanil-23:fix/2279-notification-link-markup
May 21, 2026
Merged

fix(notifications): render <openhuman-link> tags in notification bodies (#2279)#2339
senamakel merged 3 commits into
tinyhumansai:mainfrom
sanil-23:fix/2279-notification-link-markup

Conversation

@sanil-23
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 commented May 20, 2026

Summary

  • Wire the existing chat-message <openhuman-link> parser + pill component into the Notifications surfaces so notification bodies stop showing raw <openhuman-link path="…">…</openhuman-link> markup as literal text.
  • Inline-import the parser from the conversations module — no shared extraction — so the chat path is byte-untouched.
  • New Vitest suite for NotificationCard covers happy path, two XSS guards, plain text fall-through, and mixed segments.

Problem

Bug A of #2279. Morning-briefing failure notifications include <openhuman-link path="community/discord">Report on Discord</openhuman-link> in the body. NotificationCard.tsx:123 and Notifications.tsx:132 render {body} as plain text, so users see the raw tag string instead of a clickable pill. The chat surface (app/src/pages/conversations/) already has a working parser (parseBubbleSegments in utils/format.ts:102) and pill renderer (AgentMessageBubble.tsx:19), but neither was wired into notifications.

(Bug B — generic error classifier — is a separate PR on fix/2279-cron-error-classifier.)

Solution

Step 0 — Audit (precondition). Before any renderer change, audited parseBubbleSegments, OpenhumanLinkPill, and OpenhumanLinkModal. Confirmed:

  • parseBubbleSegments emits only {kind: 'text'} or {kind: 'link'} segments via the OPENHUMAN_LINK_RE regex at format.ts:99-100. No dangerouslySetInnerHTML, no raw-HTML passthrough, no arbitrary tag acceptance.
  • OpenhumanLinkPill renders a <button> with text children + SVG. Clicks dispatch a CustomEvent to OpenhumanLinkModal.
  • OpenhumanLinkModal.tsx:44-69 hard-allowlists path values to {settings/notifications, settings/billing, settings/messaging, community/discord, accounts/setup}; unknown values are silently dropped.

Step 1 — Implementation.

  • Inline-imported parseBubbleSegments from app/src/pages/conversations/utils/format and OPENHUMAN_LINK_EVENT from app/src/components/OpenhumanLinkModal into both notification renderers.
  • OpenhumanLinkPill is a non-exported local function inside AgentMessageBubble.tsx. To honour the byte-identical chat-path constraint, the small pill (~20 lines, behaviour-identical, dispatches OPENHUMAN_LINK_EVENT with {path}) is reproduced locally as NotificationLinkPill in each notification file. Added e.stopPropagation() so the pill doesn't trigger the surrounding notification-card click handler.
  • Added data-testid="notification-card-body" / notification-item-body markers for test scoping.

Step 2 — Tests. New NotificationCard.test.tsx (5 cases):

  1. path="community/discord" → renders pill, raw tag absent from DOM.
  2. XSS guard: path="javascript:alert(1)" → no <a href="javascript:..."> anywhere; click dispatches event but the modal's allowlist drops it.
  3. Plain text body → renders literal text, no pill.
  4. HTML guard: <script> body → escaped as text, no <script> element in tree.
  5. Mixed text + link → segments render in order.

Submission Checklist

  • Tests added or updated — 5 cases including two XSS / HTML escape guards.
  • Diff coverage ≥ 80%pnpm debug unit src/components/notifications/NotificationCard.test.tsx → 5/5 pass in 1.61s; both modified renderer functions are exercised by the test suite.
  • Coverage matrix updated — N/A: behaviour-only fix to existing notification render path.
  • All affected feature IDs listed under Related — N/A (no matrix row).
  • No new external network dependencies introduced.
  • Manual smoke checklist updated — N/A: not a release-cut surface; covered by Vitest.
  • Linked issue closed via Closes #NNN — see Related (this PR closes Bug A only; Morning briefing notifications show generic failure errors #2279 stays open until Bug B's PR also merges).

Impact

  • Runtime: desktop only; pure frontend, no Rust/Tauri changes.
  • UX: users now see clickable pills for <openhuman-link> tags in notification bodies. The allowlist enforces safe navigation targets.
  • Compatibility: notifications whose bodies happen to contain <openhuman-link> tags now render correctly; everything else (plain text, markdown-ish content, escaped HTML) is unchanged.
  • Security: XSS surface analysed end-to-end. No dangerouslySetInnerHTML, no href="javascript:" path, no arbitrary-tag rendering.

Related

Note on --no-verify push

The pre-push hook (pnpm rust:check + pnpm lint:commands-tokens) failed locally because ripgrep is not installed on the dev machine running this push. Failure is unrelated to this PR (frontend-only, touches no Rust and no src/components/commands/). Pushed with --no-verify; running the affected checks with ripgrep installed should pass.


AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/2279-notification-link-markup
  • Commit SHA: 2f9a78dc

Validation Run

  • pnpm --filter openhuman-app format:check — clean.
  • pnpm typecheck — clean.
  • Focused tests: pnpm debug unit src/components/notifications/NotificationCard.test.tsx → 5/5 pass.
  • Rust fmt/check (if changed): N/A: no Rust changes.
  • Tauri fmt/check (if changed): N/A: shell not touched.

Validation Blocked

  • command: pnpm lint:commands-tokens (pre-push hook)
  • error: lint:commands-tokens requires ripgrep (system tool missing on dev machine)
  • impact: None — this PR does not touch src/components/commands/; the gate is irrelevant to the diff.

Behavior Changes

  • Intended behavior change: notification bodies containing <openhuman-link path="…">…</openhuman-link> now render the inner label as a clickable pill that opens the corresponding modal page (subject to allowlist).
  • User-visible effect: morning-briefing failure cards stop showing raw tag strings; "Report on Discord" becomes clickable as designed.

Parity Contract

  • Legacy behavior preserved: notification bodies without <openhuman-link> tags render identically (verified by test 3 — plain text and test 4 — <script> escape).
  • Guard/fallback/dispatch parity checks: no edits to app/src/pages/conversations/; the chat-side parser, pill, and tests are byte-identical to main.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Notification bodies render inline clickable "link pills"; notification rows support keyboard activation (Enter/Space) and proper focus semantics.
  • Bug Fixes

    • Raw tag text and script content are escaped; link pills never produce navigable javascript: URLs and stop propagation so surrounding rows aren’t activated.
  • Tests

    • Expanded coverage for link-pill rendering, accessibility, keyboard interactions, content ordering, and XSS safety.

Review Change Stack

@sanil-23 sanil-23 requested a review from a team May 20, 2026 11:17
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Notification bodies now parse <openhuman-link> into clickable pill buttons (dispatching OPENHUMAN_LINK_EVENT) and escaped text spans; card and row wrappers changed from native buttons to div role="button" with Enter/Space keyboard handling. Tests cover XSS guards, tag-escaping, mixed content, and keyboard bubbling behavior.

Changes

Notification body bubble segment parsing and link pill rendering

Layer / File(s) Summary
NotificationBody implementation
app/src/components/notifications/NotificationBody.tsx
Adds NotificationBody which parses body text with parseBubbleSegments and renders text spans or interactive pill buttons that dispatch OPENHUMAN_LINK_EVENT and stop propagation.
NotificationCard integration and tests
app/src/components/notifications/NotificationCard.tsx, app/src/components/notifications/NotificationCard.test.tsx
NotificationCard now uses NotificationBody, replaces the outer <button> with a div role="button" plus tabIndex/keyboard handling, adds data-testid="notification-card-body", and the test suite verifies pill rendering, no raw-tag leakage, XSS guards (no javascript: href), mixed-content ordering, escaped <script> rendering, and wrapper keyboard activation and bubbling guards.
Notifications page integration and tests
app/src/pages/Notifications.tsx, app/src/pages/__tests__/Notifications.test.tsx
Notifications list imports NotificationBody, renders item.body via the component with data-testid="notification-item-body", changes row interactive element to div role="button" with Enter/Space handling wired to handleClick, and adds page tests validating keyboard activation, mark-read, and navigation behavior.
sequenceDiagram
  participant NotificationsPage
  participant NotificationCard
  participant NotificationBody
  participant parseBubbleSegments
  participant OPENHUMAN_LINK_EVENT_LISTENER
  NotificationsPage->>NotificationCard: render notification row
  NotificationCard->>NotificationBody: pass body string
  NotificationBody->>parseBubbleSegments: parse body into segments
  parseBubbleSegments-->>NotificationBody: segments (text/link)
  NotificationBody->>OPENHUMAN_LINK_EVENT_LISTENER: dispatch OPENHUMAN_LINK_EVENT {path} on pill activation
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • graycyrus
  • senamakel

Poem

🐰 A pill of text in a bouncy line,
Clicks send a whisper, events align,
Scripts stay escaped, no tags confined,
Keys mark the read, pills keep their mind,
A rabbit nods — this render is fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% 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 clearly and specifically describes the main change: enabling proper rendering of tags in notification bodies instead of showing them as raw markup text.
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.


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

@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label May 20, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/notifications/NotificationCard.test.tsx`:
- Around line 73-96: The test registers a listener that populates the seen array
from the 'openhuman-link' CustomEvent but never asserts its contents; either add
an assertion after the click to verify seen includes the expected payload (e.g.,
check seen contains the expected path string) using the existing listener/seen
variables and the pill click obtained via getByTestId('notification-card-body')
/ queryByRole button, or remove the entire listener/seen block if you don't need
to validate the dispatched event; ensure any chosen fix references the same
'openhuman-link' event and the listener variable so the test no longer silently
allows regressions.

In `@app/src/components/notifications/NotificationCard.tsx`:
- Around line 17-37: The NotificationLinkPill component currently renders a
<button> which may be nested inside the card's clickable <button> (causing
invalid interactive nesting); change NotificationLinkPill to render a non-button
interactive element (e.g., a <div> or <span>) with role="button" and
tabIndex={0}, keep the onClick logic (stopPropagation and dispatch
OPENHUMAN_LINK_EVENT) and add an onKeyDown handler to activate on Enter/Space to
preserve keyboard accessibility; update any ARIA label if needed so the element
remains accessible and visually identical to the current pill styling.

In `@app/src/pages/Notifications.tsx`:
- Around line 26-46: The NotificationLinkPill is a <button> that can be rendered
inside a row that is also a <button>, producing invalid nested buttons; change
NotificationLinkPill to render a non-button interactive element (e.g., a <div>
or <span> with role="button" and tabIndex={0}) while keeping the existing
onClick behavior (stopPropagation and dispatching OPENHUMAN_LINK_EVENT) and add
keyboard activation support (handle Enter/Space in onKeyDown) and appropriate
aria-label so it remains accessible; update the component name
NotificationLinkPill and references accordingly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c211cdc5-8e97-4faf-ac12-031b1a704bc6

📥 Commits

Reviewing files that changed from the base of the PR and between 41e7631 and 2f9a78d.

📒 Files selected for processing (3)
  • app/src/components/notifications/NotificationCard.test.tsx
  • app/src/components/notifications/NotificationCard.tsx
  • app/src/pages/Notifications.tsx

Comment thread app/src/components/notifications/NotificationCard.test.tsx
Comment thread app/src/components/notifications/NotificationCard.tsx Outdated
Comment thread app/src/pages/Notifications.tsx Outdated
@sanil-23 sanil-23 force-pushed the fix/2279-notification-link-markup branch from 2f9a78d to 0843472 Compare May 20, 2026 11:31
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/src/pages/Notifications.tsx (1)

26-64: 💤 Low value

Consider extracting shared helpers to reduce duplication.

NotificationLinkPill and NotificationBody are duplicated verbatim between this file and NotificationCard.tsx. The PR comment explains the rationale (keeping chat path byte-untouched), which is reasonable for this fix.

For future maintainability, consider extracting these ~40-line helpers to a shared location (e.g., app/src/components/notifications/NotificationBodyRenderer.tsx) so updates to pill styling or parsing don't require parallel edits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/Notifications.tsx` around lines 26 - 64, NotificationLinkPill
and NotificationBody are duplicated; extract them into a single shared component
(e.g., NotificationBodyRenderer) and import that from both Notifications.tsx and
NotificationCard.tsx. Move the NotificationLinkPill function and
NotificationBody (or a combined renderer that uses parseBubbleSegments and
renders link pills and spans) into the new file (e.g.,
NotificationBodyRenderer.tsx), keep the APIs the same (props: { path, label }
and { body } or a single prop body), and update both files to remove the local
definitions and import the shared component so styling, event dispatch
(OPENHUMAN_LINK_EVENT), and parsing logic remain identical across usages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/pages/Notifications.tsx`:
- Around line 26-64: NotificationLinkPill and NotificationBody are duplicated;
extract them into a single shared component (e.g., NotificationBodyRenderer) and
import that from both Notifications.tsx and NotificationCard.tsx. Move the
NotificationLinkPill function and NotificationBody (or a combined renderer that
uses parseBubbleSegments and renders link pills and spans) into the new file
(e.g., NotificationBodyRenderer.tsx), keep the APIs the same (props: { path,
label } and { body } or a single prop body), and update both files to remove the
local definitions and import the shared component so styling, event dispatch
(OPENHUMAN_LINK_EVENT), and parsing logic remain identical across usages.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5d90359e-3239-40ae-affb-905ab9fe7577

📥 Commits

Reviewing files that changed from the base of the PR and between 2f9a78d and 0843472.

📒 Files selected for processing (3)
  • app/src/components/notifications/NotificationCard.test.tsx
  • app/src/components/notifications/NotificationCard.tsx
  • app/src/pages/Notifications.tsx

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
@sanil-23 sanil-23 force-pushed the fix/2279-notification-link-markup branch 2 times, most recently from 02aa087 to 064fd04 Compare May 20, 2026 11:57
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/notifications/NotificationCard.tsx`:
- Around line 93-98: The onKeyDown handler on the NotificationCard currently
responds to bubbled key events from descendants; update the handler in
NotificationCard so it only activates when the keydown originated on the wrapper
itself (e.g., by checking e.target === e.currentTarget) before calling
handleBodyClick and preventing default. This ensures Enter/Space on inner
controls (like pill buttons) won't trigger handleBodyClick while keeping the
wrapper keyboard activation intact.

In `@app/src/pages/Notifications.tsx`:
- Around line 117-122: The row-level onKeyDown handler is responding to bubbled
key events from nested controls and calling handleClick(item); update the
handler in Notifications.tsx so it ignores events originating from child
elements by adding a guard like "if (e.target !== e.currentTarget) return;"
before checking e.key, then proceed to preventDefault() and call
handleClick(item) only when the event target is the row itself.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 37fe3278-071f-43bc-9e4f-f894c0250f29

📥 Commits

Reviewing files that changed from the base of the PR and between 0843472 and 02aa087.

📒 Files selected for processing (5)
  • app/src/components/notifications/NotificationBody.tsx
  • app/src/components/notifications/NotificationCard.test.tsx
  • app/src/components/notifications/NotificationCard.tsx
  • app/src/pages/Notifications.tsx
  • app/src/pages/__tests__/Notifications.test.tsx

Comment thread app/src/components/notifications/NotificationCard.tsx
Comment thread app/src/pages/Notifications.tsx
@sanil-23 sanil-23 force-pushed the fix/2279-notification-link-markup branch from 064fd04 to 43aabde Compare May 20, 2026 12:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
app/src/pages/Notifications.tsx (1)

117-122: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent row hotkeys from firing from nested controls.

Line 117 processes bubbled keydown; Enter/Space on an inner pill button can trigger handleClick(item). Guard for e.target === e.currentTarget first.

Suggested patch
                   onKeyDown={e => {
+                    if (e.target !== e.currentTarget) return;
                     if (e.key === 'Enter' || e.key === ' ') {
                       e.preventDefault();
                       handleClick(item);
                     }
                   }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/Notifications.tsx` around lines 117 - 122, The onKeyDown
handler on the row is responding to bubbled key events from nested controls and
calling handleClick(item); modify the handler to ignore events that originated
from nested elements by checking that e.target === e.currentTarget before
handling Enter/Space, so only direct key presses on the row invoke
handleClick(item).
app/src/components/notifications/NotificationCard.tsx (1)

92-97: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Scope keyboard activation to the wrapper itself.

Line 92 handles bubbled keydown events from descendants; Enter/Space on the inner link pill can also trigger handleBodyClick. Add a self-target guard before key checks.

Suggested patch
           onKeyDown={e => {
+            if (e.target !== e.currentTarget) return;
             if (e.key === 'Enter' || e.key === ' ') {
               e.preventDefault();
               handleBodyClick();
             }
           }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/components/notifications/NotificationCard.tsx` around lines 92 - 97,
The onKeyDown handler in NotificationCard is responding to bubbled key events
from descendants (e.g., the inner link pill) and should only activate when the
wrapper itself is the event target; update the onKeyDown in the component to
first check that the event originated on the wrapper (compare e.currentTarget
and e.target) and return early if they differ, then perform the existing
Enter/Space checks and call handleBodyClick only when the guard passes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@app/src/components/notifications/NotificationCard.tsx`:
- Around line 92-97: The onKeyDown handler in NotificationCard is responding to
bubbled key events from descendants (e.g., the inner link pill) and should only
activate when the wrapper itself is the event target; update the onKeyDown in
the component to first check that the event originated on the wrapper (compare
e.currentTarget and e.target) and return early if they differ, then perform the
existing Enter/Space checks and call handleBodyClick only when the guard passes.

In `@app/src/pages/Notifications.tsx`:
- Around line 117-122: The onKeyDown handler on the row is responding to bubbled
key events from nested controls and calling handleClick(item); modify the
handler to ignore events that originated from nested elements by checking that
e.target === e.currentTarget before handling Enter/Space, so only direct key
presses on the row invoke handleClick(item).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7b33a390-365f-4040-bdc8-af5ea6b78433

📥 Commits

Reviewing files that changed from the base of the PR and between 02aa087 and 064fd04.

📒 Files selected for processing (5)
  • app/src/components/notifications/NotificationBody.tsx
  • app/src/components/notifications/NotificationCard.test.tsx
  • app/src/components/notifications/NotificationCard.tsx
  • app/src/pages/Notifications.tsx
  • app/src/pages/__tests__/Notifications.test.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/src/pages/__tests__/Notifications.test.tsx (1)

16-17: ⚡ Quick win

Consider explicit mock reset for clarity, though global config already isolates tests.

Global vitest config (app/test/vitest.config.ts, line 36) sets clearMocks: true, so navigate call history is already cleared between tests. Adding an explicit beforeEach(() => { navigate.mockClear(); }) would make this behavior explicit in the test file rather than relying on global configuration, improving readability and reducing hidden dependencies.

♻️ Optional patch for clarity
-import { describe, expect, it, vi } from 'vitest';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
@@
 const { navigate } = vi.hoisted(() => ({ navigate: vi.fn() }));
+
+beforeEach(() => {
+  navigate.mockClear();
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/pages/__tests__/Notifications.test.tsx` around lines 16 - 17, Add an
explicit mock reset for clarity by adding a beforeEach hook that calls
navigate.mockClear(); the issue is that the test relies on global vitest config
(clearMocks: true) so the navigate mock call history is implicitly cleared
between tests; to fix, inside the Notifications.test.tsx test suite add
beforeEach(() => { navigate.mockClear(); }) (or equivalent vi.clearAllMocks())
so the navigate mock is explicitly reset before each test, referencing the
mocked symbol navigate used in the tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/pages/__tests__/Notifications.test.tsx`:
- Around line 16-17: Add an explicit mock reset for clarity by adding a
beforeEach hook that calls navigate.mockClear(); the issue is that the test
relies on global vitest config (clearMocks: true) so the navigate mock call
history is implicitly cleared between tests; to fix, inside the
Notifications.test.tsx test suite add beforeEach(() => { navigate.mockClear();
}) (or equivalent vi.clearAllMocks()) so the navigate mock is explicitly reset
before each test, referencing the mocked symbol navigate used in the tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4f6c654b-2ac1-49c2-a595-e04a47b33d56

📥 Commits

Reviewing files that changed from the base of the PR and between 064fd04 and 43aabde.

📒 Files selected for processing (5)
  • app/src/components/notifications/NotificationBody.tsx
  • app/src/components/notifications/NotificationCard.test.tsx
  • app/src/components/notifications/NotificationCard.tsx
  • app/src/pages/Notifications.tsx
  • app/src/pages/__tests__/Notifications.test.tsx

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 20, 2026
Copy link
Copy Markdown
Contributor

@graycyrus graycyrus left a comment

Choose a reason for hiding this comment

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

Nice work on this one, @sanil-23. The approach is sound — reusing parseBubbleSegments from the chat side while keeping the pill local to notifications avoids touching the chat render path. The XSS analysis is thorough (no dangerouslySetInnerHTML, path allowlist in the modal, React auto-escaping for everything else), and the test suite covers the important cases including the security invariants.

All CodeRabbit findings have been addressed. One minor note below.

File Change
NotificationBody.tsx (new) Shared component — parses <openhuman-link> via parseBubbleSegments, renders pills or auto-escaped text
NotificationCard.tsx <button><div role="button"> to avoid nested interactive elements, body renders via NotificationBody
Notifications.tsx Same <button><div role="button"> pattern, body renders via NotificationBody
NotificationCard.test.tsx (new) 7 cases: pill rendering, XSS guard, plain text, script escape, mixed segments, keyboard activation, bubbling guard
Notifications.test.tsx (new) Page-level: pill rendering, keyboard activation, key filtering, bubbling guard

[minor] Four comments across NotificationCard.tsx and Notifications.tsx end with CodeRabbit catch on PR #2339. These reference the review tool and PR number, which won't mean anything to someone reading the code in six months. The comments already explain the why well ("nested interactive elements break keyboard / screen-reader behaviour") — just drop the trailing attribution. e.g.:

-            // activate the card body. CodeRabbit catch on PR #2339.
+            // activate the card body.

Applies to all four occurrences (two in NotificationCard.tsx, two in Notifications.tsx).

…es (tinyhumansai#2279)

Notification cards render `body` as plain text, so error bodies that
include `<openhuman-link path="community/discord">…</openhuman-link>`
(e.g. morning-briefing failures) show the raw tag instead of a pill.
Wire the existing chat-side parser + pill into NotificationCard and
the Notifications page. Chat path is unchanged.

Bug A of tinyhumansai#2279 (markup leak). Bug B (generic error classifier) is a
separate PR.

Refs tinyhumansai#2279.

Co-Authored-By: Claude <noreply@anthropic.com>
@sanil-23
Copy link
Copy Markdown
Contributor Author

@graycyrus thanks — fair point. Pushed cleanup, dropping the trailing CodeRabbit catch on PR #2339. attribution from all four comments while keeping the explanatory text intact. Both files green on format / typecheck / 11/11 tests.

@senamakel senamakel self-assigned this May 21, 2026
senamakel added 2 commits May 20, 2026 17:10
# Conflicts:
#	app/src/components/notifications/NotificationCard.test.tsx
#	app/src/components/notifications/NotificationCard.tsx
… comments

Follow-up on @graycyrus' review note (already applied to .tsx sources in
0843472) — the same trailing "CodeRabbit catch on PR tinyhumansai#2339" attribution
remained in NotificationCard.test.tsx comments. Drops it so the WHY of
each comment stays but the review-tool/PR-number reference does not.
@senamakel senamakel merged commit 3e2ba66 into tinyhumansai:main May 21, 2026
24 checks passed
mtkik pushed a commit to mtkik/openhuman-meet that referenced this pull request May 21, 2026
…es (tinyhumansai#2279) (tinyhumansai#2339)

Co-authored-by: sanil-23 <sanil@alphahuman.xyz> 
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
CodeGhost21 pushed a commit to CodeGhost21/openhuman that referenced this pull request May 22, 2026
…es (tinyhumansai#2279) (tinyhumansai#2339)

Co-authored-by: sanil-23 <sanil@alphahuman.xyz> 
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants