@wrenautomation/read 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/brand/brand.d.ts +69 -0
- package/dist/brand/brand.js +127 -0
- package/dist/brand/brand.js.map +1 -0
- package/dist/brand/index.d.ts +5 -0
- package/dist/brand/index.js +4 -0
- package/dist/brand/index.js.map +1 -0
- package/dist/brand/page.d.ts +9 -0
- package/dist/brand/page.js +162 -0
- package/dist/brand/page.js.map +1 -0
- package/dist/brand/score.d.ts +31 -0
- package/dist/brand/score.js +63 -0
- package/dist/brand/score.js.map +1 -0
- package/dist/brand/sheet.d.ts +19 -0
- package/dist/brand/sheet.js +158 -0
- package/dist/brand/sheet.js.map +1 -0
- package/dist/brand/votes.d.ts +34 -0
- package/dist/brand/votes.js +103 -0
- package/dist/brand/votes.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
The web and files in, text out. No database, no accounts, no business rules: a library that
|
|
4
4
|
fetches and reads. Until 0.29.0 this was falcon (files, markup, media) and the fetch and video
|
|
5
5
|
halves of weaver; the research engine on top is [`@wrenautomation/birdwatch`](https://github.com/wrenautomation/engine/tree/main/packages/birdwatch),
|
|
6
|
-
whose `birdwatch` CLI carries these readers (`fetch`, `read`, `list`, `text`, `page`, `watch`).
|
|
6
|
+
whose `birdwatch` CLI carries these readers (`fetch`, `read`, `list`, `text`, `page`, `watch`, `brand`).
|
|
7
7
|
|
|
8
8
|
| Import | Does |
|
|
9
9
|
|---|---|
|
|
10
|
+
| `@wrenautomation/read/brand` | `brandOf` a site: logo (`svg` inline, `img`, `icon`, `og`), palette (`primary`, `accent`, `background`, `text` as `#rrggbb`), fonts (`heading`, `body`), a 0 to 1 confidence each, and `from` (what was read). The page and up to 8 stylesheets by plain HTTP first; when that is `thin` and a `LadderClient` is given, a browser's computed styles (`web GET /brand`, shape `BrandProbe`), else the ladder's render rung's HTML. No model |
|
|
10
11
|
| `@wrenautomation/read/fetch` | `PoliteFetcher` (one request at a time per host, a contact in the User-Agent, retries on 429 and 5xx, a byte cap), `robots` in warn mode, `fetchLadder` (plain GET up to a browser through a `LadderClient`: autobrowse's `web GET /fetch`) |
|
|
11
12
|
| `@wrenautomation/read/text` | `htmlText`, `pdfText`, `decodeHtml`, `decodeEntities`; `readPage` (HTML to title, text, links, mailtos, tels) |
|
|
12
13
|
| `@wrenautomation/read/video` | `watchFile` a video file: ffmpeg cuts and frames, an ear for the speech (`cohereSpeech`), an eye for the frames (`cohereEye`), and a video model's read of the whole file when given (`geminiVideo`, or `geminiReader` with no key check, for a proxy that holds the keys); shot labels. Over a URL: `readYoutube` (captions and chapters by yt-dlp; with an `ear`, a caption 429 rests captions 30 min and the ear hears the uploader's own audio track in 60 s clips; plus a video model's read of the screen in clips), `readYoutubeByUrl` (the video model alone, no download), `readVideo` (any other video: a `MediaStore` copy, else a direct file link such as Instagram Graph's `media_url`, else yt-dlp; then `watchFile`; `mediaKey` names a video `<platform>/<id>` and `dirStore` keeps them in a folder), `readAudio` (a podcast by its audio URL, then `hearAudio`), `watch` over a URL or a file, `flatPlaylist` (what a profile, channel or search holds), `download`. Every reader returns the one `VideoRead` shape |
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A site's brand: logo, palette and fonts (WA-1094; the shape follows ScrapeGraphAI's `branding`).
|
|
3
|
+
* Static first: the page and its stylesheets by plain HTTP. Thin (`thin`) and a ladder at hand: a
|
|
4
|
+
* browser's computed styles (`web GET /brand`); that route absent, the ladder's render rung's HTML.
|
|
5
|
+
* No model. Decision log: wren designs/2026-10-26-research-engine.md, 11-05 brand.
|
|
6
|
+
*/
|
|
7
|
+
import type { Fetcher } from "../fetch/fetcher.js";
|
|
8
|
+
import { type LadderClient } from "../fetch/ladder.js";
|
|
9
|
+
import { type Scored } from "./score.js";
|
|
10
|
+
import { type Votes } from "./votes.js";
|
|
11
|
+
/** `static`: plain HTTP; `ladder`: the page came up the ladder; `render`: a rendered page's HTML; `browser`: computed styles. */
|
|
12
|
+
export type BrandSource = "static" | "ladder" | "render" | "browser";
|
|
13
|
+
export interface Brand extends Scored {
|
|
14
|
+
/** The page read, after redirects. */
|
|
15
|
+
url: string;
|
|
16
|
+
from: BrandSource[];
|
|
17
|
+
/** Stylesheets read. */
|
|
18
|
+
sheets: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* What `web GET /brand` answers: one page's computed styles in a browser, light scheme.
|
|
22
|
+
* Colours as the browser gives them (`rgb(…)`); fonts as `font-family` lists.
|
|
23
|
+
*/
|
|
24
|
+
export interface BrandProbe {
|
|
25
|
+
finalUrl: string;
|
|
26
|
+
status: number | null;
|
|
27
|
+
/** null: a usable page; else the block the browser still met. */
|
|
28
|
+
block: string | null;
|
|
29
|
+
themeColor: string | null;
|
|
30
|
+
body: {
|
|
31
|
+
background: string;
|
|
32
|
+
color: string;
|
|
33
|
+
font: string;
|
|
34
|
+
};
|
|
35
|
+
/** Visible h1 and h2, first 6. */
|
|
36
|
+
headings: {
|
|
37
|
+
font: string;
|
|
38
|
+
color: string;
|
|
39
|
+
}[];
|
|
40
|
+
/** Visible buttons and button-like links with a fill, first 20; `area` in px². */
|
|
41
|
+
buttons: {
|
|
42
|
+
background: string;
|
|
43
|
+
color: string;
|
|
44
|
+
area: number;
|
|
45
|
+
}[];
|
|
46
|
+
/** Visible links' text colours, first 30. */
|
|
47
|
+
links: {
|
|
48
|
+
color: string;
|
|
49
|
+
}[];
|
|
50
|
+
/** Images and SVGs named logo or brand, or in the home link; `svg` is the markup. */
|
|
51
|
+
logos: {
|
|
52
|
+
src: string | null;
|
|
53
|
+
svg: string | null;
|
|
54
|
+
named: boolean;
|
|
55
|
+
top: boolean;
|
|
56
|
+
area: number;
|
|
57
|
+
}[];
|
|
58
|
+
}
|
|
59
|
+
export interface BrandDeps {
|
|
60
|
+
fetcher: Fetcher;
|
|
61
|
+
/** autobrowse's site client: the ladder and `web GET /brand`. Absent: static only. */
|
|
62
|
+
sites?: LadderClient | undefined;
|
|
63
|
+
/** Stylesheets to read, `@import`s included. Default 8. */
|
|
64
|
+
sheets?: number | undefined;
|
|
65
|
+
}
|
|
66
|
+
/** Computed styles as votes: what a person sees, so they outweigh the CSS. */
|
|
67
|
+
export declare function voteProbe(p: BrandProbe, v: Votes): void;
|
|
68
|
+
/** One site's brand. Never throws: a page that cannot be read gives an empty brand. */
|
|
69
|
+
export declare function brandOf(url: string, d: BrandDeps): Promise<Brand>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { fetchLadder } from "../fetch/ladder.js";
|
|
2
|
+
import { readPage } from "./page.js";
|
|
3
|
+
import { score, thin } from "./score.js";
|
|
4
|
+
import { importsOf, parseSheet, varsOf, voteSheet, voteVars } from "./sheet.js";
|
|
5
|
+
import { abs, apart, chromatic, fontOf, hexOf, votesOf } from "./votes.js";
|
|
6
|
+
const SHEET_MAX = 2_000_000;
|
|
7
|
+
/** Votes from a page and its stylesheets; `cache` keeps sheets across passes. */
|
|
8
|
+
async function readStatic(html, url, d, cache) {
|
|
9
|
+
const v = votesOf();
|
|
10
|
+
const page = readPage(html, url, v);
|
|
11
|
+
const cap = d.sheets ?? 8;
|
|
12
|
+
const get = (href) => d.fetcher
|
|
13
|
+
.get(href)
|
|
14
|
+
.then((r) => (r.status < 400 ? r.text.slice(0, SHEET_MAX) : null))
|
|
15
|
+
.catch(() => null);
|
|
16
|
+
const fetchAll = async (hrefs) => {
|
|
17
|
+
const fresh = hrefs.filter((h) => !cache.has(h)).slice(0, Math.max(0, cap - cache.size));
|
|
18
|
+
const got = await Promise.all(fresh.map(get));
|
|
19
|
+
for (const [i, h] of fresh.entries())
|
|
20
|
+
cache.set(h, got[i] ?? null);
|
|
21
|
+
};
|
|
22
|
+
await fetchAll(page.sheets);
|
|
23
|
+
const roots = [...page.sheets, ...page.css].flatMap((s, i) => {
|
|
24
|
+
const text = i < page.sheets.length ? cache.get(s) : s;
|
|
25
|
+
const root = text ? parseSheet(text) : null;
|
|
26
|
+
return root ? [{ root, href: i < page.sheets.length ? s : url }] : [];
|
|
27
|
+
});
|
|
28
|
+
const imports = roots.flatMap(({ root, href }) => importsOf(root).flatMap((i) => abs(i, href) ?? []));
|
|
29
|
+
if (imports.length) {
|
|
30
|
+
await fetchAll(imports);
|
|
31
|
+
for (const h of imports) {
|
|
32
|
+
const root = cache.get(h) ? parseSheet(cache.get(h)) : null;
|
|
33
|
+
if (root)
|
|
34
|
+
roots.push({ root, href: h });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const trees = roots.map((r) => r.root);
|
|
38
|
+
const vars = varsOf(trees);
|
|
39
|
+
voteVars(vars, v);
|
|
40
|
+
for (const t of trees)
|
|
41
|
+
voteSheet(t, vars, v);
|
|
42
|
+
return v;
|
|
43
|
+
}
|
|
44
|
+
/** Computed styles as votes: what a person sees, so they outweigh the CSS. */
|
|
45
|
+
export function voteProbe(p, v) {
|
|
46
|
+
const bg = hexOf(p.body.background);
|
|
47
|
+
const ink = hexOf(p.body.color);
|
|
48
|
+
if (bg)
|
|
49
|
+
v.colors.push({ role: "background", hex: bg, w: 0.9 });
|
|
50
|
+
if (ink)
|
|
51
|
+
v.colors.push({ role: "text", hex: ink, w: 0.9 });
|
|
52
|
+
const body = fontOf(p.body.font);
|
|
53
|
+
if (body)
|
|
54
|
+
v.fonts.push({ role: "body", family: body, w: 0.9 });
|
|
55
|
+
for (const h of p.headings) {
|
|
56
|
+
const family = fontOf(h.font);
|
|
57
|
+
if (family)
|
|
58
|
+
v.fonts.push({ role: "heading", family, w: 0.85 / p.headings.length });
|
|
59
|
+
}
|
|
60
|
+
const theme = p.themeColor ? hexOf(p.themeColor) : null;
|
|
61
|
+
if (theme && chromatic(theme))
|
|
62
|
+
v.colors.push({ role: "primary", hex: theme, w: 0.4 });
|
|
63
|
+
const filled = p.buttons.flatMap((b) => {
|
|
64
|
+
const hex = hexOf(b.background);
|
|
65
|
+
return hex && (!bg || apart(hex, bg)) ? [{ hex, area: Math.max(1, b.area) }] : [];
|
|
66
|
+
});
|
|
67
|
+
const area = filled.reduce((s, b) => s + b.area, 0);
|
|
68
|
+
for (const b of filled)
|
|
69
|
+
v.colors.push({ role: "primary", hex: b.hex, w: (0.8 * b.area) / area });
|
|
70
|
+
const links = p.links.flatMap((l) => {
|
|
71
|
+
const hex = hexOf(l.color);
|
|
72
|
+
return hex && chromatic(hex) ? [hex] : [];
|
|
73
|
+
});
|
|
74
|
+
for (const hex of links)
|
|
75
|
+
v.colors.push({ role: "accent", hex, w: 0.5 / links.length });
|
|
76
|
+
for (const l of p.logos) {
|
|
77
|
+
const url = l.svg
|
|
78
|
+
? `data:image/svg+xml;base64,${Buffer.from(l.svg).toString("base64")}`
|
|
79
|
+
: abs(l.src ?? undefined, p.finalUrl);
|
|
80
|
+
const w = l.named && l.top ? 0.97 : l.named ? 0.8 : l.top ? 0.65 : 0;
|
|
81
|
+
if (url && w)
|
|
82
|
+
v.logos.push({ url, kind: l.svg ? "svg" : "img", w });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** One site's brand. Never throws: a page that cannot be read gives an empty brand. */
|
|
86
|
+
export async function brandOf(url, d) {
|
|
87
|
+
const from = [];
|
|
88
|
+
const cache = new Map();
|
|
89
|
+
let at = url;
|
|
90
|
+
let html = null;
|
|
91
|
+
const page = await d.fetcher.get(url).catch(() => null);
|
|
92
|
+
if (page && page.status < 400) {
|
|
93
|
+
html = page.text;
|
|
94
|
+
at = page.url;
|
|
95
|
+
from.push("static");
|
|
96
|
+
}
|
|
97
|
+
else if (d.sites) {
|
|
98
|
+
const l = await fetchLadder(d.sites, url, "impersonate");
|
|
99
|
+
if ("fetched" in l && !l.fetched.block) {
|
|
100
|
+
html = l.fetched.html;
|
|
101
|
+
at = l.fetched.finalUrl;
|
|
102
|
+
from.push("ladder");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let v = html ? await readStatic(html, at, d, cache) : votesOf();
|
|
106
|
+
let s = score(v);
|
|
107
|
+
if (thin(s) && d.sites) {
|
|
108
|
+
const probe = await d.sites.call("web", "GET", "/brand", { url }).catch(() => null);
|
|
109
|
+
if (probe && !probe.block) {
|
|
110
|
+
voteProbe(probe, v);
|
|
111
|
+
at = probe.finalUrl;
|
|
112
|
+
from.push("browser");
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const l = await fetchLadder(d.sites, url, "render");
|
|
116
|
+
if ("fetched" in l && !l.fetched.block) {
|
|
117
|
+
// the rendered DOM holds the static page and what scripts added
|
|
118
|
+
v = await readStatic(l.fetched.html, l.fetched.finalUrl, d, cache);
|
|
119
|
+
at = l.fetched.finalUrl;
|
|
120
|
+
from.push("render");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
s = score(v);
|
|
124
|
+
}
|
|
125
|
+
return { ...s, url: at, from, sheets: [...cache.values()].filter((t) => t !== null).length };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=brand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/brand/brand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** A site's brand: logo, palette and fonts, from its CSS first and a browser when that is thin. */
|
|
2
|
+
export * from "./brand.js";
|
|
3
|
+
export type { Scored } from "./score.js";
|
|
4
|
+
export { thin } from "./score.js";
|
|
5
|
+
export type { ColorRole, FontRole, LogoKind } from "./votes.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/brand/index.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Votes } from "./votes.js";
|
|
2
|
+
export interface PageRead {
|
|
3
|
+
/** Stylesheet URLs, in page order. */
|
|
4
|
+
sheets: string[];
|
|
5
|
+
/** `<style>` blocks and inline styles as rules. */
|
|
6
|
+
css: string[];
|
|
7
|
+
}
|
|
8
|
+
/** Read one page into `v`; returns the CSS to read next. */
|
|
9
|
+
export declare function readPage(html: string, url: string, v: Votes): PageRead;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand votes from a page's HTML (htmlparser2, one pass): the logo (an image or inline SVG in the
|
|
3
|
+
* header or the home link, by its name), favicons, the theme colour, Google Fonts, and the CSS to
|
|
4
|
+
* read next (`<link rel=stylesheet>`, `<style>`, inline styles of body and buttons).
|
|
5
|
+
*/
|
|
6
|
+
import { Parser } from "htmlparser2";
|
|
7
|
+
import { abs, chromatic, hexOf } from "./votes.js";
|
|
8
|
+
const TOP = /(^|[\s_-])(header|masthead|navbar|topbar|site-header|nav)([\s_-]|$)/i;
|
|
9
|
+
const LOGO = /logo|brand/i;
|
|
10
|
+
const SVG_MAX = 60_000;
|
|
11
|
+
const FONT_HOSTS = /fonts\.googleapis\.com|use\.typekit\.net|fonts\.bunny\.net/;
|
|
12
|
+
/** Families a Google Fonts or Bunny link loads: `family=Inter:wght@400|Playfair+Display`. */
|
|
13
|
+
function familiesOf(href) {
|
|
14
|
+
try {
|
|
15
|
+
const u = new URL(href);
|
|
16
|
+
return u.searchParams
|
|
17
|
+
.getAll("family")
|
|
18
|
+
.flatMap((f) => f.split("|"))
|
|
19
|
+
.map((f) => f.split(":")[0]?.replace(/\+/g, " ").trim() ?? "")
|
|
20
|
+
.filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const homeOf = (href, base) => {
|
|
27
|
+
const u = abs(href, base);
|
|
28
|
+
if (!u)
|
|
29
|
+
return false;
|
|
30
|
+
const a = new URL(u);
|
|
31
|
+
return a.host === new URL(base).host && (a.pathname === "/" || a.pathname === "");
|
|
32
|
+
};
|
|
33
|
+
/** Read one page into `v`; returns the CSS to read next. */
|
|
34
|
+
export function readPage(html, url, v) {
|
|
35
|
+
let base = url;
|
|
36
|
+
const sheets = [];
|
|
37
|
+
const css = [];
|
|
38
|
+
const stack = [];
|
|
39
|
+
let top = 0;
|
|
40
|
+
let home = 0;
|
|
41
|
+
let style = null;
|
|
42
|
+
let svg = null;
|
|
43
|
+
let svgDepth = 0;
|
|
44
|
+
const parser = new Parser({
|
|
45
|
+
onopentag(name, a) {
|
|
46
|
+
const hint = `${a.class ?? ""} ${a.id ?? ""} ${a.role ?? ""}`;
|
|
47
|
+
const isTop = name === "header" || name === "nav" || TOP.test(hint);
|
|
48
|
+
const isHome = name === "a" && homeOf(a.href, base);
|
|
49
|
+
stack.push({ name, top: isTop, home: isHome });
|
|
50
|
+
if (isTop)
|
|
51
|
+
top++;
|
|
52
|
+
if (isHome)
|
|
53
|
+
home++;
|
|
54
|
+
if (name === "base" && a.href)
|
|
55
|
+
base = abs(a.href, url) ?? base;
|
|
56
|
+
if (name === "style")
|
|
57
|
+
style = [];
|
|
58
|
+
if (name === "svg") {
|
|
59
|
+
svgDepth++;
|
|
60
|
+
const named = LOGO.test(`${hint} ${a["aria-label"] ?? ""}`);
|
|
61
|
+
const w = named && (top || home) ? 0.9 : named ? 0.75 : home && top ? 0.7 : 0;
|
|
62
|
+
if (svgDepth === 1 && w)
|
|
63
|
+
svg = { start: parser.startIndex, depth: 1, w };
|
|
64
|
+
}
|
|
65
|
+
if (name === "img") {
|
|
66
|
+
const src = abs(a.src ?? a["data-src"] ?? a.srcset?.split(/\s+/)[0], base);
|
|
67
|
+
if (!src || /^data:image\/gif/.test(src))
|
|
68
|
+
return;
|
|
69
|
+
const named = LOGO.test(`${hint} ${a.alt ?? ""} ${src.split("?")[0]}`);
|
|
70
|
+
const w = named && (top || home) ? 0.95 : named ? 0.7 : home && top ? 0.6 : 0;
|
|
71
|
+
if (w)
|
|
72
|
+
v.logos.push({ url: src, kind: "img", w });
|
|
73
|
+
}
|
|
74
|
+
if (name === "link") {
|
|
75
|
+
const rel = (a.rel ?? "").toLowerCase().split(/\s+/);
|
|
76
|
+
const href = abs(a.href, base);
|
|
77
|
+
if (!href)
|
|
78
|
+
return;
|
|
79
|
+
if (FONT_HOSTS.test(href)) {
|
|
80
|
+
for (const family of familiesOf(href))
|
|
81
|
+
v.fonts.push({ role: "any", family, w: 0.4 });
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const sheet = rel.includes("stylesheet") || (rel.includes("preload") && a.as === "style");
|
|
85
|
+
if (sheet && !/print/.test(a.media ?? ""))
|
|
86
|
+
sheets.push(href);
|
|
87
|
+
if (rel.includes("apple-touch-icon"))
|
|
88
|
+
v.logos.push({ url: href, kind: "icon", w: 0.35 });
|
|
89
|
+
else if (rel.includes("icon") || rel.includes("mask-icon"))
|
|
90
|
+
v.logos.push({
|
|
91
|
+
url: href,
|
|
92
|
+
kind: "icon",
|
|
93
|
+
w: a.type === "image/svg+xml" || /\.svg($|\?)/.test(href) ? 0.4 : 0.25,
|
|
94
|
+
});
|
|
95
|
+
const mask = rel.includes("mask-icon") && a.color ? hexOf(a.color) : null;
|
|
96
|
+
if (mask && chromatic(mask))
|
|
97
|
+
v.colors.push({ role: "primary", hex: mask, w: 0.4 });
|
|
98
|
+
}
|
|
99
|
+
if (name === "meta") {
|
|
100
|
+
const key = (a.name ?? a.property ?? "").toLowerCase();
|
|
101
|
+
if (key === "theme-color" && !a.media?.includes("dark")) {
|
|
102
|
+
const hex = a.content ? hexOf(a.content) : null;
|
|
103
|
+
if (hex && chromatic(hex))
|
|
104
|
+
v.colors.push({ role: "primary", hex, w: 0.5 });
|
|
105
|
+
else if (hex)
|
|
106
|
+
v.colors.push({ role: "background", hex, w: 0.3 });
|
|
107
|
+
}
|
|
108
|
+
if (key === "og:image") {
|
|
109
|
+
const img = abs(a.content, base);
|
|
110
|
+
if (img)
|
|
111
|
+
v.logos.push({ url: img, kind: "og", w: 0.1 });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (a.style && (name === "body" || name === "html"))
|
|
115
|
+
css.push(`body{${a.style}}`);
|
|
116
|
+
else if (a.style && (name === "button" || /\b(btn|button|cta)\b/i.test(hint)))
|
|
117
|
+
css.push(`.btn{${a.style}}`);
|
|
118
|
+
},
|
|
119
|
+
ontext(text) {
|
|
120
|
+
if (style)
|
|
121
|
+
style.push(text);
|
|
122
|
+
},
|
|
123
|
+
onclosetag(name) {
|
|
124
|
+
if (name === "style" && style) {
|
|
125
|
+
css.push(style.join(""));
|
|
126
|
+
style = null;
|
|
127
|
+
}
|
|
128
|
+
if (name === "svg") {
|
|
129
|
+
if (svg && svgDepth === 1) {
|
|
130
|
+
const markup = html.slice(svg.start, parser.endIndex + 1);
|
|
131
|
+
if (markup.length > 80 && markup.length < SVG_MAX)
|
|
132
|
+
v.logos.push({
|
|
133
|
+
url: `data:image/svg+xml;base64,${Buffer.from(markup).toString("base64")}`,
|
|
134
|
+
kind: "svg",
|
|
135
|
+
w: svg.w,
|
|
136
|
+
});
|
|
137
|
+
svg = null;
|
|
138
|
+
}
|
|
139
|
+
svgDepth = Math.max(0, svgDepth - 1);
|
|
140
|
+
}
|
|
141
|
+
// implied closes: pop to the tag that closed
|
|
142
|
+
const at = stack.map((s) => s.name).lastIndexOf(name);
|
|
143
|
+
if (at < 0)
|
|
144
|
+
return;
|
|
145
|
+
for (const s of stack.splice(at)) {
|
|
146
|
+
if (s.top)
|
|
147
|
+
top--;
|
|
148
|
+
if (s.home)
|
|
149
|
+
home--;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
}, { decodeEntities: true, lowerCaseAttributeNames: true });
|
|
153
|
+
try {
|
|
154
|
+
parser.write(html);
|
|
155
|
+
parser.end();
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// a broken page keeps what was read
|
|
159
|
+
}
|
|
160
|
+
return { sheets, css };
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/brand/page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Votes to a brand. Per role, the value with the most weight wins. Confidence is how much evidence
|
|
3
|
+
* the winner has (capped at 1) times its share of its role. Primary and accent must have hue and
|
|
4
|
+
* differ; with no role votes they fall back to the most used hued colours.
|
|
5
|
+
*/
|
|
6
|
+
import { type LogoKind, type Votes } from "./votes.js";
|
|
7
|
+
export interface Scored {
|
|
8
|
+
logo: {
|
|
9
|
+
url: string;
|
|
10
|
+
kind: LogoKind;
|
|
11
|
+
} | null;
|
|
12
|
+
palette: {
|
|
13
|
+
primary: string | null;
|
|
14
|
+
accent: string | null;
|
|
15
|
+
background: string | null;
|
|
16
|
+
text: string | null;
|
|
17
|
+
};
|
|
18
|
+
fonts: {
|
|
19
|
+
heading: string | null;
|
|
20
|
+
body: string | null;
|
|
21
|
+
};
|
|
22
|
+
/** 0 to 1 each. */
|
|
23
|
+
confidence: {
|
|
24
|
+
logo: number;
|
|
25
|
+
palette: number;
|
|
26
|
+
fonts: number;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export declare function score(v: Votes): Scored;
|
|
30
|
+
/** Thin: worth a browser. No logo or a weak one, no hued primary, or no body font. */
|
|
31
|
+
export declare const thin: (s: Scored) => boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Votes to a brand. Per role, the value with the most weight wins. Confidence is how much evidence
|
|
3
|
+
* the winner has (capped at 1) times its share of its role. Primary and accent must have hue and
|
|
4
|
+
* differ; with no role votes they fall back to the most used hued colours.
|
|
5
|
+
*/
|
|
6
|
+
import { apart, chromatic, } from "./votes.js";
|
|
7
|
+
function rank(items, key) {
|
|
8
|
+
const sums = new Map();
|
|
9
|
+
let total = 0;
|
|
10
|
+
for (const t of items) {
|
|
11
|
+
const k = key(t);
|
|
12
|
+
sums.set(k, (sums.get(k) ?? 0) + t.w);
|
|
13
|
+
total += t.w;
|
|
14
|
+
}
|
|
15
|
+
return [...sums]
|
|
16
|
+
.map(([value, w]) => ({ value, w, conf: Math.min(1, w) * (total ? w / total : 0) }))
|
|
17
|
+
.sort((a, b) => b.w - a.w);
|
|
18
|
+
}
|
|
19
|
+
const colors = (v, role) => rank(v.colors.filter((c) => c.role === role), (c) => c.hex);
|
|
20
|
+
const fonts = (v, role) => rank(v.fonts.filter((f) => f.role === role), (f) => f.family.toLowerCase());
|
|
21
|
+
/** Shown names keep the page's casing: the first spelling seen. */
|
|
22
|
+
const spelled = (v, lower) => lower ? (v.fonts.find((f) => f.family.toLowerCase() === lower)?.family ?? lower) : null;
|
|
23
|
+
const round = (n) => Math.round(n * 100) / 100;
|
|
24
|
+
export function score(v) {
|
|
25
|
+
const background = colors(v, "background")[0] ?? null;
|
|
26
|
+
// an unset page background is the browser's white
|
|
27
|
+
const ground = background?.value ?? "#ffffff";
|
|
28
|
+
const text = colors(v, "text").find((c) => apart(c.value, ground)) ?? null;
|
|
29
|
+
// counted colours: a weak fallback, so their confidence is cut
|
|
30
|
+
const hued = colors(v, "any")
|
|
31
|
+
.filter((c) => chromatic(c.value))
|
|
32
|
+
.map((c) => ({ ...c, conf: c.conf * 0.5 }));
|
|
33
|
+
const ok = (c) => apart(c.value, ground);
|
|
34
|
+
const primary = [...colors(v, "primary"), ...hued].find(ok) ?? null;
|
|
35
|
+
const notPrimary = (c) => ok(c) && (!primary || apart(c.value, primary.value));
|
|
36
|
+
const accent = [...colors(v, "accent"), ...colors(v, "primary"), ...hued]
|
|
37
|
+
.filter(chromaticOf)
|
|
38
|
+
.find(notPrimary) ?? null;
|
|
39
|
+
const body = fonts(v, "body")[0] ?? fonts(v, "any")[0] ?? null;
|
|
40
|
+
const heading = fonts(v, "heading")[0] ?? body;
|
|
41
|
+
const logo = [...v.logos].sort((a, b) => b.w - a.w || kindRank(a.kind) - kindRank(b.kind))[0] ?? null;
|
|
42
|
+
const conf = (c) => c?.conf ?? 0;
|
|
43
|
+
return {
|
|
44
|
+
logo: logo ? { url: logo.url, kind: logo.kind } : null,
|
|
45
|
+
palette: {
|
|
46
|
+
primary: primary?.value ?? null,
|
|
47
|
+
accent: accent?.value ?? null,
|
|
48
|
+
background: background?.value ?? null,
|
|
49
|
+
text: text?.value ?? null,
|
|
50
|
+
},
|
|
51
|
+
fonts: { heading: spelled(v, heading?.value), body: spelled(v, body?.value) },
|
|
52
|
+
confidence: {
|
|
53
|
+
logo: round(logo?.w ?? 0),
|
|
54
|
+
palette: round(0.6 * conf(primary) + 0.15 * conf(background) + 0.15 * conf(text) + 0.1 * conf(accent)),
|
|
55
|
+
fonts: round(0.6 * conf(body) + 0.4 * conf(heading)),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const chromaticOf = (c) => chromatic(c.value);
|
|
60
|
+
const kindRank = (k) => ["svg", "img", "icon", "og"].indexOf(k);
|
|
61
|
+
/** Thin: worth a browser. No logo or a weak one, no hued primary, or no body font. */
|
|
62
|
+
export const thin = (s) => s.confidence.logo < 0.5 || !s.palette.primary || s.confidence.palette < 0.3 || !s.fonts.body;
|
|
63
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/brand/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand votes from CSS (postcss): custom properties on the root by name (`--primary`, `--font-heading`),
|
|
3
|
+
* then body, headings, buttons and links by selector, then every colour and family by count.
|
|
4
|
+
* Dark-mode and print rules are skipped: the page as most first see it.
|
|
5
|
+
*/
|
|
6
|
+
import { type Root } from "postcss";
|
|
7
|
+
import { type Votes } from "./votes.js";
|
|
8
|
+
/** One stylesheet's tree; a sheet postcss cannot read gives none. */
|
|
9
|
+
export declare function parseSheet(css: string): Root | null;
|
|
10
|
+
/** `@import` targets of a sheet, for one more level of fetches. */
|
|
11
|
+
export declare function importsOf(root: Root): string[];
|
|
12
|
+
/** Root custom properties across every sheet; the first one set wins, as on a light page. */
|
|
13
|
+
export declare function varsOf(roots: readonly Root[]): Map<string, string>;
|
|
14
|
+
/** `var()` filled from the root, five levels deep. */
|
|
15
|
+
export declare function resolve(value: string, vars: ReadonlyMap<string, string>): string;
|
|
16
|
+
/** Votes from the root's custom properties, by their names. */
|
|
17
|
+
export declare function voteVars(vars: ReadonlyMap<string, string>, v: Votes): void;
|
|
18
|
+
/** Votes from one sheet's rules, `var()` filled. `@font-face` families count as chosen fonts. */
|
|
19
|
+
export declare function voteSheet(root: Root, vars: ReadonlyMap<string, string>, v: Votes): void;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand votes from CSS (postcss): custom properties on the root by name (`--primary`, `--font-heading`),
|
|
3
|
+
* then body, headings, buttons and links by selector, then every colour and family by count.
|
|
4
|
+
* Dark-mode and print rules are skipped: the page as most first see it.
|
|
5
|
+
*/
|
|
6
|
+
import postcss, {} from "postcss";
|
|
7
|
+
import { chromatic, colorsIn, fontOf, hexOf } from "./votes.js";
|
|
8
|
+
const ROOT = /^(:root|html|body|:host|\[data-theme=["']?light["']?\]|\.light)$/;
|
|
9
|
+
const BODY = /^(html|body|:root)$/;
|
|
10
|
+
const HEAD = /(^|\s)(h1|h2|h3|\.h1|\.h2|\.heading|\.display|\.title)([.:[\s]|$)/;
|
|
11
|
+
const BUTTON = /(^|[\s.#[_-])(btn|button|cta)([\s.:[#_-]|$)/;
|
|
12
|
+
const BUTTON_NOT = /:hover|:focus|:active|:disabled|disabled|outline|secondary|ghost|link|close|toggle|light|white|dark|transparent|text|components-|wp-admin|admin-bar/;
|
|
13
|
+
const LINK = /^a(:link|:visited)?$/;
|
|
14
|
+
const DARK = /prefers-color-scheme:\s*dark|print/;
|
|
15
|
+
const DARK_SEL = /\.dark\b|data-theme=["']?dark|\.theme-dark|data-bs-theme=["']?dark/;
|
|
16
|
+
/** One stylesheet's tree; a sheet postcss cannot read gives none. */
|
|
17
|
+
export function parseSheet(css) {
|
|
18
|
+
try {
|
|
19
|
+
return postcss.parse(css);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** `@import` targets of a sheet, for one more level of fetches. */
|
|
26
|
+
export function importsOf(root) {
|
|
27
|
+
const out = [];
|
|
28
|
+
root.walkAtRules("import", (a) => {
|
|
29
|
+
const m = a.params.match(/url\(\s*["']?([^"')]+)|^["']([^"']+)/);
|
|
30
|
+
const href = m?.[1] ?? m?.[2];
|
|
31
|
+
if (href && !/print/.test(a.params))
|
|
32
|
+
out.push(href);
|
|
33
|
+
});
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
const hidden = (node) => {
|
|
37
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
38
|
+
if (p.type === "atrule" && DARK.test(p.params))
|
|
39
|
+
return true;
|
|
40
|
+
if (p.type === "rule" && DARK_SEL.test(p.selector))
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
};
|
|
45
|
+
const selectorsOf = (node) => node.parent?.type === "rule"
|
|
46
|
+
? node.parent.selector
|
|
47
|
+
.toLowerCase()
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((s) => s.trim())
|
|
50
|
+
: [];
|
|
51
|
+
/** Root custom properties across every sheet; the first one set wins, as on a light page. */
|
|
52
|
+
export function varsOf(roots) {
|
|
53
|
+
const vars = new Map();
|
|
54
|
+
for (const root of roots)
|
|
55
|
+
root.walkDecls(/^--/, (d) => {
|
|
56
|
+
if (hidden(d) || vars.has(d.prop))
|
|
57
|
+
return;
|
|
58
|
+
if (selectorsOf(d).some((s) => ROOT.test(s)))
|
|
59
|
+
vars.set(d.prop, d.value.trim());
|
|
60
|
+
});
|
|
61
|
+
return vars;
|
|
62
|
+
}
|
|
63
|
+
const VAR = /var\(\s*(--[\w-]+)\s*(?:,\s*((?:[^()]|\([^()]*\))*))?\)/g;
|
|
64
|
+
/** `var()` filled from the root, five levels deep. */
|
|
65
|
+
export function resolve(value, vars) {
|
|
66
|
+
let out = value;
|
|
67
|
+
for (let i = 0; i < 5 && out.includes("var("); i++)
|
|
68
|
+
out = out.replace(VAR, (_, name, fallback) => vars.get(name) ?? fallback ?? "");
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/** A custom property's colour: a colour, or bare HSL channels as shadcn sets (`221 83% 53%`). */
|
|
72
|
+
const varColor = (v) => hexOf(v) ?? (/^[\d.]+(deg)?\s+[\d.]+%\s+[\d.]+%$/.test(v) ? hexOf(`hsl(${v})`) : null);
|
|
73
|
+
/** Palette names only under a bare name or a theme's own prefix: design systems name hundreds of roles. */
|
|
74
|
+
const NS = "(?:(?:color|colors|clr|c|bs|theme|site|global|brand|wp--preset--color-|chakra-colors|mantine-color|md-sys-color|ifm-color)-)?";
|
|
75
|
+
const VAR_ROLES = [
|
|
76
|
+
[new RegExp(`^--${NS}(primary|brand|main)(-color)?$`), "primary", 0.6],
|
|
77
|
+
[new RegExp(`^--${NS}(accent|secondary)(-color)?$`), "accent", 0.45],
|
|
78
|
+
[new RegExp(`^--${NS}(background|bg|body-bg|page-bg)(-color)?$`), "background", 0.5],
|
|
79
|
+
[new RegExp(`^--${NS}(foreground|text|body-color|fg|ink)(-color)?$`), "text", 0.5],
|
|
80
|
+
];
|
|
81
|
+
const FONT_VARS = [
|
|
82
|
+
[
|
|
83
|
+
/^--([\w-]*-)?font-?(heading|display|title|headings)$|^--([\w-]*-)?(heading|display)-font$/,
|
|
84
|
+
"heading",
|
|
85
|
+
],
|
|
86
|
+
[/^--([\w-]*-)?font-?(sans|body|base|text|primary)$|^--([\w-]*-)?(body|base)-font$/, "body"],
|
|
87
|
+
];
|
|
88
|
+
/** Votes from the root's custom properties, by their names. */
|
|
89
|
+
export function voteVars(vars, v) {
|
|
90
|
+
for (const [name, raw] of vars) {
|
|
91
|
+
const value = resolve(raw, vars);
|
|
92
|
+
const fr = FONT_VARS.find(([re]) => re.test(name));
|
|
93
|
+
if (fr) {
|
|
94
|
+
const family = fontOf(value);
|
|
95
|
+
if (family)
|
|
96
|
+
v.fonts.push({ role: fr[1], family, w: 0.5 });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const hit = VAR_ROLES.find(([re]) => re.test(name));
|
|
100
|
+
const hex = hit ? varColor(value) : null;
|
|
101
|
+
if (!hit || !hex)
|
|
102
|
+
continue;
|
|
103
|
+
const [, role, w] = hit;
|
|
104
|
+
if ((role === "primary" || role === "accent") && !chromatic(hex))
|
|
105
|
+
continue;
|
|
106
|
+
v.colors.push({ role, hex, w });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Votes from one sheet's rules, `var()` filled. `@font-face` families count as chosen fonts. */
|
|
110
|
+
export function voteSheet(root, vars, v) {
|
|
111
|
+
root.walkAtRules("font-face", (a) => {
|
|
112
|
+
a.walkDecls("font-family", (d) => {
|
|
113
|
+
const family = fontOf(d.value);
|
|
114
|
+
if (family)
|
|
115
|
+
v.fonts.push({ role: "any", family, w: 0.15 });
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
root.walkDecls((d) => {
|
|
119
|
+
if (d.prop.startsWith("--") || hidden(d) || d.parent?.type !== "rule")
|
|
120
|
+
return;
|
|
121
|
+
const prop = d.prop.toLowerCase();
|
|
122
|
+
const value = resolve(d.value, vars);
|
|
123
|
+
const sels = selectorsOf(d);
|
|
124
|
+
const body = sels.some((s) => BODY.test(s));
|
|
125
|
+
const head = sels.some((s) => HEAD.test(s));
|
|
126
|
+
const button = sels.some((s) => BUTTON.test(s) && !BUTTON_NOT.test(s));
|
|
127
|
+
const link = sels.some((s) => LINK.test(s));
|
|
128
|
+
if (prop === "font-family" || prop === "font") {
|
|
129
|
+
const family = fontOf(value, prop === "font");
|
|
130
|
+
if (!family)
|
|
131
|
+
return;
|
|
132
|
+
if (body)
|
|
133
|
+
v.fonts.push({ role: "body", family, w: 0.8 });
|
|
134
|
+
if (head)
|
|
135
|
+
v.fonts.push({ role: "heading", family, w: 0.7 });
|
|
136
|
+
v.fonts.push({ role: "any", family, w: 0.05 });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const colors = colorsIn(value);
|
|
140
|
+
const [first] = colors;
|
|
141
|
+
if (!first)
|
|
142
|
+
return;
|
|
143
|
+
const bg = prop === "background" || prop === "background-color";
|
|
144
|
+
if (bg && body)
|
|
145
|
+
v.colors.push({ role: "background", hex: first, w: 0.7 });
|
|
146
|
+
if (prop === "color" && body)
|
|
147
|
+
v.colors.push({ role: "text", hex: first, w: 0.7 });
|
|
148
|
+
if (prop === "color" && head)
|
|
149
|
+
v.colors.push({ role: "text", hex: first, w: 0.2 });
|
|
150
|
+
if (bg && button)
|
|
151
|
+
v.colors.push({ role: "primary", hex: first, w: 0.5 });
|
|
152
|
+
if (prop === "color" && link && chromatic(first))
|
|
153
|
+
v.colors.push({ role: "accent", hex: first, w: 0.35 });
|
|
154
|
+
for (const hex of colors)
|
|
155
|
+
v.colors.push({ role: "any", hex, w: 0.03 });
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=sheet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sheet.js","sourceRoot":"","sources":["../../src/brand/sheet.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ColorRole = "primary" | "accent" | "background" | "text" | "any";
|
|
2
|
+
export type FontRole = "heading" | "body" | "any";
|
|
3
|
+
/** `svg`: inline in the page (a data URI); `img`: an image tag; `icon`: a favicon; `og`: the share image. */
|
|
4
|
+
export type LogoKind = "svg" | "img" | "icon" | "og";
|
|
5
|
+
export interface Votes {
|
|
6
|
+
colors: {
|
|
7
|
+
role: ColorRole;
|
|
8
|
+
hex: string;
|
|
9
|
+
w: number;
|
|
10
|
+
}[];
|
|
11
|
+
fonts: {
|
|
12
|
+
role: FontRole;
|
|
13
|
+
family: string;
|
|
14
|
+
w: number;
|
|
15
|
+
}[];
|
|
16
|
+
logos: {
|
|
17
|
+
url: string;
|
|
18
|
+
kind: LogoKind;
|
|
19
|
+
w: number;
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
export declare const votesOf: () => Votes;
|
|
23
|
+
/** One CSS colour as #rrggbb; null for see-through (alpha under 0.5) or not a colour. */
|
|
24
|
+
export declare function hexOf(value: string): string | null;
|
|
25
|
+
/** Every colour in a declaration's value: `1px solid #ccc`, a gradient's stops. */
|
|
26
|
+
export declare function colorsIn(value: string): string[];
|
|
27
|
+
/** Has hue: not a grey, not near white or black. A brand's primary is one. */
|
|
28
|
+
export declare function chromatic(hex: string): boolean;
|
|
29
|
+
/** Two colours a reader tells apart. */
|
|
30
|
+
export declare const apart: (a: string, b: string) => boolean;
|
|
31
|
+
/** The first named family of a `font-family` or `font` value; null for generic or icon stacks. */
|
|
32
|
+
export declare function fontOf(value: string, shorthand?: boolean): string | null;
|
|
33
|
+
/** An href against the page; only http(s) and data images. */
|
|
34
|
+
export declare function abs(href: string | undefined, base: string): string | null;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Evidence for a brand: each colour, font and logo a page or a browser shows, with a weight for how
|
|
3
|
+
* directly it names the brand. Every reader (HTML, CSS, a browser's computed styles) writes votes;
|
|
4
|
+
* `score` alone decides.
|
|
5
|
+
*/
|
|
6
|
+
import { colorsNamed, differenceEuclidean, formatHex, oklch, parse } from "culori";
|
|
7
|
+
export const votesOf = () => ({ colors: [], fonts: [], logos: [] });
|
|
8
|
+
/** One CSS colour as #rrggbb; null for see-through (alpha under 0.5) or not a colour. */
|
|
9
|
+
export function hexOf(value) {
|
|
10
|
+
const c = parse(value.trim());
|
|
11
|
+
if (!c || (c.alpha ?? 1) < 0.5)
|
|
12
|
+
return null;
|
|
13
|
+
return formatHex(c) ?? null;
|
|
14
|
+
}
|
|
15
|
+
const NAMED = new Set(Object.keys(colorsNamed));
|
|
16
|
+
const TOKEN = /#[0-9a-f]{3,8}\b|\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color)\([^()]*\)|\b[a-z]{3,20}\b/gi;
|
|
17
|
+
/** Every colour in a declaration's value: `1px solid #ccc`, a gradient's stops. */
|
|
18
|
+
export function colorsIn(value) {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const [t] of value.matchAll(TOKEN)) {
|
|
21
|
+
if (/^[a-z]+$/i.test(t) && !NAMED.has(t.toLowerCase()))
|
|
22
|
+
continue;
|
|
23
|
+
const hex = hexOf(t);
|
|
24
|
+
if (hex)
|
|
25
|
+
out.push(hex);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
/** Has hue: not a grey, not near white or black. A brand's primary is one. */
|
|
30
|
+
export function chromatic(hex) {
|
|
31
|
+
const c = oklch(hex);
|
|
32
|
+
return !!c && (c.c ?? 0) > 0.05 && c.l > 0.15 && c.l < 0.96;
|
|
33
|
+
}
|
|
34
|
+
const distance = differenceEuclidean("oklab");
|
|
35
|
+
/** Two colours a reader tells apart. */
|
|
36
|
+
export const apart = (a, b) => distance(a, b) > 0.08;
|
|
37
|
+
const GENERIC = new Set([
|
|
38
|
+
"serif",
|
|
39
|
+
"sans-serif",
|
|
40
|
+
"monospace",
|
|
41
|
+
"cursive",
|
|
42
|
+
"fantasy",
|
|
43
|
+
"system-ui",
|
|
44
|
+
"ui-sans-serif",
|
|
45
|
+
"ui-serif",
|
|
46
|
+
"ui-monospace",
|
|
47
|
+
"ui-rounded",
|
|
48
|
+
"-apple-system",
|
|
49
|
+
"blinkmacsystemfont",
|
|
50
|
+
"inherit",
|
|
51
|
+
"initial",
|
|
52
|
+
"unset",
|
|
53
|
+
"revert",
|
|
54
|
+
"emoji",
|
|
55
|
+
"math",
|
|
56
|
+
"fangsong",
|
|
57
|
+
"apple color emoji",
|
|
58
|
+
"segoe ui emoji",
|
|
59
|
+
"segoe ui symbol",
|
|
60
|
+
"noto color emoji",
|
|
61
|
+
]);
|
|
62
|
+
const ICON_FONT = /icon|awesome|glyph|material symbols|dashicons|eicons|swiper|slick|fontello|feather|lucide/i;
|
|
63
|
+
/** `font: 600 16px/1.5 Inter, sans-serif`: the families after the size. */
|
|
64
|
+
const SHORTHAND = /(?:^|\s)[\d.]+(?:px|r?em|%|pt|vw|vh|ch)(?:\s*\/\s*\S+)?\s+(.+)$/;
|
|
65
|
+
/** The first named family of a `font-family` or `font` value; null for generic or icon stacks. */
|
|
66
|
+
export function fontOf(value, shorthand = false) {
|
|
67
|
+
let list = value.replace(/!important/i, "");
|
|
68
|
+
if (shorthand) {
|
|
69
|
+
const m = list.match(SHORTHAND);
|
|
70
|
+
if (!m?.[1])
|
|
71
|
+
return null;
|
|
72
|
+
list = m[1];
|
|
73
|
+
}
|
|
74
|
+
for (const raw of list.split(",")) {
|
|
75
|
+
const name = raw
|
|
76
|
+
.trim()
|
|
77
|
+
.replace(/^["']|["']$/g, "")
|
|
78
|
+
.trim();
|
|
79
|
+
if (!name || name.includes("("))
|
|
80
|
+
continue;
|
|
81
|
+
if (GENERIC.has(name.toLowerCase()))
|
|
82
|
+
continue;
|
|
83
|
+
return ICON_FONT.test(name) ? null : name;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/** An href against the page; only http(s) and data images. */
|
|
88
|
+
export function abs(href, base) {
|
|
89
|
+
if (!href)
|
|
90
|
+
return null;
|
|
91
|
+
try {
|
|
92
|
+
const u = new URL(href.trim(), base);
|
|
93
|
+
if (u.protocol === "http:" || u.protocol === "https:")
|
|
94
|
+
return u.href;
|
|
95
|
+
return u.protocol === "data:" && /^data:image\/(svg|png|webp|jpe?g)/i.test(u.href)
|
|
96
|
+
? u.href
|
|
97
|
+
: null;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=votes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"votes.js","sourceRoot":"","sources":["../../src/brand/votes.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* read: the web and files in, text out.
|
|
2
|
+
* read: the web and files in, text out. Five doors: `./brand` (a site's logo, palette and fonts), `./fetch` (polite HTTP, robots, the fetch
|
|
3
3
|
* ladder up to a browser), `./text` (HTML and PDF to text, a page to title, text and links),
|
|
4
4
|
* `./video` (a video or podcast from a URL or a file: captions, `watch`, audio to markdown, what a
|
|
5
5
|
* channel or search holds) and `./study` (claims that must quote a page, drafts that must cite a
|
|
6
6
|
* claim, the report). Every model call goes through `@wrenautomation/models`. No database here.
|
|
7
7
|
*/
|
|
8
|
+
export * from "./brand/index.js";
|
|
8
9
|
export * from "./fetch/index.js";
|
|
9
10
|
export * from "./study/index.js";
|
|
10
11
|
export * from "./text/index.js";
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* read: the web and files in, text out.
|
|
2
|
+
* read: the web and files in, text out. Five doors: `./brand` (a site's logo, palette and fonts), `./fetch` (polite HTTP, robots, the fetch
|
|
3
3
|
* ladder up to a browser), `./text` (HTML and PDF to text, a page to title, text and links),
|
|
4
4
|
* `./video` (a video or podcast from a URL or a file: captions, `watch`, audio to markdown, what a
|
|
5
5
|
* channel or search holds) and `./study` (claims that must quote a page, drafts that must cite a
|
|
6
6
|
* claim, the report). Every model call goes through `@wrenautomation/models`. No database here.
|
|
7
7
|
*/
|
|
8
|
+
export * from "./brand/index.js";
|
|
8
9
|
export * from "./fetch/index.js";
|
|
9
10
|
export * from "./study/index.js";
|
|
10
11
|
export * from "./text/index.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrenautomation/read",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The web and files in, text out. Polite HTTP fetching with robots and a fetch ladder up to a browser; HTML and PDF to text, a page to title, text and links; a video or podcast from a URL or a file (captions, ffmpeg and a vision model, audio to markdown, what a channel or search holds); a study's synthesis reader whose every claim quotes a page.",
|
|
3
|
+
"version": "0.43.0",
|
|
4
|
+
"description": "The web and files in, text out. Polite HTTP fetching with robots and a fetch ladder up to a browser; HTML and PDF to text, a page to title, text and links; a video or podcast from a URL or a file (captions, ffmpeg and a vision model, audio to markdown, what a channel or search holds); a site's brand (logo, palette, fonts); a study's synthesis reader whose every claim quotes a page.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/wrenautomation/engine.git",
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
"types": "./dist/index.d.ts",
|
|
24
24
|
"import": "./dist/index.js"
|
|
25
25
|
},
|
|
26
|
+
"./brand": {
|
|
27
|
+
"types": "./dist/brand/index.d.ts",
|
|
28
|
+
"import": "./dist/brand/index.js"
|
|
29
|
+
},
|
|
26
30
|
"./fetch": {
|
|
27
31
|
"types": "./dist/fetch/index.d.ts",
|
|
28
32
|
"import": "./dist/fetch/index.js"
|
|
@@ -44,12 +48,17 @@
|
|
|
44
48
|
"node": ">=24"
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
51
|
+
"culori": "4.0.2",
|
|
47
52
|
"entities": "8.1.0",
|
|
48
53
|
"htmlparser2": "12.0.0",
|
|
54
|
+
"postcss": "8.5.32",
|
|
49
55
|
"robots-parser": "3.0.1",
|
|
50
56
|
"unpdf": "1.8.1",
|
|
51
57
|
"zod": "4.6.5",
|
|
52
|
-
"@wrenautomation/models": "0.
|
|
58
|
+
"@wrenautomation/models": "0.43.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@types/culori": "4.0.1"
|
|
53
62
|
},
|
|
54
63
|
"scripts": {
|
|
55
64
|
"typecheck": "tsc --noEmit",
|