chore: import upstream snapshot with attribution
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped

This commit is contained in:
wehub-resource-sync
2026-07-13 12:29:30 +08:00
commit e7738de6d2
1723 changed files with 216139 additions and 0 deletions
+155
View File
@@ -0,0 +1,155 @@
import Link from "next/link";
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import type { HTMLAttributes, ReactNode } from "react";
import { MDXRemote } from "next-mdx-remote/rsc";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeSlug from "rehype-slug";
import remarkGfm from "remark-gfm";
import { docs, getAdjacentDocs } from "@/lib/docs";
import { extractHeadings, readArticleByPath } from "@/lib/articles";
import { pageMetadata } from "@/lib/page-metadata";
import { DocsToc } from "@/components/docs-toc";
import { CopyCodeButton } from "@/components/copy-button";
import { HeadingAnchor } from "@/components/heading-anchor";
import { rehypeZeroHighlight } from "@/lib/rehype-zero-highlight";
import { rehypeJsonRender } from "@/lib/rehype-json-render";
import { AgentChat } from "@/components/agent-chat";
import { FlowChart } from "@/components/flow-chart";
type DocsPageParams = { slug?: string[] };
type DocsPageProps = { params: Promise<DocsPageParams> };
export function generateStaticParams(): DocsPageParams[] {
return docs.map((doc) => ({
slug: doc.path.replace(/^\//, "").split("/"),
}));
}
export async function generateMetadata({ params }: DocsPageProps): Promise<Metadata> {
const { slug } = await params;
return pageMetadata((slug ?? []).join("/"));
}
const rehypePrettyCodeOptions = {
theme: { light: "github-light", dark: "github-dark" },
keepBackground: false,
};
type HeadingProps = HTMLAttributes<HTMLHeadingElement> & {
id?: string;
children?: ReactNode;
};
function makeHeading(Tag: "h2" | "h3" | "h4") {
return function Heading({ id, children, ...rest }: HeadingProps) {
return (
<Tag id={id} {...rest} className="group">
{children}
<HeadingAnchor id={id} />
</Tag>
);
};
}
const mdxComponents = {
pre: ({ children, ...props }: HTMLAttributes<HTMLPreElement>) => (
<div data-code-block className="relative">
<CopyCodeButton />
<pre {...props}>{children}</pre>
</div>
),
h2: makeHeading("h2"),
h3: makeHeading("h3"),
h4: makeHeading("h4"),
agentchat: ({ value }: { value?: string }) => {
if (!value) return null;
try {
const spec = JSON.parse(Buffer.from(value, "base64").toString("utf8"));
return <AgentChat spec={spec} />;
} catch {
return null;
}
},
flowchart: ({ value }: { value?: string }) => {
if (!value) return null;
try {
const spec = JSON.parse(Buffer.from(value, "base64").toString("utf8"));
return <FlowChart spec={spec} />;
} catch {
return null;
}
},
};
export default async function DocsPage({ params }: DocsPageProps) {
const { slug } = await params;
const routePath = "/" + (slug ?? []).join("/");
const result = await readArticleByPath(routePath);
if (!result) notFound();
const { doc, source } = result;
const headings = extractHeadings(source);
const { prev, next } = getAdjacentDocs(doc.slug);
return (
<>
<main className="mx-auto w-full max-w-[54rem] px-4 pb-[50vh] pt-6 md:px-12">
<header className="mb-8 border-b border-border pb-6">
<p className="mb-2 text-[0.8125rem] font-medium tracking-wide text-muted">
{doc.section ?? "Documentation"}
</p>
<h1 className="m-0 text-[clamp(1.75rem,5vw,2.5rem)] font-bold leading-[1.15] tracking-[-0.04em]">
{doc.title}
</h1>
<p className="m-0 mt-3 max-w-[42rem] leading-[1.7] text-muted">{doc.description}</p>
</header>
<article className="prose prose-zinc dark:prose-invert max-w-none prose-headings:scroll-mt-20 prose-headings:tracking-[-0.03em] prose-headings:font-semibold prose-a:text-blue prose-a:font-medium prose-a:no-underline prose-a:hover:underline prose-code:font-normal">
<MDXRemote
source={source}
components={mdxComponents}
options={{
mdxOptions: {
format: "md",
remarkPlugins: [remarkGfm],
rehypePlugins: [
rehypeJsonRender,
rehypeSlug,
[rehypePrettyCode, rehypePrettyCodeOptions],
rehypeZeroHighlight,
],
},
parseFrontmatter: false,
}}
/>
</article>
<nav aria-label="Pagination" className="mt-16 grid grid-cols-1 gap-4 border-t border-border pt-8 md:grid-cols-2">
{prev ? (
<Link
href={prev.path}
className="flex flex-col gap-1 rounded-lg border border-border p-4 no-underline transition hover:border-muted"
>
<span className="text-xs font-medium uppercase tracking-[0.04em] text-muted">Previous</span>
<span className="text-[0.9375rem] font-semibold text-blue">{prev.title}</span>
</Link>
) : (
<span />
)}
{next ? (
<Link
href={next.path}
className="flex flex-col items-end gap-1 rounded-lg border border-border p-4 text-right no-underline transition hover:border-muted"
>
<span className="text-xs font-medium uppercase tracking-[0.04em] text-muted">Next</span>
<span className="text-[0.9375rem] font-semibold text-blue">{next.title}</span>
</Link>
) : null}
</nav>
</main>
<DocsToc headings={headings} />
</>
);
}
+14
View File
@@ -0,0 +1,14 @@
import type { ReactNode } from "react";
import { DocsSidebarShell } from "@/components/docs-sidebar";
import { docs, groupBySection } from "@/lib/docs";
export default function DocsLayout({ children }: { children: ReactNode }) {
const groups = groupBySection(docs);
return (
<div className="grid min-h-screen grid-cols-1 md:grid-cols-[15rem_minmax(0,1fr)] lg:grid-cols-[15rem_minmax(0,1fr)_14rem]">
<DocsSidebarShell groups={groups} />
{children}
</div>
);
}
+163
View File
@@ -0,0 +1,163 @@
import { NextResponse } from "next/server";
import { convertToModelMessages, stepCountIs, streamText } from "ai";
import type { ModelMessage, UIMessage } from "ai";
import { createBashTool } from "bash-tool";
import { docs } from "@/lib/docs";
import { readArticleBySlug } from "@/lib/articles";
import {
docsChatDailyRateLimit,
docsChatMinuteRateLimit,
isDocsChatRateLimitConfigured,
} from "@/lib/rate-limit";
export const maxDuration = 60;
const DEFAULT_MODEL = "anthropic/claude-haiku-4.5";
const SYSTEM_PROMPT = `You are a helpful documentation assistant for Zero, a pre-1 experimental programming language project exploring agent-first design. Zero intentionally makes breaking changes while searching for the language, library, and tooling patterns that work best for agents. Security vulnerabilities should be expected; Zero should be run or developed only in safe, non-production environments.
GitHub repository: https://github.com/vercel-labs/zerolang
Documentation: https://zerolang.ai
You have access to the full Zero documentation via the bash and readFile tools. The docs are available as markdown files in the /workspace/ directory. The file layout matches each page's URL slug — e.g. /workspace/index.md is the home, /workspace/getting-started.md is /getting-started, /workspace/modules/parse.md is /modules/parse.
When answering questions:
- Use the bash tool to list files (ls /workspace/) or search for content (grep -r "keyword" /workspace/)
- Use the readFile tool to read specific documentation pages (e.g. readFile with path "/workspace/getting-started.md")
- Do NOT use bash to write, create, modify, or delete files (no tee, cat >, sed -i, echo >, cp, mv, rm, mkdir, touch, etc.) — you are read-only
- Always base your answers on the actual documentation content
- Be concise and accurate
- If the docs don't cover a topic, say so honestly
- Do NOT include source references or file paths in your response
- Do NOT use emojis in your responses
- When showing Zero projection syntax, use \`\`\`zero fenced blocks and say it is a human-readable projection of the graph. When showing shell commands, use \`\`\`sh.`;
type DocsFiles = Record<string, string>;
let cachedDocsFiles: DocsFiles | null = null;
async function loadDocsFiles(): Promise<DocsFiles> {
if (cachedDocsFiles) return cachedDocsFiles;
const files: DocsFiles = {};
const results = await Promise.allSettled(
docs.map(async (doc) => {
const md = await readArticleBySlug(doc.slug);
if (!md) return null;
const fileName = doc.path === "/" ? "/index.md" : `${doc.path}.md`;
return { fileName: fileName.replace(/^\//, ""), md };
}),
);
for (const result of results) {
if (result.status === "fulfilled" && result.value) {
files[result.value.fileName] = result.value.md;
}
}
cachedDocsFiles = files;
return files;
}
function addCacheControl(messages: ModelMessage[]): ModelMessage[] {
if (messages.length === 0) return messages;
return messages.map((message, index) => {
if (index === messages.length - 1) {
return {
...message,
providerOptions: {
...message.providerOptions,
anthropic: { cacheControl: { type: "ephemeral" } },
},
};
}
return message;
});
}
function getRateLimitIdentifier(req: Request): string {
const forwardedFor = req.headers.get("x-forwarded-for");
const realIp = req.headers.get("x-real-ip");
return forwardedFor?.split(",")[0]?.trim() || realIp?.trim() || "anonymous";
}
async function rateLimitRequest(req: Request): Promise<NextResponse | null> {
if (!isDocsChatRateLimitConfigured()) {
return NextResponse.json(
{
error: "Chat rate limiting is not configured",
message: "Set KV_REST_API_URL and KV_REST_API_TOKEN to enable docs chat.",
},
{ status: 503 },
);
}
const identifier = getRateLimitIdentifier(req);
let minuteResult;
let dailyResult;
try {
[minuteResult, dailyResult] = await Promise.all([
docsChatMinuteRateLimit.limit(identifier),
docsChatDailyRateLimit.limit(identifier),
]);
} catch (error) {
console.error("Docs chat rate limiting failed", error);
return NextResponse.json(
{
error: "Chat rate limiting unavailable",
message: "Docs chat is temporarily unavailable because rate limiting could not be applied.",
},
{ status: 503 },
);
}
if (minuteResult.success && dailyResult.success) return null;
const isMinuteLimit = !minuteResult.success;
return NextResponse.json(
{
error: "Rate limit exceeded",
message: isMinuteLimit
? "Too many requests. Please wait a moment before trying again."
: "Daily limit reached. Please try again tomorrow.",
},
{ status: 429 },
);
}
export async function POST(req: Request) {
if (!process.env.AI_GATEWAY_API_KEY && !process.env.VERCEL_OIDC_TOKEN) {
return NextResponse.json(
{ error: "Chat is not configured. Set AI_GATEWAY_API_KEY or configure deployment OIDC." },
{ status: 503 },
);
}
const rateLimitResponse = await rateLimitRequest(req);
if (rateLimitResponse) return rateLimitResponse;
let body: { messages?: UIMessage[] } | null;
try {
body = (await req.json()) as { messages?: UIMessage[] };
} catch {
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
}
const messages = Array.isArray(body?.messages) ? body.messages : [];
const docsFiles = await loadDocsFiles();
const {
tools: { bash, readFile },
} = await createBashTool({ files: docsFiles });
const result = streamText({
model: DEFAULT_MODEL,
system: SYSTEM_PROMPT,
messages: await convertToModelMessages(messages),
stopWhen: stepCountIs(5),
tools: { bash, readFile },
prepareStep: ({ messages: stepMessages }) => ({
messages: addCacheControl(stepMessages),
}),
});
return result.toUIMessageStreamResponse();
}
+60
View File
@@ -0,0 +1,60 @@
import { NextRequest, NextResponse } from "next/server";
import { getSearchIndex } from "@/lib/search-index";
import type { SearchResult } from "@/lib/types";
type ScoredSearchResult = SearchResult & { score: number };
function isScoredResult(result: ScoredSearchResult | null): result is ScoredSearchResult {
return result !== null;
}
export async function GET(req: NextRequest) {
const q = req.nextUrl.searchParams.get("q")?.trim().toLowerCase();
if (!q) return NextResponse.json({ results: [] });
const index = await getSearchIndex();
const terms = q.split(/\s+/).filter(Boolean);
const results = index
.map((entry) => {
const titleLower = entry.title.toLowerCase();
const contentLower = entry.content.toLowerCase();
const titleMatch = terms.every((t) => titleLower.includes(t));
const contentMatch = terms.every((t) => contentLower.includes(t));
if (!titleMatch && !contentMatch) return null;
let snippet = "";
if (contentMatch) {
const firstTermIdx = Math.min(
...terms.map((t) => {
const idx = contentLower.indexOf(t);
return idx === -1 ? Infinity : idx;
}),
);
if (firstTermIdx !== Infinity) {
const start = Math.max(0, firstTermIdx - 40);
const end = Math.min(entry.content.length, firstTermIdx + 120);
snippet =
(start > 0 ? "..." : "") +
entry.content.slice(start, end).replace(/\n/g, " ") +
(end < entry.content.length ? "..." : "");
}
}
return {
title: entry.title,
href: entry.href,
section: entry.section,
snippet,
score: titleMatch ? 2 : 1,
};
})
.filter(isScoredResult)
.sort((a, b) => b.score - a.score)
.slice(0, 20)
.map(({ score: _, ...rest }) => rest);
return NextResponse.json({ results }, { headers: { "Cache-Control": "public, max-age=60" } });
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+312
View File
@@ -0,0 +1,312 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "tailwindcss-animate";
@custom-variant dark (&:is(.dark *, .dark));
@theme {
--color-bg: #ffffff;
--color-fg: #171717;
--color-muted: #666666;
--color-border: #eaeaea;
--color-surface: #ffffff;
--color-surface-muted: #fafafa;
--color-accent: #000000;
--color-accent-fg: #ffffff;
--color-blue: #0070f3;
--color-blue-hover: #0060df;
--color-success: #0070f3;
--color-danger: #ee0000;
--color-danger-bg: #fff0f0;
--color-code-bg: #f5f5f5;
--color-code-fg: #171717;
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
--font-mono: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
--container-content: 72rem;
--shadow-card: 0 4px 14px 0 rgb(0 0 0 / 0.1);
--shadow-soft: 0 2px 8px rgb(0 0 0 / 0.08);
}
.dark {
--color-bg: #000000;
--color-fg: #ededed;
--color-muted: #888888;
--color-border: #333333;
--color-surface: #111111;
--color-surface-muted: #0a0a0a;
--color-accent: #ffffff;
--color-accent-fg: #000000;
--color-blue: #3291ff;
--color-blue-hover: #52a9ff;
--color-danger: #ff4444;
--color-danger-bg: #2a0000;
--color-code-bg: #111111;
--color-code-fg: #ededed;
--shadow-card: 0 4px 14px 0 rgb(0 0 0 / 0.25);
--shadow-soft: 0 2px 8px rgb(0 0 0 / 0.2);
}
:root {
color-scheme: light;
}
.dark {
color-scheme: dark;
}
html {
background: var(--color-bg);
color: var(--color-fg);
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
min-height: 100vh;
}
::selection {
background: color-mix(in srgb, var(--color-blue) 25%, transparent);
}
/* ─── Shiki (rehype-pretty-code) ───────────────────── */
[data-rehype-pretty-code-figure] {
margin: 1.5rem 0;
}
[data-rehype-pretty-code-figure] pre {
overflow-x: auto;
padding: 1rem;
border: 1px solid var(--color-border);
border-radius: 0.5rem;
background: var(--color-code-bg);
font-size: 0.8125rem;
line-height: 1.6;
}
[data-rehype-pretty-code-figure] pre > code {
display: grid;
background: transparent;
padding: 0;
border: 0;
font-size: inherit;
color: inherit;
}
[data-rehype-pretty-code-figure] pre > code [data-line] {
padding: 0 0.25rem;
}
[data-rehype-pretty-code-title] {
padding: 0.5rem 1rem;
font-size: 0.75rem;
font-family: var(--font-mono);
color: var(--color-muted);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-bottom: 0;
border-radius: 0.5rem 0.5rem 0 0;
}
[data-rehype-pretty-code-title] + pre {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.prose :where(h1, h2, h3, h4, h5, h6) a,
.prose :where(h1, h2, h3, h4, h5, h6) a:hover {
color: inherit;
font-weight: inherit;
text-decoration: none;
}
[data-rehype-pretty-code-figure] [data-theme] code {
color: inherit;
background-color: transparent;
}
[data-rehype-pretty-code-figure] span {
color: var(--shiki-light);
}
.dark [data-rehype-pretty-code-figure] span {
color: var(--shiki-dark);
}
/* ─── Inline syntax highlight (home page) ──────────── */
.hl-comment { color: #6b7280; font-style: italic; }
.hl-string { color: #067a6e; }
.hl-keyword { color: #d6409f; }
.hl-type { color: #0070c0; }
.hl-function { color: #6f42c1; }
.hl-number { color: #0070c0; }
.hl-variable { color: var(--color-fg); }
.hl-id { color: #6f42c1; }
.hl-key { color: #d6409f; }
.hl-boolean { color: #0070c0; }
.hl-punctuation { color: var(--color-fg); }
.hl-operator { color: #d6409f; }
.dark .hl-comment { color: #a1a1a1; }
.dark .hl-string { color: #00ca50; }
.dark .hl-keyword { color: #ff4d8d; }
.dark .hl-type { color: #c472fb; }
.dark .hl-function { color: #c472fb; }
.dark .hl-number { color: #47a8ff; }
.dark .hl-variable { color: #47a8ff; }
.dark .hl-id { color: #c472fb; }
.dark .hl-key { color: #ff4d8d; }
.dark .hl-boolean { color: #47a8ff; }
.dark .hl-punctuation { color: #ededed; }
.dark .hl-operator { color: #ff4d8d; }
.hl-bash-function {
color: var(--color-fg);
font-weight: 600;
}
.hl-bash-key { color: #0070f3; }
.hl-bash-string { color: #067a6e; }
.hl-bash-keyword { color: #d6409f; }
.hl-bash-variable { color: #6f42c1; }
.hl-bash-operator,
.hl-bash-punctuation {
color: var(--color-muted);
}
.hl-bash-number { color: var(--color-fg); }
.dark .hl-bash-function { color: #ffffff; }
.dark .hl-bash-key { color: #52a9ff; }
.dark .hl-bash-string { color: #00ca50; }
.dark .hl-bash-keyword { color: #ff4d8d; }
.dark .hl-bash-variable { color: #c472fb; }
.dark .hl-bash-operator,
.dark .hl-bash-punctuation {
color: #888888;
}
.dark .hl-bash-number { color: #ffffff; }
/* ─── Home chat motion ────────────────────────────── */
@keyframes home-chat-reveal {
from {
opacity: 0;
transform: translateY(0.875rem) scale(0.985);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes home-command-sheen {
0% {
opacity: 0;
transform: translateX(-120%);
}
28% {
opacity: 0.18;
}
100% {
opacity: 0;
transform: translateX(120%);
}
}
.home-chat-shell {
transform-origin: center top;
}
.home-chat-code {
background: color-mix(in srgb, var(--color-fg) 9%, var(--color-bg));
border: 1px solid color-mix(in srgb, var(--color-fg) 14%, transparent);
border-radius: 0.375rem;
color: var(--color-fg);
font-family: var(--font-mono);
font-size: 0.78125rem;
padding: 0.125rem 0.45rem;
}
.home-chat-reveal,
.home-tool-row {
opacity: 0;
transform: translateY(0.875rem) scale(0.985);
will-change: opacity, transform;
}
.home-chat-viewport[data-chat-visible="true"] .home-chat-reveal,
.home-chat-viewport[data-chat-visible="true"] .home-tool-row {
animation: home-chat-reveal 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
animation-delay: var(--chat-delay, 0ms);
}
.home-tool-row {
position: relative;
}
.home-tool-row::after {
opacity: 0;
}
.home-chat-viewport[data-chat-visible="true"] .home-tool-row::after {
animation: home-command-sheen 1150ms ease-out forwards;
animation-delay: calc(var(--chat-delay, 0ms) + 140ms);
background: linear-gradient(
90deg,
transparent,
color-mix(in srgb, var(--color-fg) 18%, transparent),
transparent
);
content: "";
inset: 0;
pointer-events: none;
position: absolute;
transform: translateX(-120%);
}
@media (prefers-reduced-motion: reduce) {
.home-chat-reveal,
.home-tool-row {
animation: none;
opacity: 1;
transform: none;
}
.home-tool-row::after {
animation: none;
content: none;
}
}
/* ─── Prose adjustments ────────────────────────────── */
.prose pre {
background: var(--color-code-bg) !important;
color: var(--color-code-fg) !important;
}
.prose :where(code):not(:where([class~="not-prose"] *)) {
background: var(--color-surface-muted);
border: 1px solid var(--color-border);
border-radius: 0.25rem;
padding: 0.15rem 0.4rem;
font-weight: 400;
}
.prose :where(code):not(:where([class~="not-prose"] *))::before,
.prose :where(code):not(:where([class~="not-prose"] *))::after {
content: none;
}
.prose :where(pre code):not(:where([class~="not-prose"] *)) {
background: transparent;
border: 0;
padding: 0;
}
+61
View File
@@ -0,0 +1,61 @@
import "./globals.css";
import "@xyflow/react/dist/style.css";
import { Analytics } from "@vercel/analytics/next";
import { SpeedInsights } from "@vercel/speed-insights/next";
import type { Metadata, Viewport } from "next";
import type { ReactNode } from "react";
import { ThemeProvider } from "@/components/theme-provider";
import { SiteHeader } from "@/components/site-header";
import { DocsChat } from "@/components/docs-chat";
import { getStarCount } from "@/lib/github";
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://zerolang.ai";
const SITE_DESCRIPTION =
"Zero is a pre-1 agent-first language experiment. Expect breaking changes and run it only in safe, non-production environments.";
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
title: {
default: "Zero - Agent-first language experiment.",
template: "%s | Zero",
},
description: SITE_DESCRIPTION,
openGraph: {
type: "website",
locale: "en_US",
url: SITE_URL,
siteName: "Zero",
title: "Zero - Agent-first language experiment.",
description: SITE_DESCRIPTION,
images: [{ url: "/og", width: 1200, height: 630, alt: "Zero" }],
},
twitter: {
card: "summary_large_image",
title: "Zero - Agent-first language experiment.",
description: SITE_DESCRIPTION,
images: ["/og"],
},
};
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
};
export default async function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
const stars = await getStarCount();
return (
<html lang="en" suppressHydrationWarning>
<body className="bg-bg text-fg antialiased">
<ThemeProvider>
<SiteHeader stars={stars} />
{children}
<DocsChat />
</ThemeProvider>
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
+12
View File
@@ -0,0 +1,12 @@
import { ButtonLink } from "@/components/button";
export default function NotFound() {
return (
<main className="mx-auto w-[min(100%-3rem,42rem)] py-16">
<p className="mb-2 text-[0.8125rem] font-medium uppercase tracking-[0.04em] text-muted">404</p>
<h1 className="mb-4 text-3xl font-bold tracking-tight">Not found</h1>
<p className="mb-6 text-muted">The page you requested does not exist.</p>
<ButtonLink href="/">Go home</ButtonLink>
</main>
);
}
+15
View File
@@ -0,0 +1,15 @@
import { NextResponse } from "next/server";
import { getPageTitle, renderOgImage } from "../og-image";
type OgRouteContext = { params: Promise<{ slug: string[] }> };
export async function GET(_request: Request, { params }: OgRouteContext) {
const { slug } = await params;
const title = getPageTitle(slug.join("/"));
if (!title) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return renderOgImage(title);
}
+112
View File
@@ -0,0 +1,112 @@
import { ImageResponse } from "next/og";
import { readFile } from "node:fs/promises";
import { join } from "node:path";
export { getPageTitle } from "@/lib/page-titles";
type FontCache = {
geistRegular: Buffer;
geistPixelSquare: Buffer;
};
let fontCache: FontCache | null = null;
async function loadFonts(): Promise<FontCache> {
if (fontCache) return fontCache;
const [geistRegular, geistPixelSquare] = await Promise.all([
readFile(join(process.cwd(), "public/Geist-Regular.ttf")),
readFile(join(process.cwd(), "public/GeistPixel-Square.ttf")),
]);
fontCache = { geistRegular, geistPixelSquare };
return fontCache;
}
export async function renderOgImage(title: string): Promise<ImageResponse> {
const { geistRegular, geistPixelSquare } = await loadFonts();
return new ImageResponse(
(
<div
style={{
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
backgroundColor: "black",
padding: "60px 80px",
}}
>
<div style={{ display: "flex", alignItems: "center", gap: "16px" }}>
<svg width="36" height="32" viewBox="0 0 76 65" fill="white">
<path d="M37.5274 0L75.0548 65H0L37.5274 0Z" />
</svg>
<span
style={{
fontSize: 36,
color: "#666",
fontFamily: "Geist",
fontWeight: 400,
}}
>
/
</span>
<span
style={{
fontSize: 36,
fontFamily: "GeistPixelSquare",
fontWeight: 400,
color: "white",
}}
>
zero
</span>
</div>
<div
style={{
display: "flex",
flex: 1,
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
}}
>
{title.split("\n").map((line, i) => (
<span
key={i}
style={{
fontSize: 72,
fontFamily: "Geist",
fontWeight: 400,
color: "white",
letterSpacing: "-0.02em",
textAlign: "center",
lineHeight: 1.2,
}}
>
{line}
</span>
))}
</div>
</div>
),
{
width: 1200,
height: 630,
fonts: [
{
name: "Geist",
data: geistRegular,
style: "normal",
weight: 400,
},
{
name: "GeistPixelSquare",
data: geistPixelSquare,
style: "normal",
weight: 400,
},
],
},
);
}
+6
View File
@@ -0,0 +1,6 @@
import { getPageTitle, renderOgImage } from "./og-image";
export async function GET() {
const title = getPageTitle("") ?? "Zero";
return renderOgImage(title);
}
+400
View File
@@ -0,0 +1,400 @@
import { ArrowRightIcon, LogoIcon } from "@/components/icons";
import type { CSSProperties, ReactNode } from "react";
import { ButtonLink } from "@/components/button";
import { InstallCopy } from "@/components/install-copy";
import { HomeChatViewport } from "@/components/home-chat-viewport";
import { ChatToolRuns } from "@/components/home-chat-tools";
import { LoopDiagram } from "@/components/loop-diagram";
import { highlight } from "@/lib/highlight";
import { pageMetadata } from "@/lib/page-metadata";
export const metadata = pageMetadata("");
const CODE_EXAMPLE = `<span class="hl-keyword">pub</span> <span class="hl-keyword">fn</span> <span class="hl-variable">main</span>(world: <span class="hl-type">World</span>) -> <span class="hl-type">Void</span> <span class="hl-keyword">raises</span> {
<span class="hl-keyword">check</span> world.out.<span class="hl-variable">write</span>(<span class="hl-string">"hello from zero\\n"</span>)
}`;
const GRAPH_EXAMPLE = `zero-graph v1
origin source-text
module "hello"
hash "graph:a7f7e6899a73f3b4"
node #decl_ad8d9028 Function name:"main" type:"Void" public:true fallible:true
node #param_4610ae76 Param name:"world" type:"World"
node #expr_c403020c MethodCall name:"write" type:"Void"
node #expr_653eeb6e Literal type:"String" value:"hello from zero\\n"
edge #expr_c403020c arg #expr_653eeb6e order:0`;
const PATCH_EXAMPLE = `zero patch \\
--expect-graph-hash graph:a7f7e6899a73f3b4 \\
--op 'set node="#expr_653eeb6e" \\
field="value" \\
expect="hello from zero\\n" \\
value="hello graph\\n"'`;
const CONSTRAINTS = [
"Token efficiency",
"Low memory",
"Fast startup",
"Fast builds",
"Low latency",
"Zero dependencies",
];
const ARCHITECTURE_STEPS = [
{
label: "Human",
title: "Asks for an outcome",
body: "Build a CRM API, add auth, or fix a failing route",
},
{
label: "Agent",
title: "Uses Graph",
body: "Symbols, calls, types, effects, node IDs, and graph hashes",
},
{
label: "Compiler",
title: "Checks patches",
body: "Shape, type, stale-state, and repository metadata checks",
},
{
label: "Human",
title: "Reviews projection",
body: "Readable .0 projections stay available for review and rare manual edits",
},
];
/* ─── Primitives ──────────────────────────────────── */
function GridGlow() {
return (
<div aria-hidden className="pointer-events-none absolute inset-0 -z-10 overflow-hidden">
<div
className="absolute inset-0 opacity-[0.4] dark:opacity-[0.25]"
style={{
backgroundImage:
"linear-gradient(to right, var(--color-border) 1px, transparent 1px), linear-gradient(to bottom, var(--color-border) 1px, transparent 1px)",
backgroundSize: "64px 64px",
maskImage:
"radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%)",
WebkitMaskImage:
"radial-gradient(ellipse 80% 60% at 50% 0%, #000 30%, transparent 75%)",
}}
/>
</div>
);
}
function SectionHeader({
title,
description,
}: {
title: string;
description: string;
}) {
return (
<div className="mb-12 max-w-[44rem]">
<h2 className="m-0 max-w-[14ch] text-[clamp(2.25rem,6vw,4.25rem)] font-semibold leading-[0.98] tracking-[-0.045em]">
{title}
</h2>
<p className="mt-6 max-w-[34rem] text-pretty text-[1.0625rem] leading-[1.65] text-muted">
{description}
</p>
</div>
);
}
function VisualGlow({ className = "", children }: { className?: string; children: ReactNode }) {
return <div className={className}>{children}</div>;
}
function Panel({ className = "", children }: { className?: string; children: ReactNode }) {
return (
<div
className={`relative overflow-hidden rounded-2xl border border-fg/30 ${className}`}
style={{ boxShadow: "0 1px 0 0 color-mix(in srgb, var(--color-fg) 4%, transparent)" }}
>
{children}
</div>
);
}
function WindowBar({ title }: { title: string }) {
return (
<div className="relative flex items-center border-b border-fg/30 px-4 py-3">
<div className="flex gap-1.5">
<span className="h-2.5 w-2.5 rounded-full bg-border" />
<span className="h-2.5 w-2.5 rounded-full bg-border" />
<span className="h-2.5 w-2.5 rounded-full bg-border" />
</div>
<span className="absolute left-1/2 -translate-x-1/2 font-mono text-xs font-medium text-muted">
{title}
</span>
</div>
);
}
function CodeWindow({
title,
html,
className = "",
}: {
title: string;
html: string;
className?: string;
}) {
return (
<Panel className={`bg-bg ${className}`}>
<WindowBar title={title} />
<pre className="m-0 overflow-x-auto bg-bg p-5 text-[0.8125rem] leading-[1.7]">
<code className="text-code-fg" dangerouslySetInnerHTML={{ __html: html }} />
</pre>
</Panel>
);
}
function ArchitectureDiagram() {
return (
<div className="mt-12 w-full text-left">
<div className="grid grid-cols-1 gap-px overflow-hidden rounded-2xl border border-border bg-border sm:grid-cols-2 lg:grid-cols-4">
{ARCHITECTURE_STEPS.map((item, index) => (
<div
key={item.title}
className="group relative flex flex-col bg-bg p-7 transition-colors duration-200 hover:bg-surface-muted"
>
<div className="mb-7 flex items-center gap-2.5">
<span className="flex h-6 w-6 items-center justify-center rounded-md border border-border font-mono text-[0.6875rem] tabular-nums text-muted transition-colors group-hover:border-fg/40 group-hover:text-fg">
{index + 1}
</span>
<span className="font-mono text-[0.6875rem] font-semibold uppercase tracking-[0.16em] text-muted">
{item.label}
</span>
</div>
<h2 className="m-0 text-[1.0625rem] font-semibold leading-snug tracking-[-0.02em] text-fg">
{item.title}
</h2>
<p className="m-0 mt-2.5 text-[0.8125rem] leading-[1.65] text-muted">
{item.body}
</p>
<span
aria-hidden
className="pointer-events-none absolute right-0 top-1/2 z-10 hidden -translate-y-1/2 translate-x-[calc(50%+1px)] lg:flex"
>
{index < ARCHITECTURE_STEPS.length - 1 ? (
<span className="flex h-6 w-6 items-center justify-center rounded-full border border-border bg-bg text-muted">
<ArrowRightIcon width={12} height={12} />
</span>
) : null}
</span>
</div>
))}
</div>
</div>
);
}
/* ─── Chat mockup ─────────────────────────────────── */
function chatRevealStyle(delay: string): CSSProperties {
return { "--chat-delay": delay } as CSSProperties;
}
function ChatMockup() {
return (
<HomeChatViewport>
<Panel className="home-chat-shell bg-bg shadow-card">
<WindowBar title="your agent" />
<div className="flex flex-col gap-5 p-5 sm:p-6">
<div className="home-chat-reveal flex justify-end" style={chatRevealStyle("80ms")}>
<div className="max-w-[82%] rounded-2xl rounded-br-md bg-fg px-4 py-2.5 text-[0.875rem] leading-relaxed text-bg">
build hello world in zerolang
</div>
</div>
<div
className="home-chat-reveal max-w-[92%] text-[0.875rem] leading-[1.65] text-fg"
style={chatRevealStyle("360ms")}
>
I&apos;ll set up the package, patch the graph, and run it.
</div>
<ChatToolRuns startDelayMs={640} />
<div
className="home-chat-reveal max-w-[92%] text-[0.875rem] leading-[1.65] text-fg"
style={chatRevealStyle("1320ms")}
>
Done. <code className="home-chat-code">zero.graph</code> validated at{" "}
<code className="home-chat-code">graph:a7f7e689</code> with symbol{" "}
<code className="home-chat-code">main</code>. It prints:
</div>
<div
className="home-chat-reveal home-chat-output rounded-lg border border-border bg-code-bg px-4 py-3 font-mono text-[0.8125rem] text-code-fg"
style={chatRevealStyle("1520ms")}
>
hello from zero
</div>
</div>
</Panel>
</HomeChatViewport>
);
}
/* ─── Section wrapper ─────────────────────────────── */
function Section({ children }: { children: ReactNode }) {
return (
<section className="relative z-10 mx-auto w-[min(100%-3rem,var(--container-content))] border-t border-border py-[clamp(5rem,11vh,8rem)]">
{children}
</section>
);
}
export default function HomePage() {
return (
<div className="relative min-h-screen overflow-hidden">
<main>
{/* Hero */}
<section className="relative overflow-hidden px-6 pb-24 pt-[clamp(4rem,11vh,7rem)]">
<GridGlow />
<div className="relative z-10 mx-auto flex max-w-[54rem] flex-col items-center text-center">
<div className="mb-7 inline-flex items-center rounded-full border border-border bg-surface/70 px-3.5 py-1 font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-muted backdrop-blur">
Experimental
</div>
<h1 className="m-0 text-[clamp(2.5rem,7vw,5rem)] font-semibold leading-[0.98] tracking-[-0.05em]">
The Programming
<br />
Language for Agents
</h1>
<p className="mt-7 max-w-[40rem] text-pretty text-[clamp(1.0625rem,2vw,1.25rem)] leading-[1.6] text-muted">
A programming language where the graph is the program. Humans ask
for outcomes. Agents query the program graph, submit checked
edits, and prove the result.
</p>
<InstallCopy />
<ArchitectureDiagram />
<p className="mt-5 max-w-[34rem] text-[0.8125rem] leading-relaxed text-muted">
Expect breaking changes. Run it in a safe environment, not against
production systems.
</p>
</div>
</section>
{/* 01 — Chat */}
<Section>
<SectionHeader
title="Start with a request."
description="The expected workflow is a normal conversation. The graph discipline lives in the agent skills and compiler commands, not in stiff human prompts."
/>
<VisualGlow className="mx-auto max-w-[40rem]">
<ChatMockup />
</VisualGlow>
</Section>
{/* 02 — Loop */}
<Section>
<SectionHeader
title="Tighter agent loop."
description="A traditional loop writes text, then runs tools to learn what the edit meant. Zerolang puts the compiler in the loop, so an edit is a checked change to the graph."
/>
<LoopDiagram />
</Section>
{/* 03 — Patch */}
<Section>
<SectionHeader
title="Checked by default."
description="Graph patches target semantic nodes and fields, guarded by graph hashes and expected values. Stale or invalid edits fail before they touch the store."
/>
<VisualGlow className="grid grid-cols-1 gap-5 lg:grid-cols-[1.4fr_1fr]">
<CodeWindow title="zero patch" html={highlight(PATCH_EXAMPLE, "sh")} />
<Panel className="bg-bg">
<dl className="m-0 divide-y divide-border/50 font-mono text-[0.8125rem]">
{[
["graph hash", "graph:b3c1d04f"],
["node", "#expr_653eeb6e"],
["field", "value"],
["symbols", "main"],
["projection", "src/main.0"],
].map(([k, v]) => (
<div key={k} className="flex items-center justify-between gap-4 px-5 py-3">
<dt className="text-muted">{k}</dt>
<dd className="m-0 truncate text-code-fg">{v}</dd>
</div>
))}
</dl>
</Panel>
</VisualGlow>
</Section>
{/* 04 — Graph */}
<Section>
<SectionHeader
title="The program database."
description="Readable text stays useful for review. The compiler-owned graph is the program database agents edit and the compiler consumes."
/>
<VisualGlow className="grid grid-cols-1 gap-5 lg:grid-cols-2">
<CodeWindow title="zero query · graph" html={highlight(GRAPH_EXAMPLE, "zero-graph")} />
<CodeWindow title="src/main.0 · projection" html={CODE_EXAMPLE} />
</VisualGlow>
</Section>
{/* 04 — Constraints */}
<Section>
<SectionHeader
title="Built for runtime constraints."
description="The graph model should reduce guessing without relaxing the runtime goals. Zerolang still aims to stay small, fast, explicit, and dependency-free."
/>
<VisualGlow>
<div className="grid grid-cols-2 gap-px overflow-hidden rounded-2xl border border-border bg-border md:grid-cols-3">
{CONSTRAINTS.map((c) => (
<div
key={c}
className="flex items-center gap-3 bg-bg px-6 py-7 text-[0.9375rem] font-medium tracking-[-0.01em] transition-colors hover:bg-surface-muted"
>
<span className="h-1.5 w-1.5 shrink-0 rounded-full bg-fg/30" />
{c}
</div>
))}
</div>
</VisualGlow>
</Section>
{/* CTA */}
<section className="relative z-10 border-t border-border">
<div className="mx-auto flex w-[min(100%-3rem,var(--container-content))] flex-col items-center px-6 py-[clamp(6rem,14vh,9rem)] text-center">
<h2 className="m-0 text-[clamp(2rem,6vw,3.5rem)] font-semibold leading-[1.02] tracking-[-0.045em]">
Explore with us.
</h2>
<p className="mx-auto mt-6 max-w-[38rem] text-pretty text-[1.0625rem] leading-[1.6] text-muted">
Start with the getting started guide, then read the graph architecture
and compile-path pages to see why the program database matters.
</p>
<div className="mt-9 flex flex-wrap items-center justify-center gap-3">
<ButtonLink href="/getting-started" variant="primary" size="lg">
Get started <ArrowRightIcon />
</ButtonLink>
<ButtonLink href="/concepts/graph-architecture" variant="default" size="lg">
Graph architecture <ArrowRightIcon />
</ButtonLink>
</div>
</div>
</section>
</main>
<footer className="relative z-10 border-t border-border py-8">
<div className="mx-auto flex w-[min(100%-3rem,var(--container-content))] flex-col items-center gap-2 text-center md:flex-row md:justify-between md:text-left">
<div className="flex items-center gap-2 text-sm font-semibold text-fg">
<LogoIcon width="14" height="12" />
<span>zerolang</span>
</div>
<p className="m-0 text-[0.8125rem] text-muted">
Experimental graph-first language design.
</p>
</div>
</footer>
</div>
);
}