Initial build: stash server + browser extension
Server (Express + better-sqlite3 + FTS5, mirroring keep's stack): /api/capture (upsert-on-url, per PROPOSAL.md's dedup decision), /api/articles (list/search/mark-read/delete), plus a minimal server-rendered reading list and reader view at / and /read/:id (token passed as ?t= there since browser navigation can't set an Authorization header). Bearer-token auth generated via `npm run token` — no login flow, matching keep's one-time identity registration pattern. Extension (Manifest V3, sideloaded, no store distribution): toolbar click or context menu "Send to stash" runs @mozilla/readability (vendored into extension/lib/) against the current page, falling back to raw rendered HTML when extraction comes back too thin. Options page for one-time server URL + token entry. Verified end-to-end with the real extension loaded in a live Chromium instance (not just unit-tested extraction logic): capture against a real page, dedup-on-recapture, mark-read, FTS5 search, 401 on missing auth, and both server-rendered views all confirmed working. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
31
extension/extract.js
Normal file
31
extension/extract.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// Runs in the page as a content script, after lib/Readability.js has already
|
||||
// been injected (see background.js's executeScript files array — order matters).
|
||||
// Its return value becomes the result of chrome.scripting.executeScript.
|
||||
(() => {
|
||||
try {
|
||||
const docClone = document.cloneNode(true);
|
||||
const article = new Readability(docClone).parse();
|
||||
if (article && article.textContent && article.textContent.trim().length > 200) {
|
||||
return {
|
||||
url: location.href,
|
||||
title: article.title || document.title,
|
||||
content_html: article.content,
|
||||
content_text: article.textContent,
|
||||
author: article.byline || undefined,
|
||||
published_at: article.publishedTime || undefined,
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
// fall through to raw capture below
|
||||
}
|
||||
|
||||
// Fallback: Readability either failed or returned too little to be useful
|
||||
// (common on heavily JS-rendered pages) — capture what's actually rendered
|
||||
// rather than failing outright. Worse capture beats no capture.
|
||||
return {
|
||||
url: location.href,
|
||||
title: document.title,
|
||||
content_html: document.body ? document.body.innerHTML : '',
|
||||
content_text: document.body ? document.body.innerText : '',
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user