send-to-ereader/public/index.html

377 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Send to E-Reader — Web Articles to EPUB</title>
<!-- Primary Meta Tags -->
<meta
name="description"
content="Convert web articles to EPUB and email them straight to your e-reader (Kindle, PocketBook, or any device that accepts emailed files). No ads, no tracking, open source."
/>
<meta
name="keywords"
content="send to ereader, send to kindle, send to pocketbook, article to epub, web to ereader, web article epub"
/>
<meta name="author" content="Stefan Atanasov" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://read.atanasov.fi" />
<meta
property="og:title"
content="Send to E-Reader — Web Articles to EPUB"
/>
<meta
property="og:description"
content="Convert web articles to EPUB and email them to your e-reader in one click."
/>
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://read.atanasov.fi" />
<meta
name="twitter:title"
content="Send to E-Reader — Web Articles to EPUB"
/>
<meta
name="twitter:description"
content="Convert web articles to EPUB and email them to your e-reader in one click."
/>
<!-- Canonical URL -->
<link rel="canonical" href="https://read.atanasov.fi" />
<style>
body {
font-family: "Courier New", Courier, monospace;
line-height: 1.4;
max-width: 800px;
margin: 8px auto;
padding: 8px;
background: #f6f6ef;
}
.container {
background: #f6f6ef;
}
h1 {
color: #000;
margin: 0 0 16px 0;
font-size: 16px;
}
.setup-info {
border: 1px solid #828282;
padding: 8px;
margin-bottom: 16px;
font-size: 14px;
}
.setup-info code {
background: #fff;
padding: 2px 4px;
}
.setup-info table {
border-collapse: collapse;
margin: 8px 0;
font-size: 13px;
}
.setup-info table th,
.setup-info table td {
text-align: left;
padding: 2px 8px 2px 0;
}
.input-group {
margin-bottom: 12px;
}
.input-group label {
display: block;
margin-bottom: 4px;
font-size: 14px;
}
input[type="url"],
input[type="email"] {
width: 100%;
padding: 4px;
border: 1px solid #828282;
font-family: "Courier New", Courier, monospace;
font-size: 14px;
background: #fff;
margin-bottom: 8px;
}
input[type="url"]:focus,
input[type="email"]:focus {
outline: none;
border-color: #000;
}
button {
background: #1ce783;
color: #000;
border: 1px solid #828282;
padding: 4px 8px;
font-family: "Courier New", Courier, monospace;
font-size: 14px;
cursor: pointer;
}
button:hover {
background: #00b45a;
}
button:disabled {
background: #cccccc;
cursor: not-allowed;
}
.message {
margin-top: 12px;
padding: 8px;
font-size: 14px;
border: 1px solid #828282;
display: none;
}
.success {
background: #dfd;
display: block;
}
.error {
background: #fdd;
display: block;
}
.loading {
background: #ffffd1;
display: block;
}
#credits {
font-size: 12px;
margin-top: 16px;
}
</style>
</head>
<body>
<header>
<h1>Send to E-Reader</h1>
</header>
<main>
<article class="container">
<section class="setup-info">
<h2>guide</h2>
<ol>
<li>
whitelist <code>send@read.atanasov.fi</code> on your device — one-time setup:
<ul>
<li>
<strong>Kindle</strong>: add it in
<a
href="https://www.amazon.com/hz/mycd/myx#/home/settings/payment"
target="_blank"
rel="noopener noreferrer"
>Amazon → Manage Your Content and Devices → Preferences →
Personal Document Settings → Approved Personal Document E-mail List</a
>
before you send the first article.
</li>
<li>
<strong>PocketBook</strong>: no dashboard step needed. Send the
first article — PocketBook will email you a confirmation with
an "add to whitelist" link. Click it once, and every future
article will be delivered automatically.
</li>
</ul>
</li>
<li>enter your e-reader's email below</li>
<li>paste the article URL and hit send</li>
</ol>
<h3>e-reader email formats</h3>
<table>
<tr>
<th>device</th>
<th>email pattern</th>
</tr>
<tr>
<td>Amazon Kindle</td>
<td><code>*@kindle.com</code></td>
</tr>
<tr>
<td>PocketBook</td>
<td><code>*@pbsync.com</code></td>
</tr>
</table>
<h3>notes:</h3>
<ul>
<li>any email works — domain isn't checked, use whatever your device gave you</li>
<li>article should be publicly accessible (no paywalls / login required)</li>
<li>EPUB is generated server-side and deleted immediately after sending</li>
<li>
your e-reader email is saved in your browser
<code>localStorage</code> for future use; nothing is stored on the server
</li>
</ul>
</section>
<form
id="readerForm"
class="reader-form"
aria-label="Article to e-reader conversion form"
>
<div class="input-group">
<label for="readerEmail">E-Reader Email:</label>
<input
type="email"
id="readerEmail"
name="readerEmail"
placeholder="your-device@kindle.com"
autocomplete="email"
required
aria-required="true"
/>
</div>
<div class="input-group">
<label for="articleUrl">Article URL:</label>
<input
type="url"
id="articleUrl"
name="articleUrl"
placeholder="https://example.com/article"
autocomplete="off"
required
aria-required="true"
/>
</div>
<button type="submit" id="sendButton">Send</button>
<div
id="message"
class="message"
role="alert"
aria-live="polite"
></div>
</form>
</article>
</main>
<footer>
<p id="credits">
v 0.1.0 •
<a
href="https://atanasov.fi"
target="_blank"
rel="noopener noreferrer"
>atanasov.fi</a
>
</p>
</footer>
<script>
const urlInput = document.getElementById("articleUrl");
const emailInput = document.getElementById("readerEmail");
const sendButton = document.getElementById("sendButton");
const messageDiv = document.getElementById("message");
// Migrate any previously-saved key from the old version
const legacy = localStorage.getItem("kindleEmail");
if (legacy && !localStorage.getItem("readerEmail")) {
localStorage.setItem("readerEmail", legacy);
localStorage.removeItem("kindleEmail");
}
if (localStorage.getItem("readerEmail")) {
emailInput.value = localStorage.getItem("readerEmail");
}
function showMessage(text, type) {
messageDiv.textContent = text;
messageDiv.className = `message ${type}`;
}
async function sendArticle(event) {
if (event) event.preventDefault();
const url = urlInput.value.trim();
const email = emailInput.value.trim();
if (!url) {
showMessage("please enter a valid url", "error");
return;
}
if (!email) {
showMessage("please enter your e-reader email", "error");
return;
}
try {
sendButton.disabled = true;
showMessage("processing article...", "loading");
localStorage.setItem("readerEmail", email);
const response = await fetch("/send-article", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ url, readerEmail: email }),
});
const data = await response.json();
if (data.success) {
showMessage(`success! "${data.title}" sent to your e-reader`, "success");
urlInput.value = "";
} else {
throw new Error(data.error || data.message || "failed to send article");
}
} catch (error) {
showMessage(error.message || "error sending article", "error");
} finally {
sendButton.disabled = false;
}
}
document
.getElementById("readerForm")
.addEventListener("submit", sendArticle);
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "Send to E-Reader",
"applicationCategory": "UtilityApplication",
"offers": {
"@type": "Offer",
"price": "0",
"priceCurrency": "EUR"
},
"creator": {
"@type": "Person",
"name": "Stefan Atanasov",
"url": "https://atanasov.fi"
},
"description": "Convert web articles to EPUB and email them to your e-reader.",
"operatingSystem": "All",
"browserRequirements": "Requires JavaScript"
}
</script>
</body>
</html>