A browser extension that lets you summarize any webpage and ask questions using AI.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(popup): use CONFIG rules for unsupported extraction UI

+81 -22
+1 -1
popup/popup.css
··· 589 589 max-width: 280px; 590 590 } 591 591 592 - /* ── YouTube Error ── */ 592 + /* ── Unsupported extraction (YouTube, streaming, Bluesky, email UIs, etc.) ── */ 593 593 .youtube-error { 594 594 display: flex; 595 595 flex-direction: column;
+80 -21
popup/popup.js
··· 443 443 return; 444 444 } 445 445 446 - // Check if this is a YouTube video 447 - if (isYoutubeUrl(currentTabUrl)) { 448 - showYoutubeError(); 446 + const unsupportedExtraction = getUnsupportedExtractionInfo(currentTabUrl); 447 + if (unsupportedExtraction) { 448 + showUnsupportedExtractionError( 449 + unsupportedExtraction.title, 450 + unsupportedExtraction.message, 451 + ); 449 452 return; 450 453 } 451 454 ··· 1531 1534 return pathname.endsWith(".pdf") || pathname.includes(".pdf?"); 1532 1535 } 1533 1536 1534 - function isYoutubeUrl(url) { 1535 - if (!url) return false; 1536 - // Check for YouTube video URLs (youtube.com/watch or youtu.be/short) 1537 - const urlObj = new URL(url); 1538 - const hostname = urlObj.hostname.toLowerCase(); 1539 - const pathname = urlObj.pathname.toLowerCase(); 1540 - // Match youtube.com/watch?v= or youtu.be/VIDEO_ID 1541 - return ( 1542 - ((hostname === "www.youtube.com" || hostname === "youtube.com") && 1543 - pathname === "/watch") || 1544 - hostname === "youtu.be" 1545 - ); 1537 + function unsupportedHostSuffixMatches(hostname, suffix) { 1538 + const s = String(suffix).toLowerCase(); 1539 + const h = hostname.toLowerCase(); 1540 + return h === s || h.endsWith("." + s); 1541 + } 1542 + 1543 + function unsupportedUrlRuleMatches(hostname, pathname, anyOf) { 1544 + if (!anyOf || !anyOf.length) return false; 1545 + for (const part of anyOf) { 1546 + if (part.hostEquals) { 1547 + if (hostname === String(part.hostEquals).toLowerCase()) { 1548 + return true; 1549 + } 1550 + continue; 1551 + } 1552 + if (part.hostIn) { 1553 + const hosts = part.hostIn.map((x) => String(x).toLowerCase()); 1554 + if (!hosts.includes(hostname)) continue; 1555 + if (part.pathnameEquals !== undefined) { 1556 + if (pathname === String(part.pathnameEquals).toLowerCase()) { 1557 + return true; 1558 + } 1559 + continue; 1560 + } 1561 + return true; 1562 + } 1563 + } 1564 + return false; 1565 + } 1566 + 1567 + /** 1568 + * Sites where page text isn't extractable like a normal article (streaming, apps, banking, etc.). 1569 + * Rules live in `CONFIG.UNSUPPORTED_EXTRACTION.RULES` (`scripts/config.js`). 1570 + */ 1571 + function getUnsupportedExtractionInfo(url) { 1572 + const rules = 1573 + typeof CONFIG !== "undefined" && CONFIG.UNSUPPORTED_EXTRACTION?.RULES; 1574 + if (!url || !rules?.length) return null; 1575 + 1576 + let hostname; 1577 + let pathname = ""; 1578 + try { 1579 + const u = new URL(url); 1580 + hostname = u.hostname.toLowerCase(); 1581 + pathname = u.pathname.toLowerCase(); 1582 + } catch { 1583 + return null; 1584 + } 1585 + 1586 + for (const rule of rules) { 1587 + if (rule.kind === "url") { 1588 + if (unsupportedUrlRuleMatches(hostname, pathname, rule.anyOf)) { 1589 + return { title: rule.title, message: rule.message }; 1590 + } 1591 + } else if (rule.kind === "host") { 1592 + if (unsupportedHostSuffixMatches(hostname, rule.hostSuffix)) { 1593 + return { title: rule.title, message: rule.message }; 1594 + } 1595 + } else if (rule.kind === "labeled") { 1596 + for (const [suffix, label] of rule.sites) { 1597 + if (unsupportedHostSuffixMatches(hostname, suffix)) { 1598 + return { 1599 + title: `${label} isn't supported`, 1600 + message: rule.message, 1601 + }; 1602 + } 1603 + } 1604 + } 1605 + } 1606 + 1607 + return null; 1546 1608 } 1547 1609 1548 1610 function showPdfError() { ··· 1564 1626 `; 1565 1627 } 1566 1628 1567 - function showYoutubeError() { 1568 - // Hide initial state and show error 1629 + function showUnsupportedExtractionError(title, message) { 1569 1630 initialState.classList.add("hidden"); 1570 1631 resultContainer.classList.remove("hidden"); 1571 1632 footer.classList.add("hidden"); ··· 1574 1635 resultContainer.innerHTML = ` 1575 1636 <div class="youtube-error"> 1576 1637 <div class="youtube-error-icon">:(</div> 1577 - <div class="youtube-error-title">YouTube videos aren't supported</div> 1578 - <div class="youtube-error-message"> 1579 - This extension can't extract text from YouTube videos at the moment. 1580 - </div> 1638 + <div class="youtube-error-title">${escapeHtml(title)}</div> 1639 + <div class="youtube-error-message">${escapeHtml(message)}</div> 1581 1640 </div> 1582 1641 `; 1583 1642 }