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
15 changes: 13 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: Documentation
on:
push:
branches: [main]
paths: ['apps/**', 'packages/**', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
paths: ['apps/**', 'packages/**', 'vendor/**', 'hig-drift.json', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
pull_request:
paths: ['apps/**', 'packages/**', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
paths: ['apps/**', 'packages/**', 'vendor/**', 'hig-drift.json', 'package.json', 'package-lock.json', '.github/workflows/docs.yml']
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -33,6 +33,17 @@ jobs:
- run: npm run docs:build
- run: npm run docs:check
- run: npm run lint
# Scoped HIG anti-drift check from the same HIG commit as vendor/bloxwap-hig (bloxwap/hig#58).
- uses: actions/checkout@v7
with:
repository: bloxwap/hig
ref: 19c75619fa5020c015aa639172c83fd02f2bc0a2
path: .hig
sparse-checkout: scripts/drift.mjs
sparse-checkout-cone-mode: false
- run: node .hig/scripts/drift.mjs --root . --config hig-drift.json
- run: npx playwright install --with-deps chromium
- run: npm run docs:smoke
- uses: actions/upload-pages-artifact@v5
with:
path: apps/docs/out
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ binaries/
out/
*.tsbuildinfo
apps/docs/next-env.d.ts
.hig/
!vendor/bloxwap-hig/*.tgz
19 changes: 13 additions & 6 deletions apps/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,21 @@ uses a static `search.json` index in the browser. `llms.txt`, robots, and sitema
are generated for the configured deployment URL. The Pages workflow builds and
checks PRs and deploys only main.

The design mirrors Chart's dark surfaces, green accents, typography, navigation,
footer, and HIG controls. `app/global.css`, `styles/hig.generated.css`, and
`vendor/hig/` are copied from Chart, including the upstream license and provenance.
`hig-tokens.mjs --check` verifies the pinned hashes and adapter; update vendor files
from the upstream source rather than hand-editing generated tokens.
The design mirrors Chart's dark surfaces, green accents, typography, navigation and
footer. Shared presentation comes from the Bloxwap HIG packages, installed from the
packed candidates in [`vendor/bloxwap-hig/`](../../vendor/bloxwap-hig/README.md):
`@bloxwap/hig-tokens/css` and `@bloxwap/hig-react/css` supply the `--hig-*` roles and
control recipes, and the install tabs, copy buttons, search states and MDX callouts
use HIG `Tabs`, `InstallCommand`, `IconButton`, `Alert`, `EmptyState` and `Callout`.
`app/global.css` maps Fumadocs onto the HIG roles and keeps only site layout. Never
copy HIG CSS, palettes or fonts into this app. `hig-tokens.mjs --check` (part of
`docs:check`) verifies the vendored hashes, the installed versions and the few
JS-only HIG values `global.css` repeats. CI also runs HIG's scoped drift check
(`hig-drift.json`) and `npm run docs:smoke`, a Playwright pass over install/copy,
keyboard, search, 404 and 390 px navigation against the static export.

Bloxwap Sans and Mono are self-hosted by `@bloxwap/[email protected]`. The social renderer
uses local Bold/Black TTF instances; see `fonts/README.md` and the font license.
uses the static Bold/Black instances shipped in `@bloxwap/hig-tokens/fonts`.
Brand marks in `public/logos/` are supplied artwork, never typeset replacements.
Every MDX page receives a static social card via `app/og/[...slug]/route.tsx`.

Expand Down
380 changes: 180 additions & 200 deletions apps/docs/app/global.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function NotFound() {
<p className="eyebrow">404 / PAGE NOT FOUND</p>
<h1>That page is missing.</h1>
<p>The page you requested does not exist on this site. It may have moved, or the URL may be wrong. Head back to the documentation to find your next step, or use one of the machine-readable indexes below.</p>
<Link href="/docs" className="btn">Open documentation <ArrowRight aria-hidden="true" /></Link>
<Link href="/docs" className="hig-button" data-intent="primary">Open documentation <ArrowRight aria-hidden="true" /></Link>
<nav className="not-found-links" aria-label="Site indexes">
{RESOURCES.map(([label, href]) => <a key={href} href={assetUrl(href)}>{label}</a>)}
</nav>
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function Home() {
<p className="eyebrow"><span className="status-dot" /> HYPERLIQUID · AGENT CLI</p>
<h1>Trading tools.<br /><span>For your agent.</span></h1>
<p className="hero-description">JSON commands, a persistent signing runner, Privy managed agents, and Relay funding. Keep master custody in your wallet and give your agent the tools to trade.</p>
<div className="hero-actions"><Link href="/docs/quickstart" className="btn">Get started <ArrowRight aria-hidden="true" /></Link><Link href="/docs/commands" className="btn btn--secondary">Command reference <ArrowRight aria-hidden="true" /></Link></div>
<div className="hero-actions"><Link href="/docs/quickstart" className="hig-button" data-intent="primary">Get started <ArrowRight aria-hidden="true" /></Link><Link href="/docs/commands" className="hig-button" data-intent="secondary">Command reference <ArrowRight aria-hidden="true" /></Link></div>
<InstallCommand />
</div>
<HeroCode />
Expand Down
13 changes: 13 additions & 0 deletions apps/docs/components/callout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { ReactNode } from 'react';
import { Callout as HIGCallout } from '@bloxwap/hig-react/status';

const severities = { info: 'info', idea: 'info', warn: 'warning', warning: 'warning', error: 'error', success: 'success' } as const;
const titles = { info: 'Note', warning: 'Important', error: 'Error', success: 'Success' } as const;

/** Fumadocs' MDX `<Callout type title>` API, rendered with the HIG callout. Static docs content is not a live region. */
export function Callout({ type = 'info', title, children }: { type?: keyof typeof severities; title?: ReactNode; children?: ReactNode }) {
const severity = severities[type] ?? 'info';
return <div className="docs-callout not-prose">
<HIGCallout title={typeof title === 'string' ? title : titles[severity]} severity={severity} announce={false}>{children}</HIGCallout>
</div>;
}
14 changes: 6 additions & 8 deletions apps/docs/components/copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import { useEffect, useRef, useState } from 'react';
import { Check, Copy } from 'lucide-react';
import { IconButton } from '@bloxwap/hig-react/button';

/** A quiet icon button that copies `text`, with a check mark and a screen-reader status on success. */
/** A compact HIG icon button that copies `text`, with a check mark and a screen-reader status on success. */
export function CopyButton({ text, label }: { text: string; label: string }) {
const pending = useRef(false);
const [busy, setBusy] = useState(false);
const [result, setResult] = useState<'copied' | 'failed' | null>(null);

useEffect(() => {
Expand All @@ -18,23 +18,21 @@ export function CopyButton({ text, label }: { text: string; label: string }) {
async function copy() {
if (pending.current) return;
pending.current = true;
setBusy(true);
try {
await navigator.clipboard.writeText(text);
setResult('copied');
} catch {
setResult('failed');
} finally {
pending.current = false;
setBusy(false);
}
}

return <>
<button type="button" className="btn btn--ghost btn--icon" aria-label={label} disabled={busy} aria-busy={busy} title={result === 'copied' ? 'Copied!' : label} onClick={copy}>
{result === 'copied' ? <Check aria-hidden="true" /> : <Copy aria-hidden="true" />}
</button>
<span role="status" className={result === 'failed' ? 'copy-feedback' : 'sr-only'}>
<IconButton intent="ghost" label={label} data-shape="icon" data-hig-density="compact" title={result === 'copied' ? 'Copied!' : label} onClick={copy}>
{result === 'copied' ? <Check /> : <Copy />}
</IconButton>
<span role="status" className={result === 'failed' ? 'copy-feedback' : 'hig-sr-only'}>
{result === 'copied' ? 'Code copied to clipboard.' : result === 'failed' ? 'Could not copy. Select the code to copy it manually.' : ''}
</span>
</>;
Expand Down
52 changes: 13 additions & 39 deletions apps/docs/components/install-command.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,25 @@
'use client';

import { useId, useState } from 'react';
import { CopyButton } from './copy-button';
import { Tabs } from '@bloxwap/hig-react/navigation';
import { InstallCommand as HIGInstallCommand } from '@bloxwap/hig-react/recipes';

const managers = [
{ name: 'npm', command: 'npm install -g @bloxwap/agent-cli' },
{ name: 'bun', command: 'bunx @bloxwap/agent-cli --help' },
];

/** HIG Tabs pick the package manager (roving focus, arrow/Home/End keys); each panel is the HIG install recipe with its own copy status. */
export function InstallCommand() {
const id = useId();
const [selected, setSelected] = useState(0);
const { command } = managers[selected];

function select(index: number) {
setSelected(index);
}

return <div className="package-install">
<div className="install-tabs" role="tablist" aria-label="Package manager">
{managers.map(({ name }, index) => <button
key={name}
type="button"
role="tab"
id={`${id}-tab-${index}`}
aria-controls={`${id}-command`}
aria-selected={selected === index}
tabIndex={selected === index ? 0 : -1}
onClick={() => select(index)}
onKeyDown={(event) => {
let next: number;
if (event.key === 'ArrowRight') next = (index + 1) % managers.length;
else if (event.key === 'ArrowLeft') next = (index + managers.length - 1) % managers.length;
else if (event.key === 'Home') next = 0;
else if (event.key === 'End') next = managers.length - 1;
else return;
event.preventDefault();
select(next);
document.getElementById(`${id}-tab-${next}`)?.focus();
}}
>{name}</button>)}
</div>
<div className="install-command" role="tabpanel" id={`${id}-command`} aria-labelledby={`${id}-tab-${selected}`} tabIndex={0}>
<span className="install-prompt" aria-hidden="true">$</span>
<code>{command}</code>
<CopyButton key={command} text={command} label="Copy install command" />
</div>
return <div className="package-install" data-hig-density="compact">
<Tabs
label="Package manager"
defaultValue="npm"
items={managers.map(({ name, command }) => ({
value: name,
label: name,
content: <HIGInstallCommand command={command} onCopy={() => navigator.clipboard.writeText(command)} />,
}))}
/>
<p className="install-platforms">Node.js 22.12+ · Bun</p>
</div>;
}
17 changes: 15 additions & 2 deletions apps/docs/components/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ import {
SearchDialogIcon, SearchDialogInput, SearchDialogList, SearchDialogOverlay,
type SharedProps,
} from 'fumadocs-ui/components/dialog/search';
import { Alert, EmptyState } from '@bloxwap/hig-react/status';
import { assetUrl } from '@/lib/site';

/** No-results state; the static index needs no retry, so it points at commands and the plain page list instead. */
function NoResults() {
return <div className="search-state" role="status">
<EmptyState title="No matching pages" description="Try a command such as order or run, an error code, or a topic such as custody." />
</div>;
}

export default function StaticSearch(props: SharedProps) {
const { search, setSearch, query } = useDocsSearch({
client: staticClient({ from: assetUrl('/search.json') }),
Expand All @@ -18,8 +26,13 @@ export default function StaticSearch(props: SharedProps) {
<SearchDialogOverlay />
<SearchDialogContent>
<SearchDialogHeader><SearchDialogIcon /><SearchDialogInput /><SearchDialogClose /></SearchDialogHeader>
{query.error && <p role="alert" className="search-error">Search is unavailable. Use the documentation navigation or close search and try again.</p>}
<SearchDialogList items={query.data === 'empty' ? null : query.data} />
{query.error
? <div className="search-state">
<Alert title="Search is unavailable" severity="error">
<p>Use the documentation navigation, or open <a className="hig-link" href={assetUrl('/llms.txt')}>llms.txt</a> for a plain list of pages.</p>
</Alert>
</div>
: <SearchDialogList items={query.data === 'empty' ? null : query.data} Empty={NoResults} />}
</SearchDialogContent>
</SearchDialog>
);
Expand Down
93 changes: 0 additions & 93 deletions apps/docs/fonts/Bloxwap-OFL.txt

This file was deleted.

Binary file removed apps/docs/fonts/BloxwapSans-Black.ttf
Binary file not shown.
Binary file removed apps/docs/fonts/BloxwapSans-Bold.ttf
Binary file not shown.
12 changes: 0 additions & 12 deletions apps/docs/fonts/README.md

This file was deleted.

Loading
Loading