diff --git a/CHANGELOG.md b/CHANGELOG.md index 21101e8..5411afb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Improve mobile responsiveness and book metadata layout * Fix words per display setting and limit max to 3 +* Add copy link button to share reading position across devices ### 2026-02-01: 1.0.3 diff --git a/src/App.jsx b/src/App.jsx index e81019f..d943dbd 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -10,6 +10,8 @@ import { FileText, Upload, Settings, + Link, + Check, } from "lucide-react"; import JSZip from "jszip"; @@ -274,6 +276,22 @@ function App() { .filter((w) => w.length > 0); }); const [currentIndex, setCurrentIndex] = useState(() => { + // Check URL hash for shared position + const hash = window.location.hash; + if (hash) { + const params = new URLSearchParams(hash.slice(1)); + const urlPos = parseInt(params.get("pos"), 10); + if (!isNaN(urlPos) && urlPos >= 0) { + // Clear hash after reading + window.history.replaceState(null, "", window.location.pathname); + const t = savedSettings?.text || DEFAULT_TEXT; + const wordCount = t + .trim() + .split(/\s+/) + .filter((w) => w.length > 0).length; + return Math.min(Math.max(0, urlPos), Math.max(0, wordCount - 1)); + } + } const t = savedSettings?.text || DEFAULT_TEXT; const pos = getPositionForText(t, savedSettings?.positions || {}); const wordCount = t @@ -301,6 +319,7 @@ function App() { const [fetchMetadataOnline, setFetchMetadataOnline] = useState( () => savedSettings?.fetchMetadataOnline ?? false, ); + const [linkCopied, setLinkCopied] = useState(false); const timeoutRef = useRef(null); const prevTextRef = useRef(text); const fileInputRef = useRef(null); @@ -487,6 +506,17 @@ function App() { setWpm((prev) => Math.max(50, Math.min(1500, prev + delta))); }; + const copyPositionUrl = async () => { + const url = `${window.location.origin}${window.location.pathname}#pos=${currentIndex}`; + try { + await navigator.clipboard.writeText(url); + setLinkCopied(true); + setTimeout(() => setLinkCopied(false), 2000); + } catch (e) { + console.error("Failed to copy URL:", e); + } + }; + const handleProgressClick = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; @@ -710,8 +740,19 @@ function App() { >
-