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
68 changes: 68 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Docs site

# Build the Fumadocs documentation site and deploy it to GitHub Pages.
# The base path (`/skit`) is configured in docs/next.config.mjs.

on:
push:
branches: [main]
paths:
# docs/ is the site; assets/ and design/ live beside it but aren't published.
- docs/**
- '!docs/assets/**'
- '!docs/design/**'
- '!docs/mutation-ledger.md'
# The landing page renders README.md (synced in at build), so a README-only
# change must still redeploy.
- README.md
- .github/workflows/docs.yml
Comment thread
coderabbitai[bot] marked this conversation as resolved.
workflow_dispatch:

# Least privilege by default; each job opts into exactly what it needs.
permissions: {}

# Allow one concurrent deployment; let in-progress production deploys finish.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22'
cache: npm
cache-dependency-path: docs/package-lock.json
- name: Install dependencies
run: npm ci
working-directory: docs
- name: Build static site
run: npm run build
working-directory: docs
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
with:
path: docs/out

deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write # deploy to GitHub Pages
id-token: write # verify the deployment originates from this workflow
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ mutants/

# OS
.DS_Store

# Demo videos are uploaded to GitHub user-attachments, not hotlinked from the
# repo like the PNGs — keeping them out of git stops history from ballooning.
docs/assets/demo-*.mp4
22 changes: 22 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,25 @@ the hero video in all three READMEs — is hand-recorded, because VHS drives no
single shared clip (not per-locale) that the pipeline cannot regenerate, so it goes stale
silently if the UI it shows changes; re-record and re-trim it by hand (recipe in
CONTRIBUTING.md).

The demo videos (`docs/assets/demo-*.mp4`) are deliberately **not** tracked in git (they'd
balloon history); the README hero videos are uploaded to GitHub user-attachments by hand.
The PNGs, banner, and `demo-mouse.gif` stay tracked — the READMEs hotlink them via
raw.githubusercontent.

## Documentation site

`docs/` doubles as the user-facing documentation site: a Fumadocs (Next.js static-export)
app, deployed to GitHub Pages (https://t41372.github.io/skit/) by
`.github/workflows/docs.yml` on pushes to main. **To change a documentation page, edit the
MDX in `docs/content/docs/`** (sidebar order: `meta.json` there); pages carry deep reference
detail, not TUI walk-throughs. The **landing page is the repo README itself** — `index.mdx`
just `<include>`s it (synced into `docs/.generated/` by the predev/prebuild hook), so fix
landing content in `README.md`, not `index.mdx`. Verify with `cd docs && npm ci &&
npm run build`; the build runs a link checker (`scripts/check-links.mjs`) that fails on any
broken internal link or `#anchor`. Preview with `npm run dev` (http://localhost:3000/skit/en/).
Gotcha: `<include>` and Turbopack only resolve files **inside** `docs/` — never reference a
path above the project root. The docs are English-only for now and sit **outside** the i18n
coverage gate; the scaffolding (`docs/lib/i18n.ts`) is ready for zh content later. README copy
vocabulary applies — the run screen is the "launch menu", never a "form". `docs/assets/` and
`docs/design/` live beside the site and are not published to it.
158 changes: 83 additions & 75 deletions README.md

Large diffs are not rendered by default.

153 changes: 82 additions & 71 deletions README.zh-CN.md

Large diffs are not rendered by default.

153 changes: 82 additions & 71 deletions README.zh-TW.md

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# deps
/node_modules

# generated content
.source
.generated

# test & build
/coverage
/.next/
/out/
/build
*.tsbuildinfo

# misc
.DS_Store
*.pem
/.pnp
.pnp.js
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# others
.env*.local
.vercel
next-env.d.ts
25 changes: 25 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# docs

This directory is two things:

- **The documentation site** — a [Fumadocs](https://fumadocs.dev) app (Next.js
static export), deployed to GitHub Pages at https://t41372.github.io/skit/ by
`.github/workflows/docs.yml` on pushes to main.
- **Repo doc assets** — `assets/` (media the READMEs hotlink via
raw.githubusercontent; the demo `.mp4`s are deliberately untracked) and
`design/` (internal design notes). Neither is published to the site.

## Editing content

Pages are the MDX files in `content/docs/`; sidebar order lives in
`content/docs/meta.json`. English-only for now — the i18n structure is ready
(`lib/i18n.ts`), no translated content exists yet.

## Commands

```bash
npm ci # install
npm run dev # preview at http://localhost:3000/skit/en/
npm run build # static production build into out/
npm run types:check # typecheck
```
15 changes: 15 additions & 0 deletions docs/app/[lang]/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';

export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;

return <HomeLayout {...baseOptions(lang)}>{children}</HomeLayout>;
}
15 changes: 15 additions & 0 deletions docs/app/[lang]/(home)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { i18n } from '@/lib/i18n';
import { RedirectToDocs } from './redirect-to-docs';

export function generateStaticParams() {
return i18n.languages.map((lang) => ({ lang }));
}

export default async function HomePage({
params,
}: {
params: Promise<{ lang: string }>;
}) {
const { lang } = await params;
return <RedirectToDocs lang={lang} />;
}
27 changes: 27 additions & 0 deletions docs/app/[lang]/(home)/redirect-to-docs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';
import Link from 'next/link';
import { useEffect } from 'react';

// There is no separate landing page — the docs Overview (the rendered README) is
// the front door. This locale-root page forwards there; the link is the no-JS
// fallback. basePath is inlined at build time so the target is absolute and
// independent of the current trailing slash.
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? '/skit';

export function RedirectToDocs({ lang }: { lang: string }) {
useEffect(() => {
window.location.replace(`${basePath}/${lang}/docs/`);
}, [lang]);

return (
<main className="flex flex-1 flex-col items-center justify-center px-4 text-center">
<p className="text-fd-muted-foreground">
Redirecting to the{' '}
<Link href={`/${lang}/docs`} className="text-fd-foreground underline">
documentation
</Link>
</p>
</main>
);
}
67 changes: 67 additions & 0 deletions docs/app/[lang]/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
import {
DocsBody,
DocsDescription,
DocsPage,
DocsTitle,
MarkdownCopyButton,
ViewOptionsPopover,
} from 'fumadocs-ui/layouts/docs/page';
import { notFound } from 'next/navigation';
import { getMDXComponents } from '@/components/mdx';
import type { Metadata } from 'next';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { gitConfig } from '@/lib/shared';

type PageParams = { lang: string; slug?: string[] };

export default async function Page(props: { params: Promise<PageParams> }) {
const { lang, slug } = await props.params;
const page = source.getPage(slug, lang);
if (!page) notFound();

const MDX = page.data.body;
const markdownUrl = getPageMarkdownUrl(page).url;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription className="mb-0">{page.data.description}</DocsDescription>
<div className="flex flex-row gap-2 items-center border-b pb-6">
<MarkdownCopyButton markdownUrl={markdownUrl} />
<ViewOptionsPopover
markdownUrl={markdownUrl}
githubUrl={`https://github.com/${gitConfig.user}/${gitConfig.repo}/blob/${gitConfig.branch}/docs/content/docs/${page.path}`}
/>
</div>
<DocsBody>
<MDX
components={getMDXComponents({
// this allows you to link to other pages with relative file paths
a: createRelativeLink(source, page),
})}
/>
</DocsBody>
</DocsPage>
);
}

export async function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata(props: {
params: Promise<PageParams>;
}): Promise<Metadata> {
const { lang, slug } = await props.params;
const page = source.getPage(slug, lang);
if (!page) notFound();

return {
title: page.data.title,
description: page.data.description,
openGraph: {
images: getPageImage(page).url,
},
};
}
20 changes: 20 additions & 0 deletions docs/app/[lang]/docs/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { source } from '@/lib/source';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/lib/layout.shared';

export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;

return (
<DocsLayout {...baseOptions(lang)} tree={source.getPageTree(lang)}>
{children}
</DocsLayout>
);
}
33 changes: 33 additions & 0 deletions docs/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Inter } from 'next/font/google';
import { i18nProvider } from 'fumadocs-ui/i18n';
import type { ReactNode } from 'react';
import { Provider } from '@/components/provider';
import { i18n } from '@/lib/i18n';
import { translations } from '@/lib/layout.shared';
import '../global.css';

const inter = Inter({
subsets: ['latin'],
});

export function generateStaticParams() {
return i18n.languages.map((lang) => ({ lang }));
}

export default async function Layout({
params,
children,
}: {
params: Promise<{ lang: string }>;
children: ReactNode;
}) {
const { lang } = await params;

return (
<html lang={lang} className={inter.className} suppressHydrationWarning>
<body className="flex flex-col min-h-screen">
<Provider i18n={i18nProvider(translations, lang)}>{children}</Provider>
</body>
</html>
);
}
9 changes: 9 additions & 0 deletions docs/app/api/search/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { source } from '@/lib/source';
import { createFromSource } from 'fumadocs-core/search/server';

export const revalidate = false;

export const { staticGET: GET } = createFromSource(source, {
// https://docs.orama.com/docs/orama-js/supported-languages
language: 'english',
});
22 changes: 22 additions & 0 deletions docs/app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';

html {
scrollbar-gutter: stable;
}

html > body[data-scroll-locked] {
margin-right: 0px !important;
--removed-body-scroll-bar-size: 0px !important;
}

/* The landing page renders the repo README, whose badge row is a paragraph of
linked images (`[![…](badge)](link)`). The prose theme makes images block, so
without this each badge stacks vertically. Linked images flow inline in a row;
bare images (banner, screenshots) stay block. */
.prose p a > img {
display: inline-block;
margin: 0 0.35rem 0.4rem 0;
vertical-align: middle;
}
Binary file added docs/app/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions docs/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { getLLMText, source } from '@/lib/source';

export const revalidate = false;

export async function GET() {
const scan = source.getPages().map(getLLMText);
const scanned = await Promise.all(scan);

return new Response(scanned.join('\n\n'));
}
Loading