The web UI
The site run screen — stages, technology, discovery, pages, depth tree, graph and run log — and the single-page run with its paste-HTML box.
The web app (apps/web) is a Next.js client that talks only to the API from the browser.
/extract?url=... runs a whole site; &mode=page runs one page; &complete=false asks for
static-only; &max=N sets the page cap. Everything on screen is drawn from the stream
in Outputs and provenance.
Options at the prompt
The Options button beside the mode row opens the run's options without leaving the
address box: max pages, max depth, stay within the path, stay on the exact host, include
and exclude path patterns, respect robots.txt (one switch for the crawl and the page
fetch), and the rendering switches — reveal collapsed, click collapsed, dismiss gates —
plus the fetch timeout. Every field is empty until you change it; the placeholder is the
engine's default from config.py. Values are saved in the browser and sent with every run,
the same store the Settings page edits, which holds the full list with each
default's comment.
Anything changed shows as a chip under the address — runs with · max depth 1 × — before the run starts, and the × clears it. A saved depth of 1 once turned a 24-page site into a 7-page crawl with nothing on the prompt to say so; the chips exist so a cap can never be silent again, and the run summary names it when it applied: every page within depth 1 crawled · 209 addresses were past the depth cap and not crawled.
The site run
Status line and the stop button
A dot and a phrase name the phase — "Detecting the technology stack", "Seeding routes from sitemap and links", "Crawling and extracting", "Complete", "Stopped", "Failed"; a capped run that did not drain the frontier reads "Reached the page cap · N pages not crawled". Stop sets the server's stop flag; everything already streamed stays.
Progress rail
Completion against the frontier as it stands, or against the page cap when there is one. For an unbounded crawl the denominator grows, so the bar can move backwards — honest, and better than a bar implying a total nobody knows.
The stages
LivePipeline.tsx draws the engine's three stages as they happen — Look at the front
door, once, Build the queue, Crawl, classify and extract — each lighting up when
it starts and filling in what it found when it ends. A stage is never shown finished before
its evidence arrives, and one that has not started shows nothing rather than zeros: "a zero
reads as a measurement, and 'not yet' is not a measurement".
Technology detected
TechnologyPanel.tsx groups the analysis event's technologies by category, each chip
carrying its evidence and confidence on hover and its version where one was read. Below:
the strategy in use, static coverage, and the flags rendering required and rendering
loses content when Stage 0 measured them.
How the site can be discovered
DiscoveryPanel.tsx: three collapsed rows, each a one-line verdict that opens to its
evidence.
- robots.txt — "not found (HTTP 404) — everything allowed", or "found · 1 rule applies
to us · crawl-delay 1s". Open: which
User-agent:group was read and why, the rules (Disallow in red, Allow in green), and the file itself. - Sitemaps — "none published — discovery is by links only", or the sitemaps found, their
URLs, and how many are in scope. Open: every address tried, in order, with its status,
and "· not a sitemap" against a 200 that carried no
<loc>. - URLs found by kind — a live tally of pages, PDFs, images, other files, date archives, categories and tags. When files outnumber pages the collapsed row warns: "Files outnumber pages (7,907 PDFs). Files are counted with the page that links to each, and never fetched." The run summary at the end says which limit ended the run and how many files were counted and not fetched.
Tabs
Discovered and Queued list URLs (queued includes the batch in flight); Extracted is the page list; Failed lists each refusal with its full error and its citation — the page that sent the crawl there and the link text. Three views sit beside them: Pages, Depth tree, Site graph.
The page list
PageList.tsx searches URLs, titles and page content. Each row shows character, image
and table counts and the share of the page identified as chrome; open it for the Markdown,
with two independent switches — content only (filtered; the whole page is the default) and preview vs
source. Download N .md files (.zip) hands over one Markdown file per page, named by
host and path (lakshx.in/docs/sign-in.md, the root as index.md), each opening with
url and title front matter so a file read alone still says where it came from — the
shape a folder of pages is expected in. single .md and Copy all hand over every
successful page as one document headed by its URL. All three follow the same checkbox.
Depth tree
DepthTree.tsx draws the site as the crawl found it, one level per link away from the
root. Each node sits under the page that first linked to it — "the tree draws what the
frontier did rather than a prettier fiction" — and a per-depth bar shows a breadth-first
crawl filling one level before the next.
Site graph and Ask
GraphPanel.tsx shows hubs (what the site links to most, with a specificity figure
separating navigation from topics) and subjects (what pages call each other, from anchor
text). AskPanel.tsx appears once three pages exist and returns a bounded context for a
question, every section saying why it is there.
Run log
Collapsed by default, above the page lists so a 1,600-URL crawl does not bury it. It shows the tail of the event stream — the end is where a run went wrong — and Copy run log pastes the whole run, header and every event, at any moment including mid-run. This is the paste an issue report needs.
The single-page run
SinglePageRun.tsx runs one URL through /api/text/stream with the same shape of
timeline, one page deep: Fetch it both ways, Turn markup into blocks, Read the
page type, Decide what counts as content, Hand it back. Each finished stage lists
what it measured — strategy, plain HTTP vs browser characters, blocks, reading order, the
type with its reasons, what the selection kept.
Paste the page's HTML instead
For the sites that refuse every automated fetch — a Cloudflare challenge, a login wall —
a disclosure opens on its own when the fetch failed. Paste the page source from your own
browser and press Read it: nothing is fetched, links are made absolute against the
address above, and the resolve stage reads "Read the HTML you supplied". The output is the
same text / markdown / content_markdown.
What a paste cannot do
Reading order is source order, text a browser would have hidden may appear, and a pasted wall is refused exactly like a fetched one. Schema mapping is off for a supplied run.
Limits and large sites
A real six-hour whole-site run as the worked example, why an unbounded crawl is expensive, what memory it holds, and what to set.
Site report
What a site shows people, what it shows machines, and how ready it is for AI agents -- every number from a measurement the engine already makes, the score's composition, the no-impersonation policy, and the limits.