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
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ jobs:
- run: pnpm install --frozen-lockfile --filter @headless/web
- run: pnpm --filter @headless/web lint
- run: pnpm --filter @headless/web build
- name: Install Chromium for responsive checks
run: pnpm --filter @headless/web exec playwright install --with-deps chromium
- name: Responsive command directory
run: pnpm --filter @headless/web test:browser

linux-e2e:
name: Linux E2E (Docker)
Expand Down
17 changes: 2 additions & 15 deletions apps/web/app/docs/commands/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { CommandBlock } from "@/components/docs-copy-controls";
import { CommandDirectory } from "@/components/command-directory";
import { DocsShell } from "@/components/docs-shell";
import { plainText } from "@/lib/markdown";
import { loadProductDocsContent } from "@/lib/repository-content.mjs";
import { docsMetadata } from "@/lib/site-metadata";

Expand All @@ -19,19 +18,7 @@ export default function CommandsPage() {
title={<>The complete agent surface.</>}
lede="These usage lines come from the generated command reference, which protocol tests keep in sync with the CLI help output. Unknown parameters fail closed."
>
{commands.groups.map((group, index) => (
<section
id={group.title.toLowerCase().replaceAll(" ", "-")}
key={group.title}
>
<p className="docs-label">
{String(index + 1).padStart(2, "0")} / {group.title}
</p>
<h2>{group.title}</h2>
<p>{plainText(group.description)}</p>
<CommandBlock>{group.usage}</CommandBlock>
</section>
))}
<CommandDirectory groups={commands.groups} />
Comment thread
SarthakWade marked this conversation as resolved.
</DocsShell>
);
}
12 changes: 10 additions & 2 deletions apps/web/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export default function DocsPage() {
return (
<DocsShell
activePath="/docs"
sections={[
{ id: "first-run", label: "First run" },
{ id: "workflow", label: "QA workflow" },
{ id: "commands", label: "Command groups" },
{ id: "context", label: "Context pruning" },
{ id: "scrollable", label: "Scrollable evidence" },
{ id: "safety", label: "Safety" },
]}
kicker="Headless documentation"
title={
<>
Expand All @@ -31,8 +39,8 @@ export default function DocsPage() {
<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.
Start the host, create a session, then visit the app.{" "}
{plainText(documentation.sessionModel)}
</p>
<CommandBlock>{documentation.firstRunCommands}</CommandBlock>
<p>{plainText(documentation.startupPresentation)}</p>
Expand Down
69 changes: 66 additions & 3 deletions apps/web/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,19 @@ h3,
width: min(1180px, calc(100% - 48px));
margin: 0 auto;
}
.skip-link {
position: absolute;
left: 16px;
top: 12px;
z-index: 20;
padding: 8px 12px;
background: var(--amber);
color: var(--ink-on-amber);
transform: translateY(-150%);
}
.skip-link:focus {
transform: none;
}
.docs-nav {
height: 84px;
display: flex;
Expand Down Expand Up @@ -1286,14 +1299,58 @@ h3,
font-family: var(--font-mono), monospace;
font-size: 12px;
}
.docs-nav-group,
.docs-toc {
display: flex;
flex-direction: column;
gap: 11px;
}
.docs-nav-group + .docs-nav-group,
.docs-toc {
margin-top: 30px;
}
.docs-toc-inline {
margin: 0 0 42px;
}
.docs-sidebar p {
margin: 0 0 6px;
color: var(--amber-ink);
font-size: 9px;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.docs-sidebar p:not(:first-child) {
margin-top: 30px;
.command-filter {
display: grid;
gap: 8px;
margin: 0 0 42px;
}
.command-filter label {
font-family: var(--font-mono), monospace;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--amber-ink);
}
.command-filter input {
width: 100%;
min-height: 42px;
padding: 0 12px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--panel);
color: var(--ink);
font-family: var(--font-mono), monospace;
font-size: 13px;
}
.command-filter input:focus {
outline: 2px solid var(--amber);
outline-offset: 2px;
}
.command-filter p {
margin: 0;
color: var(--muted);
font-family: var(--font-mono), monospace;
font-size: 11px;
}
.docs-sidebar a:hover {
color: var(--ink);
Expand Down Expand Up @@ -1474,6 +1531,8 @@ h3,
margin-top: 27px;
}
.docs-content .command-block pre {
max-width: 100%;
overflow-x: auto;
margin: 0;
padding: 24px 74px 24px 24px;
border: 1px solid rgba(255, 176, 32, 0.3);
Expand Down Expand Up @@ -1934,9 +1993,13 @@ h3,
padding-bottom: 12px;
overflow-x: auto;
}
.docs-sidebar p {
.docs-sidebar p,
.docs-sidebar .docs-toc {
display: none;
}
.docs-nav-group {
display: contents;
}
.docs-sidebar a {
flex: 0 0 auto;
padding: 7px 9px;
Expand Down
1 change: 0 additions & 1 deletion apps/web/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type { MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
const routes = [
"/",
"/docs",
"/docs/markdown",
...PRODUCT_DOC_ROUTES.map(({ href }) => href),
];
Expand Down
69 changes: 69 additions & 0 deletions apps/web/components/command-directory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use client";

import { CommandBlock } from "@/components/docs-copy-controls";
import { plainText } from "@/lib/markdown";
import { useState } from "react";

type CommandGroup = {
title: string;
id: string;
description: string;
usage: string;
};

export function CommandDirectory({ groups }: { groups: CommandGroup[] }) {
const [query, setQuery] = useState("");
const normalized = query.trim().toLowerCase();
const visible = normalized
? groups.filter((group) =>
`${group.title} ${group.description} ${group.usage}`
.toLowerCase()
.includes(normalized),
)
: groups;

const countLabel =
visible.length === 0
? `No commands match “${query}”.`
: `${visible.length} of ${groups.length} groups`;

return (
<>
<div className="command-filter">
<label htmlFor="command-filter">Filter commands</label>
<input
id="command-filter"
type="search"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="visit, inspect, credentials…"
autoComplete="off"
spellCheck={false}
/>
<p role="status" aria-live="polite">
{countLabel}
</p>
</div>
{visible.length > 0 ? (
<nav className="docs-toc docs-toc-inline" aria-label="On this page">
<p>On this page</p>
{visible.map((group) => (
<a href={`#${group.id}`} key={group.id}>
{group.title}
</a>
))}
</nav>
) : null}
{visible.map((group, index) => (
<section id={group.id} key={group.id}>
<p className="docs-label">
{String(index + 1).padStart(2, "0")} / {group.title}
</p>
<h2>{group.title}</h2>
<p>{plainText(group.description)}</p>
<CommandBlock>{group.usage}</CommandBlock>
</section>
))}
</>
);
}
65 changes: 40 additions & 25 deletions apps/web/components/docs-shell.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { HeadlessMark } from "@/components/headless-mark";
import { LinkGlyph } from "@/components/link-glyph";
import { ThemeToggle } from "@/components/theme-toggle";
import { PRODUCT_DOC_ROUTES } from "@/lib/repository-content.mjs";
import {
PRODUCT_DOC_CATEGORIES,
PRODUCT_DOC_ROUTES,
} from "@/lib/repository-content.mjs";
import Link from "next/link";

export function DocumentationTable({
Expand Down Expand Up @@ -42,16 +45,21 @@ export function DocsShell({
lede,
children,
headingAction,
sections,
}: {
activePath: string;
kicker: string;
title: React.ReactNode;
lede: string;
children: React.ReactNode;
headingAction?: React.ReactNode;
sections?: Array<{ id: string; label: string }>;
}) {
return (
<main className="docs-shell">
<a className="skip-link" href="#docs-content">
Skip to content
</a>
<nav
className="docs-nav docs-container"
aria-label="Documentation navigation"
Expand All @@ -62,7 +70,10 @@ export function DocsShell({
</Link>
<div>
<Link href="/">Overview</Link>
<Link className="active" href="/docs">
<Link
className={activePath.startsWith("/docs") ? "active" : ""}
href="/docs"
>
Docs
</Link>
<a
Expand All @@ -77,32 +88,36 @@ export function DocsShell({

<div className="docs-container docs-layout">
<aside className="docs-sidebar" aria-label="Documentation sections">
<p>DOCUMENTATION</p>
<Link className={activePath === "/docs" ? "active" : ""} href="/docs">
Overview
</Link>
{PRODUCT_DOC_ROUTES.slice(0, 3).map((route) => (
<Link
className={activePath === route.href ? "active" : ""}
href={route.href}
key={route.href}
>
{route.label}
</Link>
))}
<p>TRUST &amp; SUPPORT</p>
{PRODUCT_DOC_ROUTES.slice(3).map((route) => (
<Link
className={activePath === route.href ? "active" : ""}
href={route.href}
key={route.href}
>
{route.label}
</Link>
{PRODUCT_DOC_CATEGORIES.map((category) => (
<div className="docs-nav-group" key={category.id}>
<p>{category.label}</p>
{PRODUCT_DOC_ROUTES.filter(
(route) => route.category === category.id,
).map((route) => (
<Link
className={activePath === route.href ? "active" : ""}
href={route.href}
key={route.href}
aria-current={activePath === route.href ? "page" : undefined}
>
{route.label}
</Link>
))}
</div>
))}
{sections && sections.length > 0 ? (
<nav className="docs-toc" aria-label="On this page">
<p>On this page</p>
{sections.map((section) => (
<a href={`#${section.id}`} key={section.id}>
{section.label}
</a>
))}
</nav>
) : null}
</aside>

<article className="docs-content">
<article className="docs-content" id="docs-content" tabIndex={-1}>
<div className="docs-heading-row">
<div className="docs-kicker">
<span className="status-dot" /> {kicker}
Expand Down
Loading