Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions apps/web/app/docs/markdown/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { pageMarkdown } from "@/components/docs-markdown";
import { loadDocumentationContent } from "@/lib/repository-content.mjs";

export default function DocsMarkdownPage() {
return <main className="markdown-shell"><div className="markdown-container"><a href="/docs">← Back to docs</a><pre>{pageMarkdown}</pre></div></main>;
const documentation = loadDocumentationContent();
return (
<main className="markdown-shell">
<div className="markdown-container">
<a href="/docs">← Back to docs</a>
<pre>{documentation.markdown}</pre>
</div>
</main>
);
}
105 changes: 81 additions & 24 deletions apps/web/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,79 +2,136 @@ import { CommandBlock, CopyPageButton } from "@/components/docs-copy-controls";
import { HeadlessMark } from "@/components/headless-mark";
import { LinkGlyph } from "@/components/link-glyph";
import { ThemeToggle } from "@/components/theme-toggle";
import { loadDocumentationContent } from "@/lib/repository-content.mjs";
import Link from "next/link";

const coreCommands = [
["Navigation", "visit, back, reload, wait", "Move through a real browser session and wait for a URL, page text, or settled state."],
["Interaction", "inspect, click, fill, press, scroll", "Use task-ranked controls and accessibility roles instead of mouse coordinates or selectors."],
["Evidence", "screenshot, record, visual compare, report", "Create private PNG/JPG/PDF, MP4/MOV/WebM/GIF, diff, flow, and PR-report artifacts."],
["Diagnostics", "console, network, styles, cookies, storage", "Investigate only when something fails; sensitive values remain redacted by default."],
];
function plainText(markdown: string) {
return markdown
.replace(/\[([^\]]+)]\([^)]+\)/g, "$1")
.replaceAll("`", "")
.replaceAll("**", "");
}

