|
import('https://esm.sh/@mozilla/readability').then(({ Readability }) => { |
|
var result = new Readability(document.cloneNode(true)).parse(); |
|
const { |
|
title, |
|
byline, |
|
dir, |
|
lang, |
|
content, |
|
textContent, |
|
siteName, |
|
publishedTime, |
|
} = result; |
|
|
|
let publishedDate = new Date(publishedTime); |
|
publishedDate = Number.isNaN(publishedDate.getTime()) |
|
? undefined |
|
: publishedDate.toLocaleDateString('en-US', { |
|
year: 'numeric', |
|
month: 'long', |
|
day: 'numeric', |
|
}); |
|
|
|
function countAndApproximateWords(text) { |
|
const words = text.split(/[^\p{L}\p{N}]+/u).filter(word => word.length > 0); |
|
const wordCount = words.length; |
|
|
|
if (wordCount < 100) { |
|
return "<100 words"; |
|
} else { |
|
const approximateCount = Math.round(wordCount / 100) * 100; |
|
return `${approximateCount} words`; |
|
} |
|
} |
|
|
|
document.write(/* html */` |
|
<html lang="${lang}" dir="${dir}"> |
|
<head> |
|
<title>${title}</title> |
|
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap" rel="stylesheet"> |
|
<style> |
|
body { |
|
font-family: 'Open Sans', sans-serif; |
|
font-size: 1.5rem; |
|
line-height: 1.7; |
|
width: 960px; |
|
margin-right: auto; |
|
margin-left: auto; |
|
transition: background-color 0.3s, color 0.3s; |
|
} |
|
pre:has(> code), code { |
|
max-width: 100%; |
|
overflow-x: auto; |
|
} |
|
body.dark-mode { |
|
background-color: #1a1a1a; |
|
color: #f0f0f0; |
|
} |
|
.dark-mode-toggle { |
|
position: fixed; |
|
top: 20px; |
|
right: 20px; |
|
padding: 10px; |
|
background-color: #333; |
|
color: #fff; |
|
border: none; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<button class="dark-mode-toggle" onclick="toggleDarkMode()">Toggle Dark Mode</button> |
|
<h1>${title}</h1> |
|
<aside>${[byline, publishedDate, countAndApproximateWords(textContent)].filter(Boolean).join(' | ')}</aside> |
|
${content} |
|
<script> |
|
function setDarkMode(isDark) { |
|
if (isDark) { |
|
document.body.classList.add('dark-mode'); |
|
if (document.getElementById('highlight-theme')) { |
|
document.getElementById('highlight-theme').href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github-dark.min.css'; |
|
} |
|
} else { |
|
document.body.classList.remove('dark-mode'); |
|
if (document.getElementById('highlight-theme')) { |
|
document.getElementById('highlight-theme').href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css'; |
|
} |
|
} |
|
} |
|
|
|
|
|
function toggleDarkMode() { |
|
document.body.classList.toggle('dark-mode'); |
|
} |
|
|
|
// Check system preference and set initial mode |
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { |
|
setDarkMode(true); |
|
} |
|
|
|
// Listen for changes in system preference |
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { |
|
setDarkMode(e.matches); |
|
}); |
|
</script> |
|
</body> |
|
</html> |
|
`); |
|
}); |