Fix(widget) clicked links in widget open new tab#15676
Conversation
Add support for emitting and handling CHAT_LINK_CLICK messages so links inside the embedded widget can be opened in a new tab or navigated in-place. Introduce newTabLink toggles (example and generated embed) and wire host/embed message handling. Refactor embed code generation (generateEmbedCode) and update handleOpenInNewTab to open a blob-based HTML preview for widget embeds (uses noopener,noreferrer). Update floating markdown: add remark-gfm/remark-math, remove replaceRetrievingToSection from the pipeline, simplify paragraph renderer, and override anchor rendering to post CHAT_LINK_CLICK back to the parent. Small logging and cleanup adjustments included.
📝 WalkthroughWalkthroughThis PR implements a ChangesChat Link Click Protocol and Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
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 `@web/src/components/embed-dialog/index.tsx`:
- Line 253: Remove the debug console logging inside the message event handler
that currently calls console.log(e); locate the
window.addEventListener('message', ...) callback (or the message event handler
function) in the embed script and delete the console.log(e) call, or replace it
with a safe, environment-gated logger (e.g., only log in development via a debug
flag or use a secure logger method) so message payloads are not emitted to host
page browser consoles in production.
- Around line 266-269: In the postMessage handler's CHAT_LINK_CLICK branch (the
code that checks e.data.type==='CHAT_LINK_CLICK' and uses newTabLink), remove
the noisy console.log(e), validate and sanitize e.data.url by constructing a URL
and allowing only http: or https: protocols (reject javascript:, data:, about:
etc.), and then open it safely: if newTabLink call window.open(sanitizedUrl,
'_blank', 'noopener,noreferrer') and otherwise set window.location.href =
sanitizedUrl; if URL parsing/validation fails, do nothing or log a harmless
warning via process logger instead.
In `@web/src/components/floating-chat-widget-markdown.tsx`:
- Around line 315-327: The onClick handler for the custom anchor lets the
browser navigate before/while emitting CHAT_LINK_CLICK; update the handler in
floating-chat-widget-markdown.tsx (the onClick function that calls
onClick?.(event) and emitChatLinkClick(clickedUrl)) to call
event.preventDefault() when a clickedUrl is present (before emitChatLinkClick)
so the navigation is cancelled, then emit the event; keep calling the original
onClick?.(event) (either before or after preventDefault) to preserve existing
behavior.
🪄 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: cc0a3ee5-c8fd-43d7-b159-57b4c55b5cb8
📒 Files selected for processing (3)
example/chat_demo/widget_demo.htmlweb/src/components/embed-dialog/index.tsxweb/src/components/floating-chat-widget-markdown.tsx
| <script> | ||
| const newTabLink = true; | ||
| window.addEventListener('message',e=>{ | ||
| console.log(e) |
There was a problem hiding this comment.
Remove debug logging from the generated embed script.
Line 253 logs all message events and payloads in host pages; this can leak user/session data into browser logs and creates noise in production.
Suggested fix
- console.log(e)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| console.log(e) |
🤖 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 `@web/src/components/embed-dialog/index.tsx` at line 253, Remove the debug
console logging inside the message event handler that currently calls
console.log(e); locate the window.addEventListener('message', ...) callback (or
the message event handler function) in the embed script and delete the
console.log(e) call, or replace it with a safe, environment-gated logger (e.g.,
only log in development via a debug flag or use a secure logger method) so
message payloads are not emitted to host page browser consoles in production.
| else if(e.data.type==='CHAT_LINK_CLICK'&&e.data.url){ | ||
| if(newTabLink)window.open(e.data.url,'_blank'); | ||
| else window.location.href=e.data.url; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="web/src/components/embed-dialog/index.tsx"
# show line numbers around 240-330
python3 - <<'PY'
import itertools,sys
path="web/src/components/embed-dialog/index.tsx"
start=220; end=330
with open(path,'r',encoding='utf-8') as f:
for i,line in enumerate(f,1):
if start<=i<=end:
print(f"{i:4d}:{line.rstrip()}")
PY
# also search for CHAT_LINK_CLICK occurrences and any window.open with noopener usage
rg -n "CHAT_LINK_CLICK" web/src/components/embed-dialog/index.tsx web/src/components/embed-dialog -S || true
rg -n "window\.open\(|noopener|noreferrer" web/src/components/embed-dialog/index.tsx -S || true
# find where newTabLink is defined and whether e.data.url is derived/validated earlier in the postMessage pipeline
rg -n "newTabLink" web/src/components/embed-dialog/index.tsx -n -S || true
rg -n "postMessage|e\.data\.url|data\.url" web/src/components/embed-dialog/index.tsx -S || trueRepository: infiniflow/ragflow
Length of output: 4989
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="web/src/components/embed-dialog/index.tsx"
# show line numbers around 240-330
python3 - <<'PY'
import itertools,sys
path="web/src/components/embed-dialog/index.tsx"
start=220; end=330
with open(path,'r',encoding='utf-8') as f:
for i,line in enumerate(f,1):
if start<=i<=end:
print(f"{i:4d}:{line.rstrip()}")
PY
# also search for CHAT_LINK_CLICK occurrences and any window.open with noopener usage
rg -n "CHAT_LINK_CLICK" web/src/components/embed-dialog/index.tsx web/src/components/embed-dialog -S || true
rg -n "window\.open\(|noopener|noreferrer" web/src/components/embed-dialog/index.tsx -S || true
# find where newTabLink is defined and whether e.data.url is derived/validated earlier in the postMessage pipeline
rg -n "newTabLink" web/src/components/embed-dialog/index.tsx -n -S || true
rg -n "postMessage|e\.data\.url|data\.url" web/src/components/embed-dialog/index.tsx -S || trueRepository: infiniflow/ragflow
Length of output: 4989
Sanitize e.data.url in generated CHAT_LINK_CLICK handler and add noopener,noreferrer
web/src/components/embed-dialog/index.tsxgenerates a widgetpostMessagehandler whereCHAT_LINK_CLICKdirectly usese.data.urlinwindow.open(e.data.url,'_blank')(withnewTabLink = true) and does not validate the URL protocol/format; also missingnoopener,noreferrer, enabling tabnabbing and potentialjavascript:/data:navigation injection.console.log(e)in the message handler adds noisy debug output for embedded widgets.
Suggested fix
- else if(e.data.type==='CHAT_LINK_CLICK'&&e.data.url){
- if(newTabLink)window.open(e.data.url,'_blank');
- else window.location.href=e.data.url;
- }
+ else if(e.data.type==='CHAT_LINK_CLICK'&&e.data.url){
+ let parsed;
+ try { parsed = new URL(e.data.url, window.location.href); } catch { return; }
+ if (!['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol)) return;
+ const safeUrl = parsed.toString();
+ if (newTabLink) window.open(safeUrl, '_blank', 'noopener,noreferrer');
+ else window.location.assign(safeUrl);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| else if(e.data.type==='CHAT_LINK_CLICK'&&e.data.url){ | |
| if(newTabLink)window.open(e.data.url,'_blank'); | |
| else window.location.href=e.data.url; | |
| } | |
| else if(e.data.type==='CHAT_LINK_CLICK'&&e.data.url){ | |
| let parsed; | |
| try { parsed = new URL(e.data.url, window.location.href); } catch { return; } | |
| if (!['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol)) return; | |
| const safeUrl = parsed.toString(); | |
| if (newTabLink) window.open(safeUrl, '_blank', 'noopener,noreferrer'); | |
| else window.location.assign(safeUrl); | |
| } |
🤖 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 `@web/src/components/embed-dialog/index.tsx` around lines 266 - 269, In the
postMessage handler's CHAT_LINK_CLICK branch (the code that checks
e.data.type==='CHAT_LINK_CLICK' and uses newTabLink), remove the noisy
console.log(e), validate and sanitize e.data.url by constructing a URL and
allowing only http: or https: protocols (reject javascript:, data:, about:
etc.), and then open it safely: if newTabLink call window.open(sanitizedUrl,
'_blank', 'noopener,noreferrer') and otherwise set window.location.href =
sanitizedUrl; if URL parsing/validation fails, do nothing or log a harmless
warning via process logger instead.
| onClick={(event) => { | ||
| onClick?.(event); | ||
|
|
||
| const clickedUrl = | ||
| event.currentTarget.href || | ||
| href || | ||
| event.currentTarget.getAttribute('href') || | ||
| ''; | ||
|
|
||
| if (clickedUrl) { | ||
| emitChatLinkClick(clickedUrl); | ||
| } | ||
| }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web/src/components/floating-chat-widget-markdown.tsx"
# Show the relevant section with line numbers
nl -ba "$FILE" | sed -n '280,360p'
# Find where emitChatLinkClick is defined/used
rg -n "emitChatLinkClick" "$FILE"
# Check for any existing navigation-prevention patterns in the file
rg -n "preventDefault|stopPropagation" "$FILE"
# Check how anchors are rendered/processed (look for custom link component / preprocessing)
rg -n "anchor|link|remark|rehype|remarkGfm|remarkMath|onClick" "$FILE"Repository: infiniflow/ragflow
Length of output: 105
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web/src/components/floating-chat-widget-markdown.tsx"
# Basic sanity checks
test -f "$FILE"
echo "== file =="
wc -l "$FILE"
echo
echo "== excerpt around 295-345 (best-effort) =="
cat -n "$FILE" | sed -n '295,345p'
echo
echo "== emitChatLinkClick occurrences =="
rg -n "emitChatLinkClick" "$FILE" || true
echo
echo "== preventDefault/stopPropagation occurrences in file =="
rg -n "preventDefault|stopPropagation" "$FILE" || true
echo
echo "== onClick handler locations =="
rg -n "onClick=\{\(" "$FILE" || true
rg -n "onClick=\{\s*\(" "$FILE" || true
echoRepository: infiniflow/ragflow
Length of output: 2657
Prevent default anchor navigation when emitting CHAT_LINK_CLICK (web/src/components/floating-chat-widget-markdown.tsx:315-327)
The custom a renderer calls emitChatLinkClick(clickedUrl) but never cancels the anchor’s default behavior, so clicks will still follow the link in addition to emitting the event.
Suggested fix
onClick={(event) => {
onClick?.(event);
+ if (event.defaultPrevented) return;
+ event.preventDefault();
const clickedUrl =
event.currentTarget.href ||
href ||
event.currentTarget.getAttribute('href') ||
'';
if (clickedUrl) {
emitChatLinkClick(clickedUrl);
}
}}🤖 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 `@web/src/components/floating-chat-widget-markdown.tsx` around lines 315 - 327,
The onClick handler for the custom anchor lets the browser navigate before/while
emitting CHAT_LINK_CLICK; update the handler in
floating-chat-widget-markdown.tsx (the onClick function that calls
onClick?.(event) and emitChatLinkClick(clickedUrl)) to call
event.preventDefault() when a clickedUrl is present (before emitChatLinkClick)
so the navigation is cancelled, then emit the event; keep calling the original
onClick?.(event) (either before or after preventDefault) to preserve existing
behavior.
What problem does this PR solve?
Ensure links clicked in the widget open in a new tab
Type of change