diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1e432ce..85422c97 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,27 +2,42 @@ name: 'Build' on: workflow_dispatch: workflow_call: + pull_request: push: branches: - 'main' jobs: - deploy: - name: 'Deploy' + build: + name: 'Build' runs-on: ubuntu-latest steps: - name: 'Checkout' - uses: actions/checkout@v4 + uses: actions/checkout@v7 + + # Bun installs and runs the scripts; Next.js itself still builds on Node. + - name: 'Setup Bun' + uses: oven-sh/setup-bun@v2 + with: + bun-version-file: package.json + - name: 'Setup Node' - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: - node-version: 20 - - uses: pnpm/action-setup@v3 - name: Install pnpm + node-version: 22 + + - name: 'Cache Bun packages' + uses: actions/cache@v6 with: - version: 10 - run_install: true # pnpm will install dependencies recursively. + path: ~/.bun/install/cache + key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} + restore-keys: bun-${{ runner.os }}- + + - name: 'Install dependencies' + run: bun install --frozen-lockfile + + - name: 'Typecheck' + run: bun run typecheck - - name: 'Build' - run: | - pnpm build \ No newline at end of file + - name: 'Build' + run: bun run build diff --git a/.gitignore b/.gitignore index 0595129c..07ba78de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,15 @@ # Dependencies /node_modules -# Production -/build +# Production (static export) +/out # Generated files -.docusaurus -.cache-loader +/.next +/.source +next-env.d.ts +*.tsbuildinfo +.netlify # Misc .DS_Store @@ -22,3 +25,6 @@ yarn-error.log* /.idea package-lock.json + +# design skill working files +.taste-skill diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..af3b7c84 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ + + +## This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. + +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean. + + diff --git a/README.md b/README.md index 04c8b5cc..3a03b8e3 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,97 @@ # Pelican Docs -The source for the documentation at [pelican.dev/docs](https://pelican.dev/docs), covering [Pelican Panel](https://github.com/pelican/panel) and [Wings](https://github.com/pelican/wings). +The website and documentation at [pelican.dev](https://pelican.dev), covering [Pelican Panel](https://github.com/pelican/panel) and [Wings](https://github.com/pelican/wings). +Built with [Next.js](https://nextjs.org) and [Fumadocs](https://fumadocs.dev) and served by Netlify as a static site. -Pelican docs are created with [Docusaurus](https://docusaurus.io). +## Running it locally -## Building +You need Node.js 22 or newer (Next.js builds on Node) and [Bun](https://bun.com) 1.3 or newer (installs and runs the scripts). -To test changes locally install the dependencies and run the server using pnpm. +```sh +bun install +bun run dev # http://localhost:3000, reloads as you edit +bun run build # the production site, written to out/ +``` + +## Where things are + +| Path | What it is | +| --- | --- | +| `content/docs/` | The documentation. One `.mdx` file per page; the URL follows the folder. | +| `content/docs/**/meta.json` | Sidebar order and section names for that folder. | +| `content/pages/` | Stand-alone pages: `/support` and `/faq`. | +| `app/(home)/page.tsx` | The homepage. | +| `public/` | Files served as they are, including the `update-panel.sh` and `migrate-*.sh` scripts. | +| `netlify.toml` | Build settings and redirects such as `/discord`. | + +## Writing a page + +Create `content/docs/
/.mdx` and add `` to that folder's `meta.json` +so it shows up in the sidebar. + +```mdx +--- +title: Installing Wings +description: One sentence shown under the title and in search results. +--- + +Your content, in Markdown. +``` + +The title goes in the frontmatter, not as a `# Heading`. + +### Callouts + +```mdx +:::tip +Short tips work like this. +::: + + + Or like this, when you want a title. Types: note, info, tip, warning, danger. + +``` -```shell -pnpm i -pnpm run start +### Tabs + +```mdx + + … + … + … + ``` + +Tabs with the same `groupId` switch together on every page and remember the reader's choice. +Use `groupId="webserver"` with the values `nginx`, `apache` and `caddy`, and `groupId="ssl"` +with `https` and `http`, so they also follow the reader's setup (below). + +### Code blocks + +````mdx +```nginx title="/etc/nginx/sites-available/pelican.conf" {3,5} +```` + +`title` shows a file name above the block and `{3,5}` highlights lines 3 and 5. + +### Filling in the reader's setup + +Put a setup box on a page: + +```mdx + +``` + +Then add `setup` to any code block that should use the reader's values: + +````mdx +```nginx setup title="/etc/nginx/sites-available/pelican.conf" +server_name ; +root /var/www/pelican/public; +fastcgi_pass unix:/run/php/php8.5-fpm.sock; +``` +```` + +In blocks marked `setup`, ``, `/var/www/pelican` and the version in `php8.5` are replaced +with what the reader entered. Their choices are saved in their browser, so other pages pick them up too. +Blocks without `setup` are never changed. diff --git a/app/(landing)/[slug]/page.tsx b/app/(landing)/[slug]/page.tsx new file mode 100644 index 00000000..0639e02a --- /dev/null +++ b/app/(landing)/[slug]/page.tsx @@ -0,0 +1,85 @@ +import { notFound } from 'next/navigation'; +import type { Metadata } from 'next'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { pagesSource } from '@/lib/source'; +import { extractFaq, faqPageJsonLd, jsonLdScript, ogImage, pageMetadata } from '@/lib/og'; +import { getMDXComponents } from '@/mdx-components'; +import { SiteHeader } from '@/components/home/site-header'; +import { SiteFooterNav } from '@/components/home/sections'; + +export const dynamicParams = false; + +// Short lines under each page's title, in the same voice as the homepage. The +// frontmatter description is the longer one used for search results. +const LEDES: Record = { + faq: 'Straight answers about Pelican, Wings, licensing and plugins.', +}; + +// Social card per page; every content/pages page needs one in the /og contract. +const OG_IMAGES: Record = { + faq: ogImage.faq, +}; + +/** FAQPage structured data from the page's own
questions, so it matches what readers see. */ +function faqJsonLd(slug: string, path: string) { + if (slug !== 'faq') return null; + const entries = extractFaq(readFileSync(join(process.cwd(), 'content/pages', path), 'utf8'), `/${slug}/`); + return entries.length > 0 ? jsonLdScript(faqPageJsonLd(entries, `/${slug}/`)) : null; +} + +/** + * Stand-alone MDX pages from content/pages (today only /faq) in the homepage's design. + * /support is its own route (app/(landing)/support/page.tsx), not MDX. + */ +export default async function Page(props: PageProps<'/[slug]'>) { + const { slug } = await props.params; + const page = pagesSource.getPage([slug]); + if (!page) notFound(); + + const MDX = page.data.body; + const jsonLd = faqJsonLd(slug, page.path); + + return ( + <> + {jsonLd &&