Skip to content

Open doc links in a new tab instead of navigating the sandboxed frame - #19

Merged
rgarcia merged 8 commits into
mainfrom
hypeship/open-doc-links-in-new-tab
Jul 28, 2026
Merged

Open doc links in a new tab instead of navigating the sandboxed frame#19
rgarcia merged 8 commits into
mainfrom
hypeship/open-doc-links-in-new-tab

Conversation

@rgarcia

@rgarcia rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Problem

Clicking any external link inside a published document replaces the document with the browser's "refused to connect" error page.

The viewer serves user HTML from /d/:slug/raw into a sandboxed iframe (sandbox="allow-scripts allow-downloads", plus the matching CSP sandbox header). A plain <a href="https://github.com/..."> therefore navigates the frame, and any site sending X-Frame-Options: deny / frame-ancestors 'none' — github.com, and most of the web — refuses to render there. target="_blank" doesn't help either: without allow-popups the sandbox blocks the popup, so those links do nothing at all.

This has been true since the viewer shipped (8c9b564, 2026-06-12): the sandbox has never carried allow-popups, nothing ever rewrote link targets, and the overlay had no link handling.

Fix

  • lib/docs/overlay.ts: intercept click / middle-click on any a[href] that resolves to a cross-document http(s) URL, and open it in a new tab from inside the user gesture. Same-document fragments (#section, the gutter section links) still navigate in-frame, non-http(s) schemes are left to the browser, and edit mode still treats links as text.
  • sandbox / CSP sandbox: add allow-popups allow-popups-to-escape-sandbox so that new tab is permitted and loads the linked site as a normal page rather than inheriting the opaque origin.

The document still can't reach our origin, cookies, or storage — it gains only the ability to open a tab, which is strictly less than the in-frame navigation it already had.

Verification

  • Reproduced before/after in a local chromium harness that mirrors the shell (same sandbox flags, same CSP header, an X-Frame-Options: deny target):
    • before — frame lands on chrome-error://chromewebdata/, no tab opens
    • after — frame stays on the doc, the target loads in a new tab (real origin, cookies work, window.opener null); middle-click behaves the same; #section links stay in-frame and open no tab
  • npm test (162 tests) and tsc --noEmit pass.
  • No unit test added: the overlay is a stringified script that runs inside the sandbox and can't import repo code, so there's no existing harness to hook into.

Note

Medium Risk
Relaxes the viewer sandbox with allow-popups, but only for user-gesture tab opens; opaque-origin isolation for cookies/storage is unchanged.

Overview
Fixes external links in published docs replacing the viewer with “refused to connect” when the target blocks framing (e.g. GitHub). The sandboxed iframe and /d/:slug/raw CSP now include allow-popups and allow-popups-to-escape-sandbox, and the injected overlay intercepts http(s) link clicks (and middle-click) to window.open(..., "_blank", "noopener") instead of navigating the frame.

Same-document #fragment links still navigate in-frame. The interceptor skips edit mode, overlay chrome (reaction chips, section link icons), and clicks that end with a non-empty text selection (comment/react). Highlight segment handlers ignore clicks the interceptor treated as navigation so links inside annotated spans still work.

Reviewed by Cursor Bugbot for commit 99c12f4. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
justhtml Ready Ready Preview, Comment Jul 28, 2026 9:10pm

@rgarcia
rgarcia marked this pull request as ready for review July 28, 2026 19:18

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Highlighted links still navigate frame
    • Changed the document click link interceptor to capture phase so external links inside highlight segments are prevented before segment click handlers stop propagation.

Create PR

Or push these changes by commenting:

@cursor push 042165efd6
Preview (042165efd6)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -737,7 +737,7 @@
     ev.preventDefault();
     window.open(href, "_blank", "noopener");
   }