export default function DocsPage() {
const documentation = loadDocumentationContent();

return (
<main className="docs-shell">
<nav className="docs-nav docs-container" aria-label="Documentation navigation">
<Link className="brand" href="/" aria-label="Headless home"><HeadlessMark className="brand-mark" /><span>headless</span></Link>
<div><Link href="/">Overview</Link><Link className="active" href="/docs">Docs</Link><a className="nav-external" href="https://github.com/LockInTime/headless">GitHub <LinkGlyph kind="external" /></a><ThemeToggle /></div>
<nav
className="docs-nav docs-container"
aria-label="Documentation navigation"
>
<Link className="brand" href="/" aria-label="Headless home">
<HeadlessMark className="brand-mark" />
<span>headless</span>
</Link>
<div>
<Link href="/">Overview</Link>
<Link className="active" href="/docs">
Docs
</Link>
<a
className="nav-external"
href="https://github.com/LockInTime/headless"
>
GitHub <LinkGlyph kind="external" />
</a>
<ThemeToggle />
</div>
</nav>

<div className="docs-container docs-layout">
<aside className="docs-sidebar">
<p>GET STARTED</p>
<a href="#first-run">First run</a><a href="#workflow">QA workflow</a><a href="#commands">Core commands</a><a href="#context">Context pruning</a><a href="#scrollable">Scrollable evidence</a><a href="#safety">Safety</a>
<a href="#first-run">First run</a>
<a href="#workflow">QA workflow</a>
<a href="#commands">Core commands</a>
<a href="#context">Context pruning</a>
<a href="#scrollable">Scrollable evidence</a>
<a href="#safety">Safety</a>
<p>PLATFORMS</p>
<a href="#linux">Linux + Docker</a><a href="#macos">macOS</a>
<a href="#linux">Linux + Docker</a>
<a href="#macos">macOS</a>
</aside>

<article className="docs-content">
<div className="docs-heading-row"><div className="docs-kicker"><span className="status-dot" /> Headless documentation</div><CopyPageButton /></div>
<h1>Browser control,<br /><em>made useful.</em></h1>
<p className="docs-lede">Headless gives an agent a persistent browser through a small, safe CLI. Test a page, capture evidence, and inspect a failure.</p>
<div className="docs-heading-row">
<div className="docs-kicker">
<span className="status-dot" /> Headless documentation
</div>
<CopyPageButton pageMarkdown={documentation.markdown} />
</div>
<h1>
Browser control,
<br />
<em>made useful.</em>
</h1>
<p className="docs-lede">{plainText(documentation.introduction)}</p>

<section id="first-run">
<p className="docs-label">01 / First run</p>
<h2>Start a session.</h2>
<p>Start the host, create a session, then visit the app. The session stays isolated until you close it.</p>
<CommandBlock>{`headless start\nheadless session create qa\nheadless --session qa visit localhost:3000/designers/dashboard\nheadless --session qa inspect --context summary --task "click Continue"`}</CommandBlock>
<p>On macOS, agent startup leaves your current app in front. Use <code>headless config set startup-presentation foreground</code> to make the old foreground behavior persistent, or set it to <code>background</code> to restore the built-in default. Inspect it with <code>headless config get startup-presentation</code>. The <code>headless start --foreground</code> and <code>--background</code> flags override the setting for one new host; they do not reorder a running host.</p>
<p>
Start the host, create a session, then visit the app. The session
stays isolated until you close it.
</p>
<CommandBlock>{documentation.firstRunCommands}</CommandBlock>
<p>{plainText(documentation.startupPresentation)}</p>
</section>

<section id="workflow">
<p className="docs-label">02 / A QA workflow</p>
<h2>Capture the proof.</h2>
<p>Record the path you need, then stop and create a report.</p>
<CommandBlock>{`headless --session qa record start --fps 10 --format mp4\nheadless --session qa click --role button --name Continue\nheadless --session qa wait --url /next --settled\nheadless --session qa record stop --output dashboard-flow.mp4\nheadless --session qa report create --output pr-report.json`}</CommandBlock>
<CommandBlock>{documentation.qaWorkflowCommands}</CommandBlock>
</section>

<section id="commands">
<p className="docs-label">03 / Command groups</p>
<h2>Use intent, not pixels.</h2>
<div className="docs-command-grid">
{coreCommands.map(([title, commands, description]) => <div className="docs-command" key={title}><h3>{title}</h3><code>{commands}</code><p>{description}</p></div>)}
{documentation.commandGroups.map((group) => (
<div className="docs-command" key={group.title}>
<h3>{group.title}</h3>
<code>{group.commands}</code>
<p>{plainText(group.description)}</p>
</div>
))}
</div>
</section>

<section id="context">
<p className="docs-label">04 / Context pruning</p>
<h2>Reveal only what matters.</h2>
<p>Start with <code>{'inspect --context summary --task "..."'}</code>. For large pages, request an <code>outline</code>, choose a structural <code>@rN</code> reference, and inspect only its <code>text</code> or <code>actions</code> with <code>--within</code>. Bound results with <code>--limit</code>, <code>--budget</code>, and <code>--depth</code>; use <code>full --text</code> only as an explicit broad-page escape hatch.</p>
<p>{plainText(documentation.contextPruning)}</p>
</section>

<section id="scrollable">
<p className="docs-label">05 / Scrollable evidence</p>
<h2>Capture the whole scroll.</h2>
<p>Use <code>screenshot --every-viewport --output dashboard-scroll</code> for up to 80 viewport-height stops, always including the bottom; bounded results report <code>truncated</code> and <code>totalPoints</code>. Section series capture headings and regions, and both modes restore the original scroll position. Add <code>--format jpg</code> for JPEG series; PDF requires <code>--full-page</code>.</p>
<p>{plainText(documentation.scrollableEvidence)}</p>
</section>

<section id="safety">
<p className="docs-label">06 / Safety by default</p>
<h2>Keep the browser local.</h2>
<p>Headless uses a private Unix socket, not a public DevTools port. Only HTTP(S) navigation is allowed. Diagnostic secrets are redacted.</p>
<p>{plainText(documentation.security.slice(0, 3).join(" "))}</p>
</section>

<section id="linux" className="docs-note"><p><b>Linux:</b> use the supplied Docker runtime or native Chromium. Ubuntu Snap Chromium is not supported for repeated navigation.</p></section>
<section id="macos" className="docs-note"><p><b>macOS:</b> build with Xcode Command Line Tools, then use the visible WKWebView host through the same CLI.</p></section>
{documentation.platforms.map((platform) => {
const name = platform.startsWith("macOS") ? "macOS" : "Linux";
return (
<section id={name.toLowerCase()} className="docs-note" key={name}>
<p>
<b>{name}:</b>{" "}
{plainText(platform).replace(
new RegExp(`^${name}[^:]*:\\s*`),
"",
)}
</p>
</section>
);
})}
</article>
</div>
</main>
Expand Down
Loading