diff --git a/components/selection-toolbar/more-popover.tsx b/components/selection-toolbar/more-popover.tsx index d3b91044..3c3fbe8f 100644 --- a/components/selection-toolbar/more-popover.tsx +++ b/components/selection-toolbar/more-popover.tsx @@ -1,10 +1,9 @@ "use client"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef } from "react"; import { createPortal } from "react-dom"; import { useCopyFeedback, type Position } from "./shared"; -import { buildShortHighlightUrl } from "@/lib/text-highlight"; -import { Bookmark, FolderPlus, AtSign, Link2, Check } from "lucide-react"; +import { Bookmark, FolderPlus, AtSign } from "lucide-react"; import { Drawer, DrawerClose, @@ -34,18 +33,6 @@ export function MorePopover({ }) { const ref = useRef(null); const { copied, copy } = useCopyFeedback(); - const [generatingLink, setGeneratingLink] = useState(false); - - async function handleCopyLink() { - if (generatingLink) return; - setGeneratingLink(true); - try { - const url = await buildShortHighlightUrl(selectedText); - await copy(url); - } finally { - setGeneratingLink(false); - } - } useEffect(() => { if (!open || isMobile) return; @@ -80,17 +67,6 @@ export function MorePopover({ icon: , onClick: onSaveToCollection, }, - { - key: "copy-link", - label: copied - ? "Link Copied!" - : generatingLink - ? "Generating link…" - : "Copy Link", - description: "Copy a shareable link to this quote", - icon: copied ? : , - onClick: handleCopyLink, - }, { key: "mention", label: "Mention", diff --git a/components/selection-toolbar/share-modal.tsx b/components/selection-toolbar/share-modal.tsx index 9a314601..c7493693 100644 --- a/components/selection-toolbar/share-modal.tsx +++ b/components/selection-toolbar/share-modal.tsx @@ -51,7 +51,7 @@ export function ShareModal({ }, { key: "link", - label: "Share Link", + label: "Copy Link", icon: , description: "Copy a shareable link", onClick: handleCopyShareLink, diff --git a/components/selection-toolbar/shared.tsx b/components/selection-toolbar/shared.tsx index 7f5b2c27..b6b0fefc 100644 --- a/components/selection-toolbar/shared.tsx +++ b/components/selection-toolbar/shared.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useEffect, useRef, useCallback, type ReactNode } from "react"; +import { createShortLink } from "@/lib/share"; // --------------------------------------------------------------------------- // Kautilya Icon @@ -61,20 +62,56 @@ export function useMediaQuery(query: string): boolean { // Hook: copy to clipboard with success feedback // --------------------------------------------------------------------------- +// Match a URL inside the copy payload that carries a `?hl=` highlight +// fragment. Used by useCopyFeedback to transparently swap such URLs for the +// short `/share/` form before writing to the clipboard. The native +// ⌘C / Ctrl+C path goes through a different (synchronous) handler in +// text-selection-toolbar.tsx and is intentionally not affected. +// +// The regex stops at whitespace or trailing punctuation that wouldn't appear +// in a real URL (`>`, `]`, etc.), so it works against multi-line copy +// payloads like the source-attribution block produced by formatCopyText(). +const HL_URL_RE = /https?:\/\/[^\s<>"'`)\]]+\?hl=[^\s<>"'`)\]]+/g; + +async function shortenHighlightUrlsIn(text: string): Promise { + // Cheap pre-check — most text passed to copy() has no `?hl=` URL. + if (!text.includes("?hl=")) return text; + + const matches = Array.from(text.matchAll(HL_URL_RE)); + if (matches.length === 0) return text; + + let result = text; + for (const m of matches) { + const longUrl = m[0]; + try { + const u = new URL(longUrl); + const relativePath = u.pathname + u.search + u.hash; + const code = await createShortLink(relativePath); + if (!code) continue; // BFF failed — leave the long URL alone. + const shortUrl = new URL(`/share/${code}`, u.origin).toString(); + result = result.replace(longUrl, shortUrl); + } catch { + // Malformed URL or unexpected error — fall through with the original. + } + } + return result; +} + export function useCopyFeedback() { const [copied, setCopied] = useState(false); const timerRef = useRef | null>(null); const copy = useCallback(async (text: string) => { let success = false; + const finalText = await shortenHighlightUrlsIn(text); try { - await navigator.clipboard.writeText(text); + await navigator.clipboard.writeText(finalText); success = true; } catch { // Fallback for iOS Safari and older browsers const ta = document.createElement("textarea"); - ta.value = text; + ta.value = finalText; ta.style.position = "fixed"; ta.style.top = "0"; ta.style.left = "0";