From b511f8472ae537af19064cbda35d0eb6213c1b45 Mon Sep 17 00:00:00 2001
From: rgarcia <72655+rgarcia@users.noreply.github.com>
Date: Thu, 25 Jun 2026 08:46:17 +0000
Subject: [PATCH] Fix mobile selection toolbar
---
app/d/[slug]/CommentsShell.tsx | 6 ++---
lib/docs/overlay.ts | 40 ++++++++++++++++++++++++++++++----
2 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/app/d/[slug]/CommentsShell.tsx b/app/d/[slug]/CommentsShell.tsx
index 707ee1d..8103934 100644
--- a/app/d/[slug]/CommentsShell.tsx
+++ b/app/d/[slug]/CommentsShell.tsx
@@ -697,9 +697,9 @@ function SelectionToolbar({
onComment: () => void;
onReact: (emoji: string) => void;
}) {
- // The iframe spans the docwrap; the selection rect's viewport-top maps onto the
- // docwrap (both share the top edge). Clamp into view.
- const top = Math.max(8, viewTop);
+ // The iframe spans the docwrap; iframe viewport coordinates map onto this
+ // wrapper, then CSS clamps the toolbar inside the visible pane.
+ const top = `max(8px, min(${Math.max(8, Math.round(viewTop))}px, calc(100% - 84px)))`;
const [picking, setPicking] = useState(false);
return (
diff --git a/lib/docs/overlay.ts b/lib/docs/overlay.ts
index a5919bc..c128220 100644
--- a/lib/docs/overlay.ts
+++ b/lib/docs/overlay.ts
@@ -569,9 +569,13 @@ export const OVERLAY_SCRIPT = String.raw`
var exact = (e > s) ? tm.full.slice(s, e) : sel.toString();
return { exact: exact, prefix: tm.full.slice(Math.max(0, s-32), s), suffix: tm.full.slice(e, e+32) };
}
- document.addEventListener("mouseup", function(ev){
- if (ev.target && ev.target.closest && (ev.target.closest("[data-jh-chip]") || ev.target.closest(".jh-pop"))) return;
- setTimeout(function(){
+ var selectionTimer = null;
+ function fromOverlayChrome(ev){
+ var t = ev && ev.target;
+ return !!(t && t.closest && (t.closest("[data-jh-chip]") || t.closest(".jh-pop")));
+ }
+ function reportSelection(){
+ try {
var sel = window.getSelection();
if (!sel || !sel.rangeCount || sel.isCollapsed || !sel.toString().trim()){ send({type:"jh:selectionCleared"}); return; }
var anchor = anchorFromSelection(sel);
@@ -580,7 +584,35 @@ export const OVERLAY_SCRIPT = String.raw`
top: rect.top + window.scrollY, left: rect.left, right: rect.right, bottom: rect.bottom + window.scrollY,
viewTop: rect.top
}});
- }, 10);
+ } catch(e) {
+ send({type:"jh:selectionCleared"});
+ }
+ }
+ function queueSelectionReport(delay){
+ if (selectionTimer) clearTimeout(selectionTimer);
+ selectionTimer = setTimeout(function(){
+ selectionTimer = null;
+ reportSelection();
+ }, delay);
+ }
+ document.addEventListener("mouseup", function(ev){
+ if (fromOverlayChrome(ev)) return;
+ queueSelectionReport(10);
+ });
+ document.addEventListener("keyup", function(ev){
+ if (fromOverlayChrome(ev)) return;
+ queueSelectionReport(10);
+ });
+ document.addEventListener("touchend", function(ev){
+ if (fromOverlayChrome(ev)) return;
+ queueSelectionReport(80);
+ }, {passive:true});
+ document.addEventListener("pointerup", function(ev){
+ if (fromOverlayChrome(ev)) return;
+ queueSelectionReport(30);
+ });
+ document.addEventListener("selectionchange", function(){
+ queueSelectionReport(120);
});
window.addEventListener("message", function(ev){