Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
17 changes: 16 additions & 1 deletion packages/web/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { createFromSource } from "fumadocs-core/search/server"
import { source } from "@/lib/source"

export const { GET } = createFromSource(source)
const search = createFromSource(source)
const cache = "public, max-age=0, s-maxage=86400, stale-while-revalidate=604800"

export const runtime = "nodejs"

export async function GET(request: Request): Promise<Response> {
const response = await search.GET(request)
const headers = new Headers(response.headers)
headers.set("cache-control", cache)

return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
})
}
Binary file added packages/web/app/apple-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions packages/web/app/docs.md/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { readdir, readFile } from "node:fs/promises"
import { join } from "node:path"

export const runtime = "nodejs"
export const dynamic = "force-static"
export const revalidate = false

function roots(): string[] {
return [join(process.cwd(), "content/docs"), join(process.cwd(), "packages/web/content/docs")]
}

async function files(): Promise<string[]> {
for (const root of roots()) {
try {
const list = await readdir(root)
const out = list.filter((item) => item.endsWith(".mdx")).sort()
if (out.length > 0) return out.map((item) => join(root, item))
} catch {}
}

return []
}

function clean(text: string): string {
return text.replace(/^---[\s\S]*?---\s*/m, "").trim()
}

function name(path: string): string {
const item = path.split("/").pop() ?? "doc"
return item.replace(/\.mdx$/, "")
}

export async function GET(): Promise<Response> {
const paths = await files()
const chunks: string[] = ["# cruel docs", ""]

for (const path of paths) {
const raw = await readFile(path, "utf8")
const key = name(path)
const title = key === "index" ? "getting started" : key
chunks.push(`## ${title}`)
chunks.push("")
chunks.push(clean(raw))
chunks.push("")
}

return new Response(chunks.join("\n"), {
headers: {
"content-type": "text/markdown; charset=utf-8",
"cache-control": "public, max-age=0, s-maxage=86400, stale-while-revalidate=604800",
},
})
}
46 changes: 45 additions & 1 deletion packages/web/app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,42 @@
import { readFile } from "node:fs/promises"
import { join } from "node:path"
import defaultMdxComponents from "fumadocs-ui/mdx"
import { DocsBody, DocsPage } from "fumadocs-ui/page"
import type { MDXContent } from "mdx/types"
import { notFound } from "next/navigation"
import { Anchor } from "@/components/anchor"
import { Copy } from "@/components/copy"
import { source } from "@/lib/source"

export const dynamic = "force-static"
export const dynamicParams = false
export const revalidate = false

function clean(text: string): string {
return text.replace(/^---[\s\S]*?---\s*/m, "").trim()
}

function name(slug?: string[]): string {
if (!slug || slug.length === 0) return "index"
return slug.join("/")
}

async function markdown(slug?: string[]): Promise<string> {
const file = `${name(slug)}.mdx`
const paths = [
join(process.cwd(), "content/docs", file),
join(process.cwd(), "packages/web/content/docs", file),
]

for (const path of paths) {
try {
return clean(await readFile(path, "utf8"))
} catch {}
}

return ""
}

export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params
const page = source.getPage(params.slug)
Expand All @@ -17,9 +50,20 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>

const resolved = data.load ? await data.load() : data
const MDX = resolved.body
const text = await markdown(params.slug)

return (
<DocsPage toc={resolved.toc}>
<DocsPage
toc={resolved.toc}
tableOfContent={{
style: "clerk",
footer: <Copy text={text} />,
}}
tableOfContentPopover={{
style: "clerk",
}}
>
<Anchor />
<DocsBody>
<h1>{page.data.title}</h1>
<p className="text-fd-muted-foreground text-lg">{page.data.description}</p>
Expand Down
35 changes: 33 additions & 2 deletions packages/web/app/docs/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
left: 20px !important;
}

.docs-panel [data-radix-scroll-area-viewport],
.docs-panel [style] {
.docs-panel [data-radix-scroll-area-viewport] {
mask-image: none !important;
-webkit-mask-image: none !important;
}
Expand All @@ -35,6 +34,38 @@
-webkit-mask: none !important;
}

.docs-panel #nd-toc [style*="--fd-top"] {
background: rgba(255, 255, 255, 0.22) !important;
}

.docs-panel .prose :is(h2, h3, h4, h5, h6) > a.peer {
display: inline-flex;
align-items: center;
position: relative;
padding-inline-end: 14px;
margin-inline-end: -14px;
}

.docs-panel .prose :is(h2, h3, h4, h5, h6) > svg {
pointer-events: auto;
cursor: pointer;
transition: opacity 140ms ease;
}

.docs-panel .prose :is(h2, h3, h4, h5, h6) > a.peer[data-copied="true"] + svg {
opacity: 1 !important;
color: rgba(255, 255, 255, 0.88);
}

.docs-panel .prose :is(h2, h3, h4, h5, h6)[data-lock="true"] > svg {
opacity: 0 !important;
transition: none !important;
}

.docs-panel .prose :is(h2, h3, h4, h5, h6):hover:not([data-lock="true"]) > svg {
opacity: 1 !important;
}

[data-state="open"][class*="backdrop-blur"],
[data-state="closed"][class*="backdrop-blur"] {
background: rgba(0, 0, 0, 0.6) !important;
Expand Down
11 changes: 11 additions & 0 deletions packages/web/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,14 @@
radial-gradient(140% 120% at 90% 90%, rgba(140, 100, 100, 0.03) 0%, transparent 55%);
background-color: #0a0a0a;
}