-  document.addEventListener("click", openLink);
+  document.addEventListener("click", openLink, true);
   document.addEventListener("auxclick", openLink);
 
   // click-elsewhere (on non-highlight) clears focus + selection popovers

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts Outdated
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Confirmed and fixed in 44cca03 — good catch. A link inside a painted highlight segment did still navigate the frame: the segment's own click handler stopPropagation()s, so the bubble-phase interceptor never ran. Both listeners are now capture-phase (registered before the edit-mode capture handler, and still gated on editing).

Re-verified in the local harness with a link wrapped in a stopPropagation() span:

click before (bubble) after (capture)
plain external link new tab new tab
link inside a highlight chrome-error:// in-frame new tab
middle-click inside a highlight chrome-error:// in-frame new tab
#section in-frame, no tab in-frame, no tab
mailto: untouched untouched

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Chip clicks open enclosing links
    • The capture-phase link interceptor now ignores overlay chrome targets, so chip clicks only toggle reactions and no longer open enclosing anchors.

Create PR

Or push these changes by commenting:

@cursor push 467b38f9d2
Preview (467b38f9d2)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -731,6 +731,7 @@
   // by the segment's own click handler (it stopPropagation()s) and navigate.
   function openLink(ev){
     if (editing) return;
+    if (fromOverlayChrome(ev)) return;
     if (ev.type === "auxclick" && ev.button !== 1) return; // middle-click only
     var t = ev.target;
     var a = t && t.closest && t.closest("a[href]");

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Link clicks still focus highlights
    • Added stopPropagation() in openLink so external-link clicks no longer bubble to highlight segment click handlers.

Create PR

Or push these changes by commenting:

@cursor push fe0cbd0437
Preview (fe0cbd0437)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -737,6 +737,7 @@
     var href = a && externalHref(a);
     if (!href) return;
     ev.preventDefault();
+    ev.stopPropagation();
     window.open(href, "_blank", "noopener");
   }
   document.addEventListener("click", openLink, true);

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Both fixed in 4f00dcd.

Chip clicks open enclosing links (medium) — real, and worse than described: the interceptor ran first in capture, so a chip that lands inside an anchor lost its reaction toggle and opened a tab. openLink now bails on [data-jh-chip] / .jh-pop targets, and preventDefaults if that chrome sits inside an anchor so the frame doesn't navigate either (the chip handler stops propagation but never prevented the default action — that part predates this PR).

Link clicks still focus highlights (low)stopPropagation() + hidePop() on the intercepted click, so following a link no longer focuses the rail or opens the anchor picker.

Re-verified with a real chip: the harness shell posts jh:reactions for a span that ends inside an anchor, so the overlay paints a genuine chip inside the <a>.

click without the guard with it
reaction chip inside a link new tab opens, jh:reactionToggle never sent no tab, reaction toggles, frame stays
link inside a highlight new tab, no segment focus
#section / mailto: unchanged, in-frame

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Selection click opens external tab
    • Added a non-collapsed selection guard in openLink that suppresses link navigation without opening a new tab when drag-selection ends on linked text.

Create PR

Or push these changes by commenting:

@cursor push d2aad954ab
Preview (d2aad954ab)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -727,6 +727,14 @@
     if (u.href.split("#")[0] === location.href.split("#")[0]) return null;
     return u.href;
   }
