aboutsummaryrefslogtreecommitdiff
path: root/src/domains/readpage/read-page.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/domains/readpage/read-page.ts')
-rw-r--r--src/domains/readpage/read-page.ts36
1 files changed, 35 insertions, 1 deletions
diff --git a/src/domains/readpage/read-page.ts b/src/domains/readpage/read-page.ts
index 9d46c7e..fadee38 100644
--- a/src/domains/readpage/read-page.ts
+++ b/src/domains/readpage/read-page.ts
@@ -15,7 +15,6 @@ import {
waitForIsolatedLoad,
} from "../isolated-tab";
import { renderExpandableText } from "../render";
-import { buildPageCaptureExpr } from "./capture";
import { extractReadable, type PageCapture, type ReadablePage } from "./readability";
const LOAD_TIMEOUT_MS = 15_000;
@@ -49,6 +48,41 @@ const PageCaptureSchema = Type.Object({
const pageCaptureValidator = Compile(PageCaptureSchema);
+const BODY_TEXT_LIMIT = 20_000;
+
+const buildPageCaptureExpr = (): string => `
+ (() => {
+ const BOILERPLATE = new Set(['NAV', 'HEADER', 'FOOTER', 'ASIDE']);
+ const inBoilerplate = (el) => {
+ for (let n = el; n; n = n.parentElement) {
+ if (BOILERPLATE.has(n.tagName)) return true;
+ const role = n.getAttribute && n.getAttribute('role');
+ if (role === 'navigation' || role === 'banner' || role === 'contentinfo') return true;
+ }
+ return false;
+ };
+ const kindOf = (tag) => {
+ if (/^H[1-6]$/.test(tag)) return 'heading';
+ if (tag === 'LI') return 'listitem';
+ if (tag === 'BLOCKQUOTE') return 'blockquote';
+ if (tag === 'P') return 'paragraph';
+ return 'other';
+ };
+ const selector = 'p, h1, h2, h3, h4, h5, h6, li, blockquote';
+ const root = document.querySelector('article, main, [role=main]') || document.body;
+ const blocks = [];
+ for (const el of root.querySelectorAll(selector)) {
+ const text = (el.innerText || '').replace(/\\s+/g, ' ').trim();
+ if (!text) continue;
+ let linkTextLength = 0;
+ for (const a of el.querySelectorAll('a')) linkTextLength += (a.innerText || '').length;
+ blocks.push({ kind: kindOf(el.tagName), text, linkTextLength, inBoilerplate: inBoilerplate(el) });
+ }
+ const bodyText = (document.body.innerText || '').replace(/\\n{3,}/g, '\\n\\n').slice(0, ${BODY_TEXT_LIMIT});
+ return JSON.stringify({ url: location.href, title: document.title, blocks, bodyText });
+ })()
+`;
+
const toPageCapture = (raw: unknown): PageCapture | undefined => {
if (typeof raw === "string") {
const parsed = parseJson(raw, pageCaptureValidator);