[class*="shadow-2xl"] [class*="max-h-[460px]"][class*="overflow-y-auto"] {
scrollbar-width: none;
-ms-overflow-style: none;
}

[class*="shadow-2xl"] [class*="max-h-[460px]"][class*="overflow-y-auto"]::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
21 changes: 19 additions & 2 deletions packages/web/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,43 @@
import { RootProvider } from "fumadocs-ui/provider/next"
import { GeistMono } from "geist/font/mono"
import type { Metadata } from "next"
import type { Metadata, Viewport } from "next"
import "./globals.css"

export const metadata: Metadata = {
title: "cruel",
description: "chaos testing with zero mercy",
metadataBase: new URL("https://cruel.dev"),
icons: {
icon: "/icon.svg",
apple: "/apple-icon.png",
},
openGraph: {
title: "cruel",
description: "chaos testing with zero mercy",
url: "https://cruel.dev",
siteName: "cruel",
type: "website",
images: [
{
url: "/og.png",
width: 1200,
height: 630,
alt: "cruel",
},
],
},
twitter: {
card: "summary",
card: "summary_large_image",
title: "cruel",
description: "chaos testing with zero mercy",
images: ["/og.png"],
},
}

export const viewport: Viewport = {
themeColor: "#0a0a0a",
}

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${GeistMono.variable} dark`}>
Expand Down
56 changes: 56 additions & 0 deletions packages/web/app/llms.txt/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { readdir } from "node:fs/promises"
import { join } from "node:path"

export const runtime = "nodejs"
export const dynamic = "force-static"
export const revalidate = false

function roots(): string[] {
return [join(process.cwd(), "content/docs"), join(process.cwd(), "packages/web/content/docs")]
}

async function pages(): Promise<string[]> {
for (const root of roots()) {
try {
const list = await readdir(root)
return list
.filter((item) => item.endsWith(".mdx"))
.map((item) => item.replace(/\.mdx$/, ""))
.sort()
} catch {}
}

return []
}

function url(name: string): string {
if (name === "index") return "https://cruel.dev/docs"
return `https://cruel.dev/docs/${name}`
}

export async function GET(): Promise<Response> {
const list = await pages()
const lines: string[] = [
"project: cruel",
"site: https://cruel.dev",
"summary: chaos engineering for ai sdk and async apis",
"",
"docs_markdown: https://cruel.dev/docs.md",
"docs_root: https://cruel.dev/docs",
"",
"docs_pages:",
]

for (const item of list) {
lines.push(`- ${url(item)}`)
}

lines.push("")

return new Response(lines.join("\n"), {
headers: {
"content-type": "text/plain; charset=utf-8",
"cache-control": "public, max-age=0, s-maxage=86400, stale-while-revalidate=604800",
},
})
}
28 changes: 26 additions & 2 deletions packages/web/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { readFile } from "node:fs/promises"
import { join } from "node:path"
import Link from "next/link"

const code = `import { cruel } from "cruel"
Expand All @@ -10,7 +12,29 @@ const api = cruel(fetch, {

const res = await api("https://api.example.com")`

export default function Page() {
async function version(): Promise<string> {
const paths = [
join(process.cwd(), "../cruel/package.json"),
join(process.cwd(), "packages/cruel/package.json"),
]

for (const path of paths) {
try {
const file = await readFile(path, "utf8")
const data: unknown = JSON.parse(file)
if (typeof data === "object" && data !== null) {
const value = Reflect.get(data, "version")
if (typeof value === "string" && value.length > 0) return `v${value}`
}
} catch {}
}

return "v0.0.0"
}

export default async function Page() {
const tag = await version()

return (
<main className="h-dvh bg-[#f5f3ef] flex items-center justify-center p-[var(--docs-pad)]">
<div className="relative w-full h-full rounded-[var(--panel-radius)] border border-white/10 overflow-hidden">
Expand Down Expand Up @@ -81,7 +105,7 @@ export default function Page() {
</div>

<div className="mx-2 sm:mx-[10px] mb-2 sm:mb-[10px] rounded-b-[calc(var(--panel-radius)-8px)] border-t border-white/[0.06] h-[44px] sm:h-[48px] flex items-center justify-between px-4 sm:px-5 text-[11px] sm:text-[12px]">
<div className="text-white/20 italic">v1.0.1</div>
<div className="text-white/20 italic">{tag}</div>
<div className="text-white/35 select-all cursor-text">
<span className="text-white/15 select-none">$ </span>
bun add cruel
Expand Down
11 changes: 11 additions & 0 deletions packages/web/app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { MetadataRoute } from "next"

export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: "/",
},
sitemap: "https://cruel.dev/sitemap.xml",
}
}
20 changes: 20 additions & 0 deletions packages/web/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { MetadataRoute } from "next"
import { source } from "@/lib/source"

export default function sitemap(): MetadataRoute.Sitemap {
const base = "https://cruel.dev"
const time = new Date()
const set = new Set<string>([`${base}/`, `${base}/docs`, `${base}/story`])

for (const item of source.generateParams()) {
const slug = item.slug?.join("/")
set.add(slug ? `${base}/docs/${slug}` : `${base}/docs`)
}

return Array.from(set).map((url) => ({
url,
lastModified: time,
changeFrequency: "weekly",
priority: url === `${base}/` ? 1 : 0.8,
}))
}
Loading