diff --git a/.gitignore b/.gitignore index 4dd08f0b..01495065 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,3 @@ artifacts/ .dawn/ .superpowers/ .vercel -.knot/ diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index e65ab86d..37be5d78 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -66,10 +66,6 @@ Use this path only when you intentionally want the generated app wired to the lo - `node scripts/publish-smoke.mjs` runs the publish smoke check. - `node scripts/check-docs.mjs` checks that the website docs contain the required current-copy text. -## Brand assets - -- Rebuild the README demo gif with `./docs/brand/build-gif.sh` (requires `brew install vhs`). The script scaffolds a temp app, starts `node docs/brand/stub-openai.mjs --fixture docs/brand/quickstart-fixture.json --port 4317`, and runs `vhs docs/brand/quickstart.tape`. See [docs/brand/README.md](./docs/brand/README.md) for details and the fixture-recapture flow. - ## Verification And Test Lanes The repo uses a layered verification model: diff --git a/README.md b/README.md index 1396d084..ca78658d 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,6 @@ The meta-framework for LangGraph. Author agents and workflows as filesystem routes, get types and a local dev server for free, and ship to LangSmith with one command. -

- Dawn quickstart — scaffold a route and invoke it in under a minute -

- ## Why Dawn? - **Kill the graph boilerplate.** Export one `agent({ model, systemPrompt })` descriptor. Dawn discovers it, binds route-local tools, and emits a `langgraph.json` package ready for LangSmith. diff --git a/apps/web/app/CLAUDE.md/route.ts b/apps/web/app/CLAUDE.md/route.ts new file mode 100644 index 00000000..36e706f1 --- /dev/null +++ b/apps/web/app/CLAUDE.md/route.ts @@ -0,0 +1,13 @@ +import { readFile } from "node:fs/promises" +import path from "node:path" +import { NextResponse } from "next/server" + +// CLAUDE.md and AGENTS.md serve the same content — Claude Code reads CLAUDE.md +// while other coding agents (Codex, etc.) read AGENTS.md. Single source of truth +// at templates/AGENTS.md prevents drift. +export async function GET() { + const body = await readFile(path.join(process.cwd(), "content/templates/AGENTS.md"), "utf8") + return new NextResponse(body, { + headers: { "Content-Type": "text/markdown; charset=utf-8" }, + }) +} diff --git a/apps/web/app/blog/[slug]/opengraph-image.tsx b/apps/web/app/blog/[slug]/opengraph-image.tsx index b205a204..91ca438c 100644 --- a/apps/web/app/blog/[slug]/opengraph-image.tsx +++ b/apps/web/app/blog/[slug]/opengraph-image.tsx @@ -8,29 +8,11 @@ export function generateImageParams() { return getAllPosts().map((p) => ({ slug: p.slug })) } -// Fraunces variable font (Open Font License, hosted by google/fonts on GitHub). -// next/og needs the raw font bytes — fetched at build time per generated image. -const FRAUNCES_URL = - "https://github.com/google/fonts/raw/main/ofl/fraunces/Fraunces%5BSOFT%2CWONK%2Copsz%2Cwght%5D.ttf" - -async function loadFraunces(): Promise { - try { - const res = await fetch(FRAUNCES_URL) - if (!res.ok) return null - return await res.arrayBuffer() - } catch { - return null - } -} - export default async function Image({ params }: { params: { slug: string } }) { const post = getPost(params.slug) const title = post?.title ?? "Dawn" const eyebrow = post?.type === "release" ? `Release · v${post.version}` : "Essay" - const fraunces = await loadFraunces() - const titleFontFamily = fraunces ? "Fraunces" : "ui-serif, Georgia, serif" - return new ImageResponse(
{title}
dawnai.org/blog
, - { - ...size, - ...(fraunces && { - fonts: [{ name: "Fraunces", data: fraunces, weight: 600, style: "normal" }], - }), - }, + size, ) } diff --git a/apps/web/app/blog/[slug]/page.tsx b/apps/web/app/blog/[slug]/page.tsx index 7c1e80ca..b0a54d35 100644 --- a/apps/web/app/blog/[slug]/page.tsx +++ b/apps/web/app/blog/[slug]/page.tsx @@ -5,7 +5,7 @@ import { PostMeta } from "../../components/blog/PostMeta" import { getAllPosts, getPost, getRelatedPosts } from "../../components/blog/post-index" import { DocsTOC } from "../../components/docs/DocsTOC" import { RelatedCards } from "../../components/docs/RelatedCards" -import { FinalCta } from "../../components/landing/FinalCta" +import { CtaSection } from "../../components/landing/CtaSection" import { ReadingLayout } from "../../components/ReadingLayout" interface PageProps { @@ -50,8 +50,8 @@ export default async function BlogPostPage({ params }: PageProps) { if (!post) notFound() // Dynamic import resolves at build time because generateStaticParams enumerates slugs. - // post.sourceFile is the on-disk filename — authoritative even if frontmatter date drifts. - const mod = (await import(`../../../content/blog/${post.sourceFile}`)) as { + // Filename convention: YYYY-MM-DD-.mdx, reconstructed from post.date + post.slug. + const mod = (await import(`../../../content/blog/${post.date}-${post.slug}.mdx`)) as { default: React.ComponentType } const MdxContent = mod.default @@ -75,7 +75,7 @@ export default async function BlogPostPage({ params }: PageProps) { )} - + ) } diff --git a/apps/web/app/blog/page.tsx b/apps/web/app/blog/page.tsx index aae67ef7..08f4399d 100644 --- a/apps/web/app/blog/page.tsx +++ b/apps/web/app/blog/page.tsx @@ -2,8 +2,7 @@ import { FeaturedPostCard } from "../components/blog/FeaturedPostCard" import { PostCard } from "../components/blog/PostCard" import { getAllPosts, getAllTags, getFeaturedPost } from "../components/blog/post-index" import { TagChips } from "../components/blog/TagChips" -import { FinalCta } from "../components/landing/FinalCta" -import { Eyebrow } from "../components/ui/Eyebrow" +import { CtaSection } from "../components/landing/CtaSection" export default function BlogIndexPage() { const all = getAllPosts() @@ -14,16 +13,16 @@ export default function BlogIndexPage() { return ( <>
-
- Blog +
+ Blog

Notes on Dawn

-

+

Writing on the agent stack, type-safety, and the tools we're building.

@@ -34,7 +33,7 @@ export default function BlogIndexPage() { ))}
- + ) } diff --git a/apps/web/app/blog/tags/[tag]/page.tsx b/apps/web/app/blog/tags/[tag]/page.tsx index 103b86ff..6d19f092 100644 --- a/apps/web/app/blog/tags/[tag]/page.tsx +++ b/apps/web/app/blog/tags/[tag]/page.tsx @@ -4,7 +4,7 @@ import { notFound } from "next/navigation" import { PostCard } from "../../../components/blog/PostCard" import { getAllTags, getPostsByTag } from "../../../components/blog/post-index" import { TagChips } from "../../../components/blog/TagChips" -import { FinalCta } from "../../../components/landing/FinalCta" +import { CtaSection } from "../../../components/landing/CtaSection" interface PageProps { readonly params: Promise<{ tag: string }> @@ -32,14 +32,17 @@ export default async function TagPage({ params }: PageProps) { return ( <>
- + ← All posts

- Posts tagged {tag} + Posts tagged {tag}

@@ -48,7 +51,7 @@ export default async function TagPage({ params }: PageProps) { ))}
- + ) } diff --git a/apps/web/app/brand/page.tsx b/apps/web/app/brand/page.tsx new file mode 100644 index 00000000..1ab0ecdb --- /dev/null +++ b/apps/web/app/brand/page.tsx @@ -0,0 +1,187 @@ +import type { Metadata } from "next" +import Image from "next/image" +import Link from "next/link" + +export const metadata: Metadata = { + title: "Brand Assets", + description: + "Download official Dawn AI logos, icons, favicons, social images, and machine-readable asset metadata.", +} + +const downloads = [ + { + label: "Horizontal logo", + format: "SVG", + href: "/brand/dawn-logo-horizontal-white.svg", + note: "White horizontal logo for dark backgrounds, website headers, and documentation.", + }, + { + label: "Icon", + format: "SVG", + href: "/brand/dawn-icon-white.svg", + note: "Compact mark for logo grids, integrations, and small UI surfaces.", + }, + { + label: "Social avatar", + format: "PNG", + href: "/social/dawn-social-avatar-white-on-black-1024.png", + note: "Square 1024px avatar for profiles, marketplaces, and social surfaces.", + }, + { + label: "Favicon", + format: "ICO", + href: "/favicon.ico", + note: "Browser favicon file for web projects and references.", + }, + { + label: "Web manifest", + format: "JSON", + href: "/site.webmanifest", + note: "Installable web app metadata for browsers and app surfaces.", + }, + { + label: "Asset manifest", + format: "JSON", + href: "/brand/assets.json", + note: "Machine-readable index for coding agents and automation.", + }, +] as const + +const dos = [ + "Use the official files as provided.", + "Use white assets on dark backgrounds and black assets on light backgrounds.", + "Keep clear space around the logo and icon.", +] as const + +const donts = [ + "Do not stretch, recolor, redraw, or modify the mark.", + "Do not place the logo on low-contrast backgrounds.", + "Do not use Dawn marks in a way that implies endorsement.", +] as const + +export default function BrandPage() { + return ( +
+
+
+

+ Brand Assets +

+

+ Dawn Brand Assets +

+

+ Official Dawn AI logos, icons, favicons, social images, and machine-readable metadata + for developers, documentation, and coding agents. +

+ +
+ +
+
+ Dawn AI horizontal logo +
+

+ Use the horizontal logo when space allows. Use the icon for compact placements. +

+
+
+ +
+
+

Common Downloads

+

+ Direct links for the files developers and agents most often need. +

+
+ +
+ {downloads.map((asset) => ( + + + {asset.format} + +

{asset.label}

+

{asset.note}

+
+ ))} +
+
+ +
+
+

Usage Guidance

+
+
+

Do

+
    + {dos.map((item) => ( +
  • + {item} +
  • + ))} +
+
+
+

Don't

+
    + {donts.map((item) => ( +
  • + {item} +
  • + ))} +
+
+
+
+ +
+

For Coding Agents

+

+ Use /brand/assets.json as the canonical + machine-readable index. It lists the full ZIP, common direct URLs, formats, dimensions, + background guidance, and recommended use cases without requiring HTML scraping. +

+
+ + Open assets.json + + + llms.txt + + + llms-full.txt + +
+
+
+
+ ) +} diff --git a/apps/web/app/components/BrandLogo.tsx b/apps/web/app/components/BrandLogo.tsx index 848a760e..9e966778 100644 --- a/apps/web/app/components/BrandLogo.tsx +++ b/apps/web/app/components/BrandLogo.tsx @@ -20,7 +20,7 @@ export function BrandLogo({ className, imageClassName, variant = "light" }: Prop return ( - $ {command} + $ {command} {open && (
{ if (e.key === "Escape") close() @@ -174,11 +174,11 @@ export function DocsSearch({ index }: Props) { > {/* biome-ignore lint/a11y/noStaticElementInteractions: wrapper stops modal-close propagation; roles are on ancestor dialog */}
e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} > -
+
Search @@ -203,12 +203,12 @@ export function DocsSearch({ index }: Props) { }} onKeyDown={onInputKey} placeholder="Search Dawn docs..." - className="flex-1 bg-transparent text-ink placeholder-text-muted focus:outline-none text-sm" + className="flex-1 bg-transparent text-text-primary placeholder-text-muted focus:outline-none text-sm" /> @@ -216,7 +216,7 @@ export function DocsSearch({ index }: Props) {
    {results.length === 0 ? ( -
  • +
  • No results for "{query}"
  • ) : ( @@ -228,12 +228,12 @@ export function DocsSearch({ index }: Props) { onMouseEnter={() => setActive(i)} onClick={() => navigate(r.href)} className={`w-full text-left px-4 py-2.5 flex items-center gap-3 ${ - i === active ? "bg-accent-saas/10" : "" + i === active ? "bg-accent-amber/10" : "" }`} > {r.section} @@ -241,13 +241,13 @@ export function DocsSearch({ index }: Props) { {r.title} {r.heading && ( - + # {r.heading.text} )} diff --git a/apps/web/app/components/docs/DocsSidebar.tsx b/apps/web/app/components/docs/DocsSidebar.tsx index 25319f40..b84b2e7d 100644 --- a/apps/web/app/components/docs/DocsSidebar.tsx +++ b/apps/web/app/components/docs/DocsSidebar.tsx @@ -15,15 +15,15 @@ export function DocsSidebar({ searchIndex }: Props) { return (
    -

    - +

    + Documentation