Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121export function captureSelection(root, page) { const selection = window.getSelection(); if (!selection?.rangeCount || selection.isCollapsed) return null; const range = selection.getRangeAt(0); if ( !root?.contains(range.startContainer) || !root.contains(range.endContainer) ) return null; if ( range.startContainer.parentElement?.closest("button,pre,textarea,input") || range.endContainer.parentElement?.closest("button,pre,textarea,input") ) return null; const quote = range.toString().trim(); if (!quote || quote.length > 12000) return null; const before = range.cloneRange(); before.selectNodeContents(root); before.setEnd(range.startContainer, range.startOffset); const after = range.cloneRange(); after.selectNodeContents(root); after.setStart(range.endContainer, range.endOffset); const box = root.getBoundingClientRect(); const rects = page ? [...range.getClientRects()] .filter((r) => r.width > 1 && r.height > 1) .slice(0, 200) .map((r) => ({ x: Math.max(0, Math.min(1, (r.left - box.left) / box.width)), y: Math.max(0, Math.min(1, (r.top - box.top) / box.height)), width: Math.min(1, r.width / box.width), height: Math.min(1, r.height / box.height), })) : []; const rect = range.getBoundingClientRect(); return { quote, prefix: before.toString().slice(-100), suffix: after.toString().slice(0, 100), page: page || null, rects, toolbar: { left: Math.max(12, Math.min(window.innerWidth - 332, rect.left)), top: Math.max(82, rect.top - 45), }, };}export function applyArticleHighlights(root, highlights, name = "odre-saved") { if (!root || !globalThis.Highlight || !CSS.highlights) return; const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode: (node) => node.parentElement?.closest("button,pre,.diagram,.exercise") ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT, }); const nodes = []; let full = "", node; while ((node = walker.nextNode())) { nodes.push({ node, start: full.length }); full += node.textContent; } const ranges = []; for (const h of highlights) { if (h.page) continue; const firstMatch = full.indexOf(h.quote); let start = firstMatch; while ( start >= 0 && h.prefix && !full .slice(Math.max(0, start - h.prefix.length), start) .endsWith(h.prefix) ) start = full.indexOf(h.quote, start + 1); // Prefixes can include rendered controls that the text walker intentionally skips. if ( start < 0 && firstMatch >= 0 && full.indexOf(h.quote, firstMatch + 1) < 0 ) start = firstMatch; if (start < 0) continue; const end = start + h.quote.length, first = nodes.find((n) => n.start + n.node.length > start), last = [...nodes].reverse().find((n) => n.start < end); if (first && last) { const r = new Range(); r.setStart(first.node, start - first.start); r.setEnd(last.node, end - last.start); ranges.push(r); } } CSS.highlights.set(name, new Highlight(...ranges));}
export function scrollToArticleText(root, text, { behavior = "auto" } = {}) { if (!root || !text) return false; const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode: (node) => node.parentElement?.closest("button,textarea,input,summary") ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT, }); const nodes = []; let full = "", node; while ((node = walker.nextNode())) { nodes.push({ node, start: full.length }); full += node.textContent; } let index = full.indexOf(text); if (index < 0) index = full.toLocaleLowerCase().indexOf(text.toLocaleLowerCase()); if (index < 0) return false; const match = nodes.find((n) => n.start + n.node.length > index); if (!match) return false; match.node.parentElement.scrollIntoView({ behavior, block: "center" }); return true;}