+  function hasActiveSelection(){
+    try {
+      var sel = window.getSelection();
+      return !!(sel && sel.rangeCount && !sel.isCollapsed && sel.toString().trim());
+    } catch (e) {
+      return false;
+    }
+  }
   // Capture phase: a link inside a highlighted span would otherwise be swallowed
   // by the segment's own click handler (it stopPropagation()s) and navigate.
   function openLink(ev){
@@ -746,6 +754,13 @@
     var a = t.closest("a[href]");
     var href = a && externalHref(a);
     if (!href) return;
+    if (hasActiveSelection()){
+      // A drag-selection ending on linked text still emits click; keep the frame
+      // from navigating, but do not treat selection as link-follow.
+      ev.preventDefault();
+      ev.stopPropagation();
+      return;
+    }
     // Following a link is navigation, not an annotation click: keep the segment
     // handler from also focusing the rail / opening the anchor picker.
     ev.preventDefault();

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 9437ed8.

Selection click opens external tab (medium)openLink now bails when a non-empty selection is live at click time, using the same emptiness test as reportSelection (rangeCount && !isCollapsed && toString().trim()). It still preventDefaults on that path, so the click neither navigates the frame nor opens a tab and the selection survives for the comment/react toolbar.

Also swapped the chip/popover check for the existing fromOverlayChrome(ev) helper rather than repeating its selectors.

Verified in the harness with a live selection inside the anchor at click time:

state at click frame tabs selection
selection inside the link stays on the doc 0 preserved
no selection stays on the doc 1 (target site)

(Chrome starts a link drag rather than a selection when the drag begins inside linked text, so that path never fires a click at all — the guard covers the shift-click / drag-into-link cases where a selection does survive.)

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Edit mode middle-click opens links
    • When edit mode is active, the shared link handler now prevents default navigation for anchor targets before returning, which blocks auxclick middle-click tab opens as intended.

Create PR

Or push these changes by commenting:

@cursor push 28093e92bd
Preview (28093e92bd)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -730,7 +730,11 @@
   // Capture phase: a link inside a highlighted span would otherwise be swallowed
   // by the segment's own click handler (it stopPropagation()s) and navigate.
   function openLink(ev){
-    if (editing) return;
+    if (editing){
+      var et = ev.target;
+      if (et && et.closest && et.closest("a[href]")) ev.preventDefault();
+      return;
+    }
     if (ev.type === "auxclick" && ev.button !== 1) return; // middle-click only
     var t = ev.target;
     if (!t || !t.closest) return;

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in c4f7c59.

Edit mode middle-click opens links (medium) — reproduced: with edit mode on and no block yet focused, a middle-click on a link opened a tab (once a block is contenteditable the browser stops following links, which is why it only shows up on the first gesture). openLink now resolves the anchor first and preventDefaults while editing, covering both click and auxclick.

gesture (edit mode on) before after
middle-click as the first gesture tab opens no tab, no navigation
left-click no tab no tab
left-click after leaving edit mode tab opens tab opens

The reordering also dropped the duplicated closest("a[href]") calls — the handler now bails immediately when the click isn't in an anchor.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Selection click still focuses highlights
    • The live-selection guard in openLink now also stops propagation so drag-end clicks on linked highlights no longer bubble into segment focus handlers.

Create PR

Or push these changes by commenting:

@cursor push 8a85ae5c60
Preview (8a85ae5c60)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -752,6 +752,7 @@ export const OVERLAY_SCRIPT = String.raw`
     var sel = window.getSelection();
     if (sel && sel.rangeCount && !sel.isCollapsed && sel.toString().trim()){
       ev.preventDefault();
+      ev.stopPropagation();
       return;
     }
     // Following a link is navigation, not an annotation click: keep the segment

@@ -752,6 +752,7 @@ export const OVERLAY_SCRIPT = String.raw`
     var sel = window.getSelection();
     if (sel && sel.rangeCount && !sel.isCollapsed && sel.toString().trim()){
       ev.preventDefault();
+      ev.stopPropagation();
       return;
     }
     // Following a link is navigation, not an annotation click: keep the segment

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts Outdated
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 3304cb0.

Selection click still focuses highlights (medium) — right, the guard returned before stopPropagation. preventDefault + stopPropagation + hidePop now happen as soon as the click is known to be on an external link; the selection check only decides whether a tab opens. One shape for both paths instead of two.

Verified on a reacted (highlighted) span inside an anchor, with a live selection at click time:

shell messages tabs selection
before jh:focus, jh:selection 0 kept
after jh:selection 0 kept

So the comment/react toolbar still comes up and the rail no longer jumps.

@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Stale focus after link click
    • External link handling now clears existing annotation focus immediately after suppressing frame navigation, so dimming and rail pin state do not persist.
  • ✅ Fixed: Hash links ignore base URL
    • Fragment hrefs are now resolved against document.baseURI before same-document checks, so base-retargeted hashes are treated as external navigation and opened out of frame.

Create PR

Or push these changes by commenting:

@cursor push 64d4bc121e
Preview (64d4bc121e)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -720,9 +720,9 @@
   // Same-document fragments (#section, the gutter section links) stay in-frame.
   function externalHref(a){
     var raw = a.getAttribute("href");
-    if (!raw || raw.charAt(0) === "#") return null;
+    if (!raw) return null;
     var u;
-    try { u = new URL(a.href, document.baseURI); } catch(e){ return null; }
+    try { u = new URL(raw, document.baseURI); } catch(e){ return null; }
     if (u.protocol !== "http:" && u.protocol !== "https:") return null;
     if (u.href.split("#")[0] === location.href.split("#")[0]) return null;
     return u.href;
@@ -751,6 +751,7 @@
     ev.preventDefault();
     ev.stopPropagation();
     hidePop();
+    if (focusKey) clearFocus();
     // …but a selection that survives the click is a comment/react gesture (a drag
     // that ended in linked text), so keep the selection and open nothing. Same
     // emptiness test as reportSelection.

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts
Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Both fixed in afc6ffe.

Stale focus after link click (medium)stopPropagation did strand the pinned focus. The interceptor now applies the click-elsewhere semantics itself: hidePop(), plus clearFocus() when the click isn't on a highlight segment (matching the handler it shadows). Verified by focusing an anchored reaction, then clicking an unrelated link: before → 1 focused / 1 dimmed and no jh:focus sent; after → jh:focus with key:null and 0 focused / 0 dimmed.

Hash links ignore base URL (low) — dropped the href[0] === '#' fast path; externalHref resolves everything through new URL(a.href, document.baseURI) and decides same-document by comparing the hash-stripped URLs. With <base href="http://…/elsewhere/"> in the doc, #s1 used to navigate the frame to /elsewhere/#s1; it now opens a tab, and without a <base> fragments still scroll in-frame as before.

That change made the overlay's own gutter section links resolve externally too, so they're now treated as our chrome alongside chips and popovers — verified that a gutter click still sends jh:copyLink and opens nothing, even with the external <base>.

Full sweep on the final code (no-base doc): plain link → tab; link inside a highlight → tab, no rail focus; chip inside a link → reaction toggles, no tab; live selection → nothing opens, selection kept; edit mode left/middle click → nothing opens; #section and mailto: → unchanged.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Interceptor blocks author link handlers
    • I removed capture-phase propagation blocking for intercepted links and made segment click handlers skip default-prevented events so author link handlers still execute.
  • ✅ Fixed: SVG links resolve to garbage URLs
    • I now read link destinations from the element’s href attribute/baseVal and resolve them against baseURI before URL parsing, which correctly handles SVG anchors.

Create PR

Or push these changes by commenting:

@cursor push bc5cf8fcd0
Preview (bc5cf8fcd0)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -629,6 +629,7 @@
     span.addEventListener("mouseleave", function(){ setHover(null); send({type:"jh:hlHoverOut"}); });
     span.addEventListener("click", function(ev){
       ev.stopPropagation();
+      if (ev.defaultPrevented) return;
       onSegClick(seg, ev);
     });
   }
@@ -718,9 +719,22 @@
   // ourselves from inside the user gesture (sandbox carries allow-popups +
   // allow-popups-to-escape-sandbox so the target loads as a normal page).
   // Same-document fragments (#section, the gutter section links) stay in-frame.
+  function linkHrefValue(a){
+    if (!a) return null;
+    var href = null;
+    try { href = a.getAttribute ? a.getAttribute("href") : null; } catch(e){}
+    if (!href){
+      var ah = a.href;
+      if (ah && typeof ah.baseVal === "string") href = ah.baseVal;
+      else if (typeof ah === "string") href = ah;
+    }
+    return href || null;
+  }
   function externalHref(a){
+    var href = linkHrefValue(a);
+    if (!href) return null;
     var u;
-    try { u = new URL(a.href, document.baseURI); } catch(e){ return null; }
+    try { u = new URL(href, a.baseURI || document.baseURI); } catch(e){ return null; }
     if (u.protocol !== "http:" && u.protocol !== "https:") return null;
     if (u.href.split("#")[0] === location.href.split("#")[0]) return null;
     return u.href;
@@ -747,9 +761,9 @@
     // A click on a link is navigation, not an annotation click: the frame must not
     // move, and the segment handler must not focus the rail / open the anchor picker.
     ev.preventDefault();
-    ev.stopPropagation();
-    // Click-elsewhere semantics, which stopPropagation keeps the handler below from
-    // applying: a click off the annotation UI drops the popover and pinned focus.
+    // Keep propagation alive so author click handlers on the link still run.
+    // Segment handlers ignore defaultPrevented events, so they won't treat this as
+    // an annotation click.
     hidePop();
     if (focusKey && !t.closest("[data-jh-seg]")) clearFocus();
     // …but a selection that survives the click is a comment/react gesture (a drag

You can send follow-ups to the cloud agent here.

Comment thread lib/docs/overlay.ts Outdated
Comment thread lib/docs/overlay.ts
@rgarcia

rgarcia commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Both fixed in 99c12f4.

Interceptor blocks author link handlers (medium) — confirmed: an onclick on an external link never fired. The interceptor no longer calls stopPropagation at all; it records the claimed event (linkClick = ev) and the segment click handler skips onSegClick for that event. Author handlers run again, the segment still doesn't steal the click, and pinned focus is cleared by the existing click-elsewhere handler instead of a copy of its logic (so those two lines are gone).

author onclick runs tab
with stopPropagation 0 opens
now 1 opens

SVG links resolve to garbage URLs (medium)externalHref now reads the href attribute (falling back to xlink:href) and resolves it against document.baseURI, and the interceptor matches closest("a") so xlink-only SVG links are covered too.

SVG link before after
<a href> in <svg> opened /[object%20SVGAnimatedString] opens the real target
<a xlink:href> in <svg> navigated the frame (chrome-error://) opens the real target

Re-ran the whole sweep on the final code: plain link, link inside a highlight, chip inside a link, live selection, edit-mode left/middle click, gutter section link, #section, mailto:, and focus pin/clear all behave as described above.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale focus after in-highlight link
    • I moved the segment click stopPropagation() to run only for non-link-claimed clicks so external-link clicks inside highlights now bubble to the document click-elsewhere handler and clear stale focus/popovers.

Create PR

Or push these changes by commenting:

@cursor push 53907ec9be
Preview (53907ec9be)
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -628,9 +628,9 @@
     });
     span.addEventListener("mouseleave", function(){ setHover(null); send({type:"jh:hlHoverOut"}); });
     span.addEventListener("click", function(ev){
-      ev.stopPropagation();
       // A click the link interceptor claimed is navigation, not an annotation click.
       if (ev === linkClick) return;
+      ev.stopPropagation();
       onSegClick(seg, ev);
     });
   }

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 99c12f4. Configure here.

Comment thread lib/docs/overlay.ts
var sel = window.getSelection();
if (sel && sel.rangeCount && !sel.isCollapsed && sel.toString().trim()) return;
window.open(href, "_blank", "noopener");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale focus after in-highlight link

Medium Severity

When an external link sits inside a highlight segment, openLink opens the tab and sets linkClick, but the segment handler still stopPropagation()s before bailing. That blocks the document click-elsewhere listener, so pinned focus/dimming and any open pick popover remain after the navigation gesture—neither onSegClick nor clearFocus/hidePop runs.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 99c12f4. Configure here.

@rgarcia
rgarcia merged commit a13a319 into main Jul 28, 2026
6 checks passed
@rgarcia
rgarcia deleted the hypeship/open-doc-links-in-new-tab branch July 28, 2026 21:22
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.

1 participant