Commit Graph

4 Commits

Author SHA1 Message Date
76d0906564 Add furst-serve, a local reader server
Following a link inside an extracted article used to drop the reader back
onto the live site. The server rewrites in-page links to /read?u=... so
browsing stays in reader mode, and caches rendered pages on disk, which
takes a revisit from ~350ms to under a millisecond.

The HTTP layer is blocking and hand-rolled: GET only, one response per
connection, a fixed pool of four worker threads so memory stays
predictable, and nothing but the loopback interface is ever bound.

Routes so far:

  /            home, from ~/.config/furst/home.toml
  /read?u=     the article, with links routed back through the reader
  /go?u=       hand the original URL to the heavy browser

Pages that are not documents redirect to the original rather than being
run through an article extractor, and pages with too little text to be an
article say so and offer the escape hatches. Redirect stubs are followed
before that judgement is made.

furst-serve --open ensures a server is running, starting a detached one if
needed, then execs a browser at the reader URL. Cache entries carry a
schema number so a renderer change drops them rather than serving stale
markup.
2026-09-06 19:55:59 +02:00
af9723714d Make furst-read a library and teach the renderer link rewriting
furst-serve needs the fetch, extract and render stages, so they move behind
a lib target with the CLI as a thin consumer.

Adds Options::link_prefix. When set, http and https links are rewritten to
{prefix}{percent-encoded url} so that following one stays inside the reader
rather than dropping the viewer back onto the live site; other schemes are
left alone. render::body exposes the article markup without the standalone
document wrapper, for callers supplying their own page chrome, and CSS
becomes public so they can reuse it.

Percent encode and decode live in urljoin, next to the other URL handling.
2026-09-06 19:50:03 +02:00
6dcb00da80 Add furst-read, an article extractor and minimal renderer
Fetches a page, discards everything that is not the article, and renders
what is left as a self-contained HTML document with no scripts, no
stylesheets, no webfonts and no third-party requests. The reader rule in
the starter config already points at it.

Extraction follows Readability: score p/pre/td/blockquote by prose weight,
propagate to ancestors with decay, adjust by class and id names, scale by
one minus link density, then take the winner plus sibling nodes that also
read like body copy. Serialisation runs against a tag whitelist, with
unlisted elements contributing their children but no tag of their own, so
wrapper divs disappear.

Handling for what real pages actually do:

- follow meta http-equiv=refresh stubs, including inside noscript
- fall back to data-src when src holds a lazy-load placeholder
- take the first srcset candidate, the smallest, not the last
- decode via Content-Type charset, then meta charset, then UTF-8
- resolve links against the post-redirect URL so file:// output works
- ignore script and style text so a page cannot inflate its own score

Output goes to a cache file named by a hash of the URL and opens in
$FURST_BROWSER, $BROWSER, or the first light browser on $PATH; --html,
--text, --out and --stdin cover the other uses.

Restructures the repository as a workspace so the router keeps its two
dependencies and its fast build.
2026-09-06 19:35:30 +02:00
ec79c07b0f Add furst, a URL router for low-end hardware
Sits where the default browser used to and dispatches each URL to the
cheapest tool that can handle it: mpv for video, zathura for PDFs, a light
WebKit browser for reading, Firefox only when nothing else will do.

Rules live in ~/.config/furst/rules.toml and are matched in order. When a
rule's command is missing from $PATH the router falls through to the next
match, then to the default, so a config may name tools that do not exist
yet without breaking today.

- host patterns match apex plus subdomains, with = for exact and * for any
- paths glob case-insensitively, contains matches the raw URL
- {url} {url_enc} {host} {path} {scheme} placeholders, URL appended if unused
- terminal = true wraps a handler in $TERMINAL -e for TUI tools
- --explain shows the parse and every candidate in priority order
- --install registers a .desktop entry as the system default browser

Host parsing strips userinfo with rfind('@') so that
https://bank.example@evil.example/ routes on evil.example. Handlers are
exec'd rather than forked, leaving no process behind.
2026-09-06 19:18:13 +02:00