diff --git a/components/layout/page-header.tsx b/components/layout/page-header.tsx index e8f7bf3b..adceb191 100644 --- a/components/layout/page-header.tsx +++ b/components/layout/page-header.tsx @@ -25,6 +25,7 @@ import { useEffect, useRef, useState } from "react"; import { useRouter, usePathname } from "next/navigation"; +import { Check, Link2 } from "lucide-react"; import { cn } from "@/lib/utils"; const SEARCH_RETURN_KEY = "spsays_from_search"; @@ -91,6 +92,43 @@ export function PageHeader({ return () => window.removeEventListener("scroll", onScroll); }, [sticky]); + // "Copy link to this page" — copies the full, unshortened absolute URL of + // the active page. Deliberately bypasses useCopyFeedback (which rewrites + // `?hl=` highlight URLs into short /share links); here we want the literal + // page URL. The write is synchronous within the click so a single tap works. + const [copied, setCopied] = useState(false); + const copyTimerRef = useRef | null>(null); + useEffect(() => { + return () => { + if (copyTimerRef.current) clearTimeout(copyTimerRef.current); + }; + }, []); + + async function copyPageLink() { + const url = window.location.href; + try { + await navigator.clipboard.writeText(url); + } catch { + // Fallback for browsers without the async clipboard API. + const ta = document.createElement("textarea"); + ta.value = url; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + try { + document.execCommand("copy"); + } catch { + // silent fail + } + document.body.removeChild(ta); + } + setCopied(true); + if (copyTimerRef.current) clearTimeout(copyTimerRef.current); + copyTimerRef.current = setTimeout(() => setCopied(false), 1500); + } + return (
+ +
); diff --git a/components/text-selection-toolbar.tsx b/components/text-selection-toolbar.tsx index 9bcc76ca..043f8e33 100644 --- a/components/text-selection-toolbar.tsx +++ b/components/text-selection-toolbar.tsx @@ -12,10 +12,7 @@ import { } from "lucide-react"; import { toast } from "sonner"; import { formatCopyText, getSourceReference } from "@/lib/source-reference"; -import { - buildHighlightUrl, - buildShortHighlightUrl, -} from "@/lib/text-highlight"; +import { buildHighlightUrl } from "@/lib/text-highlight"; import { bookmarks, SocialError } from "@/lib/social"; import { KautilyaIcon, @@ -393,8 +390,12 @@ export function TextSelectionToolbar() { } async function onCopyLink() { - const url = await buildShortHighlightUrl(selectedText); - await copy(url); + // Hand copy() the long ?hl= URL synchronously — it shortens it to the + // /share/ form inside the ClipboardItem promise, which keeps the + // click's transient activation alive so the short link copies in one go. + // (Pre-awaiting buildShortHighlightUrl here would burn the gesture and + // require a second click, the way "copy caption" via onCopyText doesn't.) + await copy(buildHighlightUrl(selectedText)); toast.success("Link copied", { duration: 1500 }); finishAction(); }