Expand description
Noctiluca, a terminal web browser on intuiTUIve.
Chrome loads and runs the page. Noctiluca reads what Chrome laid out
(one DOMSnapshot.captureSnapshot and the accessibility tree, which the
page cannot observe) and draws it in cells: text as terminal text,
controls as intuiTUIve widgets, images as media regions. This crate is
the noctiluca binary and the library it is built from.
The user guide is on the docs site: Noctiluca, with pages on
getting started, keys, configuration,
privacy and security and using it as a library.
The design and its phases are in docs/design/browser.md.
§The pieces
engine: theEnginetrait, what Noctiluca needs from a browser, andcdpits Chrome backend over rs-rich-embed (ChromeBackend).tab: aTabruns an engine on a thread of its own: loading, the debounced change ping, live logs read incrementally, guarded actions, stills and pictures. It sendsUpdates.model: thePagemodel, rebuilt from each snapshot: blocks of text, controls and media.layout: the page as lines of cells, in reader mode or the projection;adaptits terminal adaptations, named generic rules that can each be switched off;picturesdraws pictures and moving GIFs over their regions.widgetsgives controls their intuiTUIve widgets.app: the browser as an intuiTUIve app;actionsis the one list of what it can do, with the keys of both presets;pixel_viewis the page in Chrome’s own pixels, sent as tiles where they change (#818).control: the control socket’s verbs and thenoctiluca lsandnoctiluca actionclient, for scripts and coding agents (#816).config,bookmarks,args,dump(--dump) andurl: the rest of the binary, usable on their own.testing:FakeEngine, an engine over saved snapshots, for tests with no browser.
Everything the page says is untrusted: control characters are stripped
(clean()) before any of it reaches the terminal.
§Examples
The address bar’s rules, from a config:
use noctiluca::config::{Config, Destination};
let config = Config::parse(
r#"
[shortcuts]
wiki = "https://en.wikipedia.org/w/index.php?search={}"
"#,
)
.unwrap();
assert!(config.warnings.is_empty());
assert_eq!(
config.destination("wiki sea sparkle", "about:blank", &[]),
Destination::Keyword("https://en.wikipedia.org/w/index.php?search=sea+sparkle".into())
);
assert_eq!(
config.destination("docs.rs/serde", "about:blank", &[]),
Destination::Url("https://docs.rs/serde".into())
);A page read once through an engine, as --dump does. Here the engine
is FakeEngine, over the snapshots of the
crate’s test pages, so no browser runs:
use std::path::Path;
use std::time::Duration;
use noctiluca::testing::FakeEngine;
let snapshots = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/snapshots");
let engine = FakeEngine::from_dir(&snapshots)?;
let page = noctiluca::dump::read_page(
Box::new(engine),
"https://fixture.example/second.html",
Duration::from_secs(10),
)?;
assert_eq!(page.title, "Second page");
assert!(page.text().contains("You followed the link."));
let link = page.find("link", "Back to the start");
assert_eq!(link.len(), 1);The same with Chrome (it needs a Chrome or Chromium, and a normal user):
use std::time::Duration;
use noctiluca::cdp::{ChromeBackend, ChromeOptions};
let engine = ChromeBackend::start(ChromeOptions {
columns: 100,
rows: 40,
..ChromeOptions::default()
})?;
let page = noctiluca::dump::read_page(
Box::new(engine),
"https://example.com",
Duration::from_secs(60),
)?;
println!("{}", page.text());Re-exports§
Modules§
- about
about:noctiluca: what this browser is, how it adapts pages to the terminal, and what it can play.- actions
- The action registry: one list of what Noctiluca can do. The keys, the link hints, site keys and the F1 overlay all run actions from it, and each checks the action is available when it runs.
- adapt
- Terminal adaptations: named, generic rules about constructs and cells,
applied to every page alike (
docs/design/browser.md, “Terminal adaptations”). None of them knows a site: each reads only what the model has from the snapshot and the accessibility tree. - app
- The browser as an intuiTUIve app: the address line, the page (the projection by default, reader mode one key away), the status line, and the action registry’s keys, hints, find, F1 overlay and site keys.
- args
noctiluca’s command line, in the hand-written style ofrs-rich-cli’s commands (fn parse_args(&[String]) -> Result<…, String>).- bars
- The header and the status bar, as the PoC drew them:
- bookmarks
- Bookmarks, in a file of Noctiluca’s own (decision 4):
bookmarks.tomlin its data directory, written only when the user adds or removes one. Each is a[[bookmark]]with aurl, atitleand an optionalkeyword, which the address bar expands like a shortcut’s. - cdp
ChromeBackend: theEngineover rs-rich-embed’sChromeEngine, speaking the DevTools protocol throughDevTools::callandDevTools::listen.- clean
- Page text made safe for a terminal. Everything a page says is untrusted: its text, title, addresses, alt text, option labels and download names all pass through here before any of it is drawn.
- config
- Noctiluca’s configuration:
noctiluca/config.tomlin the platform config directory, or the file--confignames. A documented sample isconfig.sample.tomlbeside this crate’s manifest. - control
- Agent control (#816): scripts and coding agents of the same user drive a running Noctiluca through its control socket, and read the page as the projection’s text, far fewer tokens than a screenshot.
- draft
DraftField: a value edited here while the page holds the real one, with a guarded commit and a conflict check. C5 of the design note: it stays in Noctiluca until a second user needs it. A port of the proof of concept’sprimitives/draft-field.js.- dump
noctiluca --dump: a page read once and printed, for scripts and CI. Every string in it comes from the model, so it is cleaned already.- engine
- The engine trait: what Noctiluca needs from a browser, so the browser
behind it can change (decision 1 of
docs/design/browser.md). Its first backend isChromeBackend, over rs-rich-embed’sChromeEngine; tests use their own. - layout
- The page as lines of cells, two ways:
- model
- The page model: one per tab, built from each
PageSnapshot(aDOMSnapshot.captureSnapshotand anAccessibility.getFullAXTree). - pictures
- Pictures in the views: each media region’s picture drawn over its cells, and animated pictures kept moving within a byte budget.
- pixel_
view - The pixel view (#818): the page as Chrome paints it, pixel for pixel,
for terminals with Kitty graphics (
--pixels,view = "pixels", or theview.pixelsaction). The projection stays the default: it works in every terminal, and a screen reader can read it. - ssh
--ssh HOST: Chrome runs on this machine and its network goes through HOST (#817). A dev server on the remote’slocalhostthen opens as if it were local, with no browser on the remote and no frames over SSH.- start
- The start page: what Noctiluca shows when it starts with no address
(and no
home), while Chrome starts behind it. - tab
- A tab: an
Engineon a thread of its own, which keeps the page model up to date and runs actions with their guards. - testing
- A scripted
Enginefor tests: pages from saved snapshots, actions recorded, and a hook to change the page when an action runs. No browser. - url
- Addresses: local files as
file://URLs, links resolved against their page, queries escaped, and what the address bar means by what was typed. - video
- Video in its region: frames of a playing
<video>, cut from Chrome’s screencast, handed to the picture side to draw. - widgets
- Web widgets: a page’s controls as stock intuiTUIve widgets.