diff --git a/docs/assets/website-fumadocs-astryx/after-desktop-docs.png b/docs/assets/website-fumadocs-astryx/after-desktop-docs.png new file mode 100644 index 000000000..ba7ef1e9c Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-desktop-docs.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-desktop-home.png b/docs/assets/website-fumadocs-astryx/after-desktop-home.png new file mode 100644 index 000000000..c4b92aa4b Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-desktop-home.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-mobile-docs-dark.png b/docs/assets/website-fumadocs-astryx/after-mobile-docs-dark.png new file mode 100644 index 000000000..c02a0ae1e Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-mobile-docs-dark.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-mobile-docs-navigation.png b/docs/assets/website-fumadocs-astryx/after-mobile-docs-navigation.png new file mode 100644 index 000000000..e30dfe876 Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-mobile-docs-navigation.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-mobile-docs.png b/docs/assets/website-fumadocs-astryx/after-mobile-docs.png new file mode 100644 index 000000000..69d466d55 Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-mobile-docs.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-mobile-home.png b/docs/assets/website-fumadocs-astryx/after-mobile-home.png new file mode 100644 index 000000000..1875d6db0 Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-mobile-home.png differ diff --git a/docs/assets/website-fumadocs-astryx/after-opengraph.png b/docs/assets/website-fumadocs-astryx/after-opengraph.png new file mode 100644 index 000000000..f4992006a Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/after-opengraph.png differ diff --git a/docs/assets/website-fumadocs-astryx/before-desktop-docs.png b/docs/assets/website-fumadocs-astryx/before-desktop-docs.png new file mode 100644 index 000000000..873b3e37b Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/before-desktop-docs.png differ diff --git a/docs/assets/website-fumadocs-astryx/before-desktop-home.png b/docs/assets/website-fumadocs-astryx/before-desktop-home.png new file mode 100644 index 000000000..c813ab3a2 Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/before-desktop-home.png differ diff --git a/docs/assets/website-fumadocs-astryx/before-mobile-docs.png b/docs/assets/website-fumadocs-astryx/before-mobile-docs.png new file mode 100644 index 000000000..1a6190c4c Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/before-mobile-docs.png differ diff --git a/docs/assets/website-fumadocs-astryx/before-mobile-home.png b/docs/assets/website-fumadocs-astryx/before-mobile-home.png new file mode 100644 index 000000000..220f09ded Binary files /dev/null and b/docs/assets/website-fumadocs-astryx/before-mobile-home.png differ diff --git a/website/.gitignore b/website/.gitignore index d45f74092..47641617c 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -12,13 +12,15 @@ # testing /coverage +/output/ +/.playwright-cli/ # next.js /.next/ /out/ -# docs synced from ../docs at build time (single source of truth lives there) -/content/docs/ +# Fumadocs' generated source adapter (the Markdown itself stays in the repo root) +/.source/ # test262 dashboard snapshot synced from CI artifacts at build time /content/test262/ diff --git a/website/README.md b/website/README.md index c4bc8e0a9..b3efa9aef 100644 --- a/website/README.md +++ b/website/README.md @@ -1,6 +1,6 @@ # GocciaScript website -The marketing/docs site for GocciaScript — a Next.js 16 app under `website/`. Markdown content for the docs section is synced from `../docs/` at build time via `scripts/sync-docs.mjs`. +The marketing/docs site for GocciaScript — a Next.js 16 app under `website/`. Fumadocs reads `../README.md` and `../docs/**/*.md` directly, while Astryx provides the shared shell and generic controls. The warm-paper GocciaScript theme remains project-owned. Bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). @@ -9,20 +9,15 @@ Bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/ First, run the development server: ```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev +bun install --frozen-lockfile +bun run dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -You can start editing the page by modifying `src/app/page.tsx`. The page auto-updates as you edit the file. +Edit route UI under `src/app/` and `src/components/`. Edit documentation in the repository root; `source.config.ts` defines the collection and generated routes update automatically. -Fonts (Instrument Serif + IBM Plex Sans) are loaded directly in `src/app/layout.tsx` via `` from Google Fonts; the OG image generator (`src/app/opengraph-image.tsx`) fetches the same `.ttf` files at build time. +Fonts (Instrument Serif, IBM Plex Sans, and JetBrains Mono) are loaded through `next/font` in `src/app/layout.tsx`; the OG image generator (`src/app/opengraph-image.tsx`) embeds the matching display and body fonts. ## Learn More diff --git a/website/bun.lock b/website/bun.lock index 86728cdca..9daff16e9 100644 --- a/website/bun.lock +++ b/website/bun.lock @@ -5,7 +5,11 @@ "": { "name": "website", "dependencies": { + "@astryxdesign/core": "^0.1.6", + "@stylexjs/stylex": "^0.19.0", "@vercel/blob": "^2.4.0", + "fumadocs-core": "^16.11.5", + "fumadocs-mdx": "^15.2.0", "mermaid": "^11.15.0", "next": "16.2.9", "posthog-js": "^1.372.1", @@ -18,6 +22,7 @@ "@biomejs/biome": "2.4.13", "@tailwindcss/postcss": "^4", "@types/bun": "^1.3.13", + "@types/mdx": "^2.0.13", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -35,6 +40,8 @@ "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + "@astryxdesign/core": ["@astryxdesign/core@0.1.6", "", { "peerDependencies": { "@stylexjs/stylex": "^0.19.0", "react": ">=19.0.0", "react-dom": ">=19.0.0" } }, "sha512-htHYq+tlhIoM+HA9bGR1IPMswX/UZCWTduH2V30XB21hA2nf32zufI+v9aPpWhgPdjccIY11sFI8m2NPM8a4JA=="], + "@biomejs/biome": ["@biomejs/biome@2.4.13", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.13", "@biomejs/cli-darwin-x64": "2.4.13", "@biomejs/cli-linux-arm64": "2.4.13", "@biomejs/cli-linux-arm64-musl": "2.4.13", "@biomejs/cli-linux-x64": "2.4.13", "@biomejs/cli-linux-x64-musl": "2.4.13", "@biomejs/cli-win32-arm64": "2.4.13", "@biomejs/cli-win32-x64": "2.4.13" }, "bin": { "biome": "bin/biome" } }, "sha512-gLXOwkOBBg0tr7bDsqlkIh4uFeKuMjxvqsrb1Tukww1iDmHcfr4Uu8MoQxp0Rcte+69+osRNWXwHsu/zxT6XqA=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2KImO1jhNFBa2oWConyr0x6flxbQpGKv6902uGXpYM62Xyem8U80j441SyUJ8KyngsmKbQjeIv1q2CQfDkNnYg=="], @@ -59,6 +66,58 @@ "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], @@ -123,6 +182,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], + "@mermaid-js/parser": ["@mermaid-js/parser@1.1.1", "", { "dependencies": { "@chevrotain/types": "~11.1.1" } }, "sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw=="], "@next/env": ["@next/env@16.2.9", "", {}, "sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg=="], @@ -165,6 +226,8 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + "@posthog/core": ["@posthog/core@1.27.5", "", { "dependencies": { "@posthog/types": "1.372.1" } }, "sha512-sYCcUDuYKumYTjwGqGCPT8aUy086v9PKw5wD+UXCRSfCsxWy5R/ic6W13kGTn4O5B2cD1V19wJv19oIH5kHUiQ=="], "@posthog/types": ["@posthog/types@1.372.1", "", {}, "sha512-yl2x2HgtdhFk8bvf6HuRSDzXnKmKGrzNxUahKvA/0mcwheweINvmWy5MsN55NevrcCrNXA6m8GPHS9o/y1mn4A=="], @@ -189,6 +252,26 @@ "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + "@shikijs/core": ["@shikijs/core@4.3.1", "", { "dependencies": { "@shikijs/primitive": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg=="], + + "@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="], + + "@shikijs/primitive": ["@shikijs/primitive@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A=="], + + "@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="], + + "@shikijs/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@stylexjs/stylex": ["@stylexjs/stylex@0.19.0", "", { "dependencies": { "css-mediaquery": "^0.1.2", "invariant": "^2.2.4", "styleq": "0.2.1" } }, "sha512-CnUFp7YMaDLDeemsWOfJgoC/gKM5P/yBNMcpJaE6ChJmXr7s0DJwSeGTTlHJcqqwN9OW1qGtmARWLFhGZN1pTA=="], + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@tailwindcss/node": ["@tailwindcss/node@4.2.4", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.4" } }, "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA=="], @@ -285,8 +368,22 @@ "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + "@types/hast": ["@types/hast@3.0.5", "", { "dependencies": { "@types/unist": "*" } }, "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdx": ["@types/mdx@2.0.14", "", {}, "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg=="], + + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], + "@types/node": ["@types/node@20.19.39", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw=="], "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], @@ -295,30 +392,84 @@ "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.3", "", {}, "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg=="], + "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="], "@vercel/blob": ["@vercel/blob@2.4.0", "", { "dependencies": { "async-retry": "^1.3.3", "is-buffer": "^2.0.5", "is-node-process": "^1.2.0", "throttleit": "^2.1.0", "undici": "^6.23.0" } }, "sha512-ncQ8CRb6XoEAYJwjOTRGpACRT6h/AeY+/33gLyeVxG5BIes27OPm1jmqreF+JHjcTmGhClTP+kBpmyLfbV0xew=="], + "@yuku-analyzer/binding-darwin-arm64": ["@yuku-analyzer/binding-darwin-arm64@0.6.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9rpIP7IeybjyvWUf6WnU24h1qo+JdxIHr1o3yb06HoE8tM3S/Jh5RrUw9aw5P9BKSIvSPbLyVlItX7PcD3o5bQ=="], + + "@yuku-analyzer/binding-darwin-x64": ["@yuku-analyzer/binding-darwin-x64@0.6.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-ELLhNT4FGnqY8yh0W3cSs9rGMSeUyhib1aYD84RupjlfsrDTrQRoDhWu01Dv6xCfYgASYaj1Abntk91A7njNag=="], + + "@yuku-analyzer/binding-freebsd-x64": ["@yuku-analyzer/binding-freebsd-x64@0.6.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-s76XocUMlK9liTyipALFb2K64ku35u/wg238A0NW8U5CUDsuIe/8tu5TzdLjJAGxnd0IV+gBneDt9cJJzLeFRQ=="], + + "@yuku-analyzer/binding-linux-arm-gnu": ["@yuku-analyzer/binding-linux-arm-gnu@0.6.12", "", { "os": "linux", "cpu": "arm" }, "sha512-hm8Tq0umop3RGu6dOMF61q69tYn1bDp1CeYD5ZjuGFQJclp0moVtjzY4z0bzusicKeZ9+k5LRroR0p5HWC2hDw=="], + + "@yuku-analyzer/binding-linux-arm-musl": ["@yuku-analyzer/binding-linux-arm-musl@0.6.12", "", { "os": "linux", "cpu": "arm" }, "sha512-CxtPKLddogHAB3ZHVWaUl+U8jx0pdriTSbQ1K/orlDqU0GDhg8LuIRyUscP7r2/62fGGMzkc119fE71I4Nl1Fg=="], + + "@yuku-analyzer/binding-linux-arm64-gnu": ["@yuku-analyzer/binding-linux-arm64-gnu@0.6.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-EOyLcpAmF5qAVDKmKvV7xt8oBGeWQ92CqFI4s7h7TRlrF6TfGRrh8PwawGn92gFploNLAYj/1Z9Q1gVvwGgG9g=="], + + "@yuku-analyzer/binding-linux-arm64-musl": ["@yuku-analyzer/binding-linux-arm64-musl@0.6.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-T3eCYy6bMnVRMQEYAbDcpj08/XM93dBTtnn/DDocJN21RARe+KCzWKeL26J3yd3bOW3WVjVLq09BfdpAGB0buQ=="], + + "@yuku-analyzer/binding-linux-x64-gnu": ["@yuku-analyzer/binding-linux-x64-gnu@0.6.12", "", { "os": "linux", "cpu": "x64" }, "sha512-1Y+noIuvnDugIVsoIr5NduZqX7KuFTzICSkvG8RW3OKK9URVeTOicKK217i44ABZSSZJ7A0E7vzifapx0c9VDw=="], + + "@yuku-analyzer/binding-linux-x64-musl": ["@yuku-analyzer/binding-linux-x64-musl@0.6.12", "", { "os": "linux", "cpu": "x64" }, "sha512-woN/GuG95Fd6bp+ZQfmiFrZnoA2hdu3vfVSc89A8LElnYpzFaJM81sOZp8f3tVOVUJxbt7KAUiCLwSy34MJKqA=="], + + "@yuku-analyzer/binding-win32-arm64": ["@yuku-analyzer/binding-win32-arm64@0.6.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-8OVFnKbK+lgsL6MqILPLpzlsa00K4KiKsdbHH94hpGcrqaz1jv+k0Y7ujSaoYTWw5Bb7Lr9GJ3L1n1hT2sXoYA=="], + + "@yuku-analyzer/binding-win32-x64": ["@yuku-analyzer/binding-win32-x64@0.6.12", "", { "os": "win32", "cpu": "x64" }, "sha512-3w8w1Xc5njwgbGTcn3JfDxWuQnFvtSll1D8gBlk4U8CI5v7ibKOMIdABucCXH8WtsRREG0ME5Vn0i422eX3zLQ=="], + + "@yuku-toolchain/types": ["@yuku-toolchain/types@0.6.11", "", {}, "sha512-i1JYFNJaKNCgyJ/nVoR8GK7wvlXF+ShYzFHBauWcvg8IoiXInK7pVziHcgNz/MWLPNr/Mb/CtmXccrJMkKqSHQ=="], + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], + "async-retry": ["async-retry@1.3.3", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="], + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA=="], "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], "caniuse-lite": ["caniuse-lite@1.0.30001790", "", {}, "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw=="], + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], + + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], "core-js": ["core-js@3.49.0", "", {}, "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg=="], "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], + "css-mediaquery": ["css-mediaquery@0.1.2", "", {}, "sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q=="], + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], "cytoscape": ["cytoscape@3.33.2", "", {}, "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw=="], @@ -395,34 +546,102 @@ "dayjs": ["dayjs@1.11.20", "", {}, "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + "delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + "dompurify": ["dompurify@3.4.1", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw=="], "enhanced-resolve": ["enhanced-resolve@5.21.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA=="], "es-toolkit": ["es-toolkit@1.48.1", "", {}, "sha512-wfnXlwd5I75eXRtdD2vuEs50xHHESECDsGD7yiQnfFVNoa5522NwXEbmgo98LfiukSQHs+mBM7/YG3qKJB9/mQ=="], + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], + + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], + + "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], + + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], + + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], + + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + "fflate": ["fflate@0.4.8", "", {}, "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA=="], + "fumadocs-core": ["fumadocs-core@16.11.5", "", { "dependencies": { "@orama/orama": "^3.1.18", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.3.1", "tinyglobby": "^0.2.17", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "yaml": "^2.9.0" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "0.x.x", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x || 8.x.x", "waku": "*", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-YrHjS09+QYYKOSTGyiZbxF/VDs7ciMcjurYBGfmYqtzdj14k7Ho0HX9c6VuvG54YsYHQs5mGWemT1TXm7vDBaA=="], + + "fumadocs-mdx": ["fumadocs-mdx@15.2.0", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.28.1", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "magic-string": "^0.30.21", "mdast-util-mdx": "^3.0.0", "picocolors": "^1.1.1", "picomatch": "^4.0.5", "tinyexec": "^1.2.4", "tinyglobby": "^0.2.17", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "yaml": "^2.9.0", "yuku-analyzer": "^0.6.3", "zod": "^4.4.3" }, "peerDependencies": { "@fumadocs/satteri": "0.x.x", "@types/mdast": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "mdast-util-directive": "*", "next": "^15.3.0 || ^16.0.0", "react": "^19.2.0", "rolldown": "*", "satteri": "^0.9.4", "vite": "7.x.x || 8.x.x" }, "optionalPeers": ["@fumadocs/satteri", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "rolldown", "satteri", "vite"], "bin": { "fumadocs-mdx": "./bin.js" } }, "sha512-+yBP8QYw5wA9LF5eVdMhwbP7KT1OF4B/YfC6PZoD2jz0amZi1B+6QHTI6XoRRSTmhWrI4cL5LU1DspW0itk+NA=="], + + "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], + "gpt-tokenizer": ["gpt-tokenizer@3.4.0", "", {}, "sha512-wxFLnhIXTDjYebd9A9pGl3e31ZpSypbpIJSOswbgop5jLte/AsZVDvjlbEuVFlsqZixVKqbcoNmRlFDf6pz/UQ=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], + + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + "is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="], + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + "is-node-process": ["is-node-process@1.2.0", "", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="], + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "katex": ["katex@0.16.45", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA=="], "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], @@ -457,26 +676,146 @@ "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], + + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], + + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], + + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], + + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], + + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], + + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], + + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], + + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], + + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], + "mermaid": ["mermaid@11.15.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.1.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "cytoscape-cose-bilkent": "^4.1.0", "cytoscape-fcose": "^2.2.0", "d3": "^7.9.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "es-toolkit": "^1.45.1", "katex": "^0.16.25", "khroma": "^2.1.0", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0 || ^12 || ^13 || ^14.0.0" } }, "sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw=="], + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], + + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], + + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], + + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], + + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], + + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], + + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], + + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], + + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], + + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], + + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], + + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], + + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], + + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], + + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], + + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], + + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], + + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], + + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], + + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], + + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], + + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], + + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], + + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], + + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + "mlly": ["mlly@1.8.2", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "next": ["next@16.2.9", "", { "dependencies": { "@next/env": "16.2.9", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.9", "@next/swc-darwin-x64": "16.2.9", "@next/swc-linux-arm64-gnu": "16.2.9", "@next/swc-linux-arm64-musl": "16.2.9", "@next/swc-linux-x64-gnu": "16.2.9", "@next/swc-linux-x64-musl": "16.2.9", "@next/swc-win32-arm64-msvc": "16.2.9", "@next/swc-win32-x64-msvc": "16.2.9", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww=="], + "oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="], + + "oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="], + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], @@ -491,6 +830,8 @@ "preact": ["preact@10.29.1", "", {}, "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg=="], + "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + "protobufjs": ["protobufjs@7.5.5", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg=="], "query-selector-shadow-dom": ["query-selector-shadow-dom@1.0.1", "", {}, "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw=="], @@ -499,6 +840,36 @@ "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], + + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], + + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], + + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + + "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], + + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], + + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="], @@ -511,14 +882,30 @@ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + "shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + "styleq": ["styleq@0.2.1", "", {}, "sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw=="], + "stylis": ["stylis@4.4.0", "", {}, "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA=="], "tailwindcss": ["tailwindcss@4.2.4", "", {}, "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA=="], @@ -527,7 +914,13 @@ "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], - "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], + "tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], @@ -541,12 +934,42 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], + + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + "web-vitals": ["web-vitals@5.2.0", "", {}, "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "yuku-analyzer": ["yuku-analyzer@0.6.12", "", { "dependencies": { "@yuku-toolchain/types": "0.6.11", "yuku-ast": "0.6.11" }, "optionalDependencies": { "@yuku-analyzer/binding-darwin-arm64": "0.6.12", "@yuku-analyzer/binding-darwin-x64": "0.6.12", "@yuku-analyzer/binding-freebsd-x64": "0.6.12", "@yuku-analyzer/binding-linux-arm-gnu": "0.6.12", "@yuku-analyzer/binding-linux-arm-musl": "0.6.12", "@yuku-analyzer/binding-linux-arm64-gnu": "0.6.12", "@yuku-analyzer/binding-linux-arm64-musl": "0.6.12", "@yuku-analyzer/binding-linux-x64-gnu": "0.6.12", "@yuku-analyzer/binding-linux-x64-musl": "0.6.12", "@yuku-analyzer/binding-win32-arm64": "0.6.12", "@yuku-analyzer/binding-win32-x64": "0.6.12" } }, "sha512-0zu/gwv6nKA3wm2GMjM1iczw9rbt77ijEyR5tXpPQ8AZcXIpXlll66BXOtMHgYudLn91bJx0ybhpARoJWm5/dw=="], + + "yuku-ast": ["yuku-ast@0.6.11", "", { "dependencies": { "@yuku-toolchain/types": "0.6.8" } }, "sha512-ZfXkFYVsDewS45+kv3WiA/qNB73CRfxFDEQwfnRMUAR4AD5zRI7PRqxmI2U3Jz/oG41GneTVW6mxDOQal0lgeA=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + + "@antfu/install-pkg/tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], + "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], "@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.7.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ=="], @@ -577,8 +1000,14 @@ "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "fumadocs-mdx/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + + "yuku-ast/@yuku-toolchain/types": ["@yuku-toolchain/types@0.6.8", "", {}, "sha512-AbUd1775RVkOxJkh8hkldIWoU6kRMTCsZFSZq8Ny53q7GkbaVe5UCfleNZ3RWCoz/ZKE8qwfeB7Cj0xqhLWsKA=="], + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], diff --git a/website/next.config.d.mts b/website/next.config.d.mts new file mode 100644 index 000000000..7ebed20bd --- /dev/null +++ b/website/next.config.d.mts @@ -0,0 +1,4 @@ +import type { NextConfig } from "next"; + +declare const nextConfig: NextConfig; +export default nextConfig; diff --git a/website/next.config.ts b/website/next.config.mjs similarity index 58% rename from website/next.config.ts rename to website/next.config.mjs index 1775e1e0d..58b47daff 100644 --- a/website/next.config.ts +++ b/website/next.config.mjs @@ -1,7 +1,10 @@ -import type { NextConfig } from "next"; -import { AGENT_DISCOVERY_LINK_HEADER } from "./src/lib/agent-discovery"; +import { fileURLToPath } from "node:url"; +import { createMDX } from "fumadocs-mdx/next"; +import { AGENT_DISCOVERY_LINK_HEADER } from "./src/lib/agent-discovery-header.mjs"; -const nextConfig: NextConfig = { +const withMDX = createMDX(); + +const nextConfig = { async headers() { return [ { @@ -28,6 +31,11 @@ const nextConfig: NextConfig = { "/api/execute": ["./vendor/**"], "/api/test": ["./vendor/**"], }, + // The documentation collection deliberately lives one level above the + // Next.js package. Turbopack otherwise refuses to resolve those files. + turbopack: { + root: fileURLToPath(new URL("..", import.meta.url)), + }, }; -export default nextConfig; +export default withMDX(nextConfig); diff --git a/website/package.json b/website/package.json index cda966af9..bbf8607d6 100644 --- a/website/package.json +++ b/website/package.json @@ -3,17 +3,15 @@ "version": "0.1.0", "private": true, "scripts": { - "sync-docs": "node scripts/sync-docs.mjs", "publish-test262": "bun scripts/publish-test262-results.ts", "publish-awfy": "bun scripts/publish-awfy-report.ts", "publish-jetstream": "bun scripts/publish-jetstream-report.ts", "publish-web-tooling": "bun scripts/publish-web-tooling-report.ts", "backfill-test262": "bun scripts/backfill-test262-dashboard.ts", - "predev": "node scripts/sync-docs.mjs", - "prebuild": "node scripts/sync-docs.mjs && bun scripts/fetch-binaries.ts", - "dev": "next dev", - "build": "next build", - "start": "next start", + "prebuild": "bun scripts/fetch-binaries.ts", + "dev": "bun --bun next dev", + "build": "bun --bun next build", + "start": "bun --bun next start", "lint": "biome check", "format": "biome format --write", "test": "bun test" @@ -34,7 +32,11 @@ ] }, "dependencies": { + "@astryxdesign/core": "^0.1.6", + "@stylexjs/stylex": "^0.19.0", "@vercel/blob": "^2.4.0", + "fumadocs-core": "^16.11.5", + "fumadocs-mdx": "^15.2.0", "mermaid": "^11.15.0", "next": "16.2.9", "posthog-js": "^1.372.1", @@ -47,6 +49,7 @@ "@biomejs/biome": "2.4.13", "@tailwindcss/postcss": "^4", "@types/bun": "^1.3.13", + "@types/mdx": "^2.0.13", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/website/scripts/sync-docs.mjs b/website/scripts/sync-docs.mjs deleted file mode 100644 index 47e912ed3..000000000 --- a/website/scripts/sync-docs.mjs +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env node -// Mirror ../docs/**/*.md and ../README.md into website/content/docs/. -// Single source of truth lives in the repo root; this directory is gitignored -// and rebuilt from scratch on every dev/build. - -import fs from "node:fs/promises"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const here = path.dirname(fileURLToPath(import.meta.url)); -const websiteRoot = path.resolve(here, ".."); -const repoRoot = path.resolve(websiteRoot, ".."); -const docsSrc = path.join(repoRoot, "docs"); -const readmeSrc = path.join(repoRoot, "README.md"); -const docsDest = path.join(websiteRoot, "content", "docs"); - -async function exists(p) { - try { - await fs.access(p); - return true; - } catch (err) { - // Only "missing" should map to false. Permission / IO failures must - // propagate so the prebuild aborts loudly instead of silently skipping - // copies. - if (err && err.code === "ENOENT") return false; - throw err; - } -} - -async function copyMarkdownTree(src, dest) { - if (!(await exists(src))) return 0; - const entries = await fs.readdir(src, { withFileTypes: true }); - let count = 0; - for (const entry of entries) { - const srcPath = path.join(src, entry.name); - const destPath = path.join(dest, entry.name); - if (entry.isDirectory()) { - await fs.mkdir(destPath, { recursive: true }); - count += await copyMarkdownTree(srcPath, destPath); - } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) { - await fs.copyFile(srcPath, destPath); - count++; - } - } - return count; -} - -async function main() { - await fs.rm(docsDest, { recursive: true, force: true }); - await fs.mkdir(docsDest, { recursive: true }); - - const docCount = await copyMarkdownTree(docsSrc, docsDest); - - const readmeCopied = await exists(readmeSrc); - if (readmeCopied) { - await fs.copyFile(readmeSrc, path.join(docsDest, "readme.md")); - } - - const suffix = readmeCopied ? " + README" : ""; - console.log( - `[sync-docs] copied ${docCount} doc files${suffix} from ${path.relative(websiteRoot, repoRoot)}/docs → content/docs`, - ); -} - -main().catch((err) => { - console.error("[sync-docs] failed:", err); - process.exit(1); -}); diff --git a/website/source.config.ts b/website/source.config.ts new file mode 100644 index 000000000..1c2652374 --- /dev/null +++ b/website/source.config.ts @@ -0,0 +1,53 @@ +import { defineConfig, defineDocs } from "fumadocs-mdx/config"; +import { z } from "zod"; + +function titleFromMarkdown(source: string, path: string): string { + const heading = source.match(/^#\s+(.+)$/m)?.[1]?.trim(); + if (heading) { + return heading + .replace(/\[([^\]]+)\]\([^)]*\)/g, "$1") + .replace(/[*_`]/g, ""); + } + return ( + path + .replace(/(?:^|\/)README\.md$/i, "") + .replace(/\.mdx?$/i, "") + .split("/") + .at(-1) + ?.replace(/[-_]+/g, " ") ?? "GocciaScript" + ); +} + +export const docs = defineDocs({ + // The repository Markdown is the source of truth. Fumadocs compiles it in + // place, so the website no longer needs a copied content tree. + dir: "..", + docs: { + files: ["README.md", "docs/**/*.md"], + // Existing docs use their first H1 as the title. Adapt that convention at + // the collection boundary rather than adding website-only frontmatter to + // every repository document. + schema: ({ source, path }) => + z + .object({ + title: z.string().optional(), + description: z.string().optional(), + }) + .transform((frontmatter) => ({ + ...frontmatter, + title: frontmatter.title ?? titleFromMarkdown(source, path), + })), + lastModified: true, + postprocess: { + includeProcessedMarkdown: true, + }, + }, + // There are no Fumadocs ordering files in the repository. Narrowing the + // meta glob prevents the repo-root collection from treating unrelated JSON + // files (including dependencies) as documentation metadata. + meta: { + files: ["docs/**/meta.json"], + }, +}); + +export default defineConfig(); diff --git a/website/src/__tests__/agent-discovery.test.ts b/website/src/__tests__/agent-discovery.test.ts index 0fe50a03b..515765e23 100644 --- a/website/src/__tests__/agent-discovery.test.ts +++ b/website/src/__tests__/agent-discovery.test.ts @@ -26,7 +26,7 @@ import { sha256Digest, versionFromReleaseTag, } from "@/lib/agent-discovery"; -import nextConfig from "../../next.config"; +import nextConfig from "../../next.config.mjs"; describe("agent discovery", () => { test("homepage advertises agent-useful Link relations", async () => { diff --git a/website/src/__tests__/markdown.test.ts b/website/src/__tests__/markdown.test.ts deleted file mode 100644 index 142e3cd50..000000000 --- a/website/src/__tests__/markdown.test.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { parseMarkdown, safeHref } from "@/components/markdown"; -import { DOC_HREF_MAP } from "@/lib/docs-data"; - -describe("safeHref — link scheme allowlist", () => { - test("allows http(s) URLs", () => { - expect(safeHref("https://example.com/x")).toBe("https://example.com/x"); - expect(safeHref("http://example.com/x")).toBe("http://example.com/x"); - }); - - test("allows mailto and tel", () => { - expect(safeHref("mailto:hi@example.com")).toBe("mailto:hi@example.com"); - expect(safeHref("tel:+15551234567")).toBe("tel:+15551234567"); - }); - - test("allows in-page anchors and relative paths through unchanged", () => { - expect(safeHref("#section")).toBe("#section"); - expect(safeHref("/docs/architecture")).toBe("/docs/architecture"); - expect(safeHref("../sibling.md")).toBe("../sibling.md"); - expect(safeHref("page.md")).toBe("page.md"); - }); - - test("rejects javascript: scheme regardless of casing", () => { - expect(safeHref("javascript:alert(1)")).toBeNull(); - expect(safeHref("JavaScript:alert(1)")).toBeNull(); - expect(safeHref("JAVASCRIPT:alert(1)")).toBeNull(); - }); - - test("rejects other dangerous schemes", () => { - expect(safeHref("data:text/html,")).toBeNull(); - expect(safeHref("vbscript:msgbox")).toBeNull(); - expect(safeHref("file:///etc/passwd")).toBeNull(); - }); - - test("rejects empty string", () => { - expect(safeHref("")).toBeNull(); - }); - - test("rejects whitespace-prefixed dangerous schemes", () => { - // Browsers strip leading whitespace before navigating, so a naïve - // scheme check that doesn't trim would let `" javascript:..."` slip - // through and fire the dangerous scheme. - expect(safeHref(" javascript:alert(1)")).toBeNull(); - expect(safeHref("\tjavascript:alert(1)")).toBeNull(); - expect(safeHref("\njavascript:alert(1)")).toBeNull(); - }); - - test("rejects schemes with embedded control characters", () => { - // Browsers also strip TAB / CR / LF *inside* URLs before resolving, - // so `j\tavascript:` would resolve to `javascript:` at navigation - // time. Reject anything containing C0 controls or DEL outright. - expect(safeHref("j\tavascript:alert(1)")).toBeNull(); - expect(safeHref("java\nscript:alert(1)")).toBeNull(); - expect(safeHref("javascript:alert(1)")).toBeNull(); - }); - - test("trims whitespace around safe schemes", () => { - expect(safeHref(" https://example.com/x ")).toBe("https://example.com/x"); - }); - - test("rejects whitespace-only input", () => { - expect(safeHref(" ")).toBeNull(); - expect(safeHref("\t\n")).toBeNull(); - }); -}); - -describe("DOC_HREF_MAP — README links", () => { - test("keeps nested README docs from shadowing the root overview", () => { - expect(DOC_HREF_MAP.README).toBe("readme"); - expect(DOC_HREF_MAP.readme).toBe("readme"); - expect(DOC_HREF_MAP["adr/README"]).toBe("adr"); - }); -}); - -describe("parseMarkdown — block segmentation", () => { - test("breaks paragraphs at horizontal rules instead of swallowing them", () => { - const blocks = parseMarkdown("Some paragraph text.\n---\nNext paragraph."); - const kinds = blocks.map((b) => b.kind); - expect(kinds).toEqual(["p", "hr", "p"]); - }); - - test("breaks paragraphs at table starts", () => { - const src = [ - "Intro paragraph that introduces a table.", - "| col1 | col2 |", - "| ---- | ---- |", - "| a | b |", - ].join("\n"); - const blocks = parseMarkdown(src); - expect(blocks.map((b) => b.kind)).toEqual(["p", "table"]); - const tableBlock = blocks.find((b) => b.kind === "table"); - if (tableBlock?.kind !== "table") throw new Error("expected table block"); - expect(tableBlock.hdr).toEqual(["col1", "col2"]); - expect(tableBlock.rows).toEqual([["a", "b"]]); - }); - - test("breaks paragraphs at headings, fences, blockquotes, lists (existing rule)", () => { - const src = [ - "Paragraph before.", - "## Heading", - "Paragraph after heading.", - "```js", - "x = 1;", - "```", - "Paragraph after fence.", - "> blockquote", - "Paragraph after quote.", - "- list item", - ].join("\n"); - const blocks = parseMarkdown(src); - expect(blocks.map((b) => b.kind)).toEqual([ - "p", - "heading", - "p", - "code", - "p", - "quote", - "p", - "list", - ]); - }); - - test("preserves a paragraph that legitimately contains a `-` (just not at start)", () => { - const src = "A paragraph with a hyphen-word inside."; - const blocks = parseMarkdown(src); - expect(blocks).toEqual([{ kind: "p", text: src }]); - }); - - test("recognizes mixed-case fences as their canonical kind (sanity)", () => { - const blocks = parseMarkdown("```js\nfoo\n```"); - expect(blocks).toEqual([{ kind: "code", lang: "js", code: "foo" }]); - }); - - test("table cells preserve escaped pipes (`\\|`)", () => { - const src = [ - "| left | right |", - "| ---- | ----- |", - "| a \\| b | c |", - ].join("\n"); - const blocks = parseMarkdown(src); - if (blocks[0]?.kind !== "table") throw new Error("expected table"); - expect(blocks[0].rows[0]).toEqual(["a | b", "c"]); - }); - - test("table header preserves intentional empty cells (column-count parity)", () => { - // `filter(Boolean)` on the header would drop the empty middle cell - // and desynchronize the header (2 cols) from the rows (3 cols). The - // outer-only filter keeps both at 3 columns. - const src = ["| a | | b |", "| --- | --- | --- |", "| 1 | 2 | 3 |"].join( - "\n", - ); - const blocks = parseMarkdown(src); - if (blocks[0]?.kind !== "table") throw new Error("expected table"); - expect(blocks[0].hdr).toEqual(["a", "", "b"]); - expect(blocks[0].rows[0]).toEqual(["1", "2", "3"]); - expect(blocks[0].hdr.length).toBe(blocks[0].rows[0].length); - }); -}); diff --git a/website/src/__tests__/sitemap.test.ts b/website/src/__tests__/sitemap.test.ts index 8c61cbc40..78e7d73cb 100644 --- a/website/src/__tests__/sitemap.test.ts +++ b/website/src/__tests__/sitemap.test.ts @@ -1,27 +1,36 @@ import { describe, expect, test } from "bun:test"; -import fs from "node:fs"; -import { fileURLToPath } from "node:url"; import { buildSitemap, getDocLastModified } from "@/app/sitemap"; -import { DOC_PAGES } from "@/lib/docs-data"; +import { docsSource } from "@/lib/docs-source"; + +describe("Fumadocs repository source", () => { + test("derives the established routes from repository Markdown paths", () => { + expect(docsSource.getPage(undefined)?.path).toBe("README.md"); + expect(docsSource.getPage(["language"])?.path).toBe("docs/language.md"); + expect(docsSource.getPage(["contributing-workflow"])?.path).toBe( + "docs/contributing/workflow.md", + ); + expect(docsSource.getPage(["adr"])?.path).toBe("docs/adr/README.md"); + }); +}); describe("sitemap", () => { - test("uses a build date for app pages and file mtimes for doc pages", () => { + test("publishes each app and repository-backed docs route once", () => { const appLastModified = new Date("2026-06-17T00:00:00.000Z"); const entries = buildSitemap("https://example.test///", appLastModified); + const urls = entries.map((entry) => entry.url); const home = entries.find((entry) => entry.url === "https://example.test/"); - const languagePage = DOC_PAGES.find((page) => page.id === "language"); + const docs = entries.find( + (entry) => entry.url === "https://example.test/docs", + ); + const languagePage = docsSource.getPage(["language"]); if (!languagePage) throw new Error("missing language doc page"); - - const languageEntry = entries.find( + const language = entries.find( (entry) => entry.url === "https://example.test/docs/language", ); - const languageSource = fileURLToPath( - new URL(`../../../docs/${languagePage.file}`, import.meta.url), - ); - const languageMtime = fs.statSync(languageSource).mtime; + expect(new Set(urls).size).toBe(urls.length); expect(home?.lastModified).toEqual(appLastModified); - expect(languageEntry?.lastModified).toEqual(languageMtime); - expect(getDocLastModified(languagePage)).toEqual(languageMtime); + expect(docs?.priority).toBe(0.8); + expect(language?.lastModified).toEqual(getDocLastModified(languagePage)); }); }); diff --git a/website/src/app/docs/[[...slug]]/page.tsx b/website/src/app/docs/[[...slug]]/page.tsx new file mode 100644 index 000000000..f48a52fea --- /dev/null +++ b/website/src/app/docs/[[...slug]]/page.tsx @@ -0,0 +1,81 @@ +import { Outline } from "@astryxdesign/core/Outline"; +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { docsMdxComponents } from "@/components/docs-mdx"; +import { DocsNav } from "@/components/docs-nav"; +import { + docsPageDescription, + docsPageTitle, + docsSource, +} from "@/lib/docs-source"; + +type Props = { params: Promise<{ slug?: string[] }> }; + +export function generateStaticParams() { + return docsSource.generateParams(); +} + +export async function generateMetadata({ params }: Props): Promise { + const { slug } = await params; + const page = docsSource.getPage(slug); + if (!page) return { title: "Docs" }; + + const title = docsPageTitle(page); + const description = docsPageDescription(page); + return { + title: `${title} · Docs`, + description, + alternates: { canonical: page.url }, + openGraph: { + title: `${title} · Docs · GocciaScript`, + description, + url: page.url, + }, + twitter: { title: `${title} · Docs · GocciaScript`, description }, + }; +} + +export default async function DocsPage({ params }: Props) { + const { slug } = await params; + const page = docsSource.getPage(slug); + if (!page) notFound(); + + const Content = page.data.body; + const pages = docsSource + .getPages() + .map((item) => ({ + href: item.url, + path: item.path, + title: docsPageTitle(item), + })) + .sort((a, b) => a.title.localeCompare(b.title)); + const outline = page.data.toc + .filter((item) => typeof item.title === "string") + .map((item) => ({ + id: item.url.replace(/^#/, ""), + label: item.title as string, + level: item.depth, + })); + + return ( +
+
+ Browse documentation + +
+
+ +
+ +
+ {outline.length > 0 && ( + + )} +
+
+ ); +} diff --git a/website/src/app/docs/[id]/page.tsx b/website/src/app/docs/[id]/page.tsx deleted file mode 100644 index d610decf6..000000000 --- a/website/src/app/docs/[id]/page.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import type { Metadata } from "next"; -import { notFound } from "next/navigation"; -import { Docs } from "@/components/docs"; -import { readDocSource } from "@/lib/doc-source"; -import { DOC_PAGES } from "@/lib/docs-data"; - -export function generateStaticParams() { - return DOC_PAGES.filter((p) => p.id !== "readme").map((p) => ({ id: p.id })); -} - -export async function generateMetadata({ - params, -}: { - params: Promise<{ id: string }>; -}): Promise { - const { id } = await params; - const page = DOC_PAGES.find((p) => p.id === id); - if (!page) return { title: "Docs" }; - const title = `${page.title} · Docs`; - const description = - page.desc ?? - "Reference documentation for GocciaScript — language, built-ins, architecture."; - const url = `/docs/${id}`; - return { - title, - description, - alternates: { canonical: url }, - openGraph: { - title: `${title} · GocciaScript`, - description, - url, - }, - twitter: { - title: `${title} · GocciaScript`, - description, - }, - }; -} - -export default async function DocsIdPage({ - params, -}: { - params: Promise<{ id: string }>; -}) { - const { id } = await params; - if (!DOC_PAGES.some((p) => p.id === id)) notFound(); - const source = await readDocSource(id); - return ; -} diff --git a/website/src/app/docs/page.tsx b/website/src/app/docs/page.tsx deleted file mode 100644 index 00f677ab2..000000000 --- a/website/src/app/docs/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import type { Metadata } from "next"; -import { Docs } from "@/components/docs"; -import { readDocSource } from "@/lib/doc-source"; - -export const metadata: Metadata = { - title: "Docs", - description: - "Reference documentation for GocciaScript — language, built-ins, architecture, bytecode VM, embedding.", - alternates: { canonical: "/docs" }, - openGraph: { - title: "Docs · GocciaScript", - description: - "Reference documentation for GocciaScript — language, built-ins, architecture, bytecode VM, embedding.", - url: "/docs", - }, - twitter: { - title: "Docs · GocciaScript", - description: - "Reference documentation for GocciaScript — language, built-ins, architecture, bytecode VM, embedding.", - }, -}; - -export default async function DocsPage() { - const source = await readDocSource("readme"); - return ; -} diff --git a/website/src/app/globals.css b/website/src/app/globals.css index 99e1fbda8..aba147383 100644 --- a/website/src/app/globals.css +++ b/website/src/app/globals.css @@ -2,7 +2,10 @@ GocciaScript — warm paper aesthetic ============================================================ */ +@layer theme, base, astryx-base, astryx-theme, components, utilities; @import "tailwindcss"; +@import "@astryxdesign/core/reset.css"; +@import "@astryxdesign/core/astryx.css"; @custom-variant dark (&:where( [data-theme="espresso"], @@ -75,6 +78,33 @@ 0 2px 0 rgba(42, 36, 25, 0.05), 0 6px 20px -6px rgba(42, 36, 25, 0.18); --shadow-lg: 0 4px 0 rgba(42, 36, 25, 0.04), 0 24px 48px -20px rgba(42, 36, 25, 0.3); + + /* Astryx semantic tokens mapped onto the GocciaScript identity. */ + --color-accent: var(--accent); + --color-accent-muted: color-mix(in oklab, var(--accent) 16%, transparent); + --color-on-accent: var(--accent-ink); + --color-background-body: var(--paper); + --color-background-surface: var(--paper); + --color-background-card: var(--paper-2); + --color-background-popover: var(--paper); + --color-background-muted: var(--paper-2); + --color-overlay-hover: color-mix(in oklab, var(--ink) 7%, transparent); + --color-overlay-pressed: color-mix(in oklab, var(--ink) 12%, transparent); + --color-text-primary: var(--ink); + --color-text-secondary: var(--ink-2); + --color-text-disabled: var(--ink-3); + --color-text-accent: var(--accent); + --color-icon-primary: var(--ink); + --color-icon-secondary: var(--ink-2); + --color-icon-accent: var(--accent); + --color-border: var(--rule-soft); + --color-border-emphasized: var(--rule); + --color-shadow: color-mix(in oklab, var(--ink) 18%, transparent); + --font-family-body: var(--body); + --font-family-heading: var(--display); + --font-family-code: var(--mono); + --radius-element: 6px; + --radius-container: 12px; } /* Dark theme — "late night espresso" */ @@ -296,233 +326,71 @@ p { flex-direction: column; } -.container { - width: 100%; - max-width: 1280px; - margin: 0 auto; - padding: 0 clamp(1.25rem, 3vw, 2.5rem); -} - -/* ---------- navbar ---------- */ -.nav { +/* Astryx owns the generic shell behavior; these selectors only carry the + project-specific wordmark and paper treatment. */ +.site-top-nav { position: sticky; top: 0; z-index: 50; - backdrop-filter: blur(8px) saturate(1.2); - background: color-mix(in oklab, var(--paper) 78%, transparent); - border-bottom: 1px solid var(--rule-soft); -} -.nav-inner { - display: flex; - align-items: center; - gap: 2rem; - padding: 0.9rem 0; + width: 100%; + padding-inline: clamp(1.25rem, 3vw, 2.5rem); + background: color-mix(in oklab, var(--paper) 90%, transparent); + backdrop-filter: blur(10px) saturate(1.15); } -.brand { - display: flex; +.astryx-brand { + display: inline-flex; align-items: center; gap: 0.65rem; - font-family: var(--display); - font-size: 1.55rem; color: var(--ink); - text-decoration: none; + font-family: var(--display); + font-size: 1.5rem; letter-spacing: -0.015em; + white-space: nowrap; } -.brand img { - width: 36px; - height: 36px; - object-fit: contain; +.astryx-brand img { filter: drop-shadow(0 2px 4px rgba(42, 36, 25, 0.15)); } -.brand em { - font-style: italic; +.astryx-brand em { color: var(--accent); + font-style: italic; } -.nav-links { - position: relative; - display: flex; - gap: 0.35rem; - margin-left: auto; +.astryx-desktop-nav, +.astryx-desktop-actions, +.astryx-nav-actions, +.astryx-stars { + display: inline-flex; align-items: center; } - -/* Collapsed navigation button — hidden while the full tab bar fits. */ -.nav-hamburger { - display: none; - margin-left: auto; - width: 38px; - height: 38px; - border-radius: 8px; - background: transparent; - border: 1px solid var(--rule-soft); - color: var(--ink-2); - cursor: pointer; - align-items: center; - justify-content: center; - transition: - background 0.15s, - color 0.15s, - border-color 0.15s; +.astryx-nav-actions { + gap: 0.35rem; } -.nav-hamburger:hover { - color: var(--ink); - background: var(--paper-2); - border-color: var(--rule); +.astryx-stars { + gap: 0.2rem; + font-family: var(--mono); + font-size: 0.72rem; } - -@media (max-width: 1400px) { - .nav-hamburger { - display: inline-flex; +@media (max-width: 767px) { + .site-top-nav { + padding-inline: 0.75rem; } - .nav-links { + .astryx-desktop-nav, + .astryx-desktop-actions { display: none; - position: absolute; - top: 100%; - left: 0; - right: 0; - margin-left: 0; - flex-direction: column; - align-items: stretch; - gap: 0.3rem; - padding: 0.85rem clamp(1.25rem, 3vw, 2.5rem) 1.1rem; - background: color-mix(in oklab, var(--paper) 96%, transparent); - backdrop-filter: blur(8px) saturate(1.2); - border-bottom: 1px solid var(--rule-soft); - box-shadow: var(--shadow); } - .nav-links[data-open="true"] { - display: flex; - animation: routeIn 0.22s ease-out both; + .astryx-brand { + font-size: 1.3rem; } - .nav-indicator { - display: none; - } - .nav-link { - width: 100%; - text-align: left; - padding: 0.7rem 0.95rem; - border-radius: 8px; - } - .nav-link[data-active="true"] { - background: var(--paper-2); - border-color: var(--rule-soft); - } - .nav-cta { - margin-left: 0; - margin-top: 0.4rem; - justify-content: center; - } -} -.nav-indicator { - position: absolute; - top: 0; - left: 0; - background: var(--paper-2); - border: 1px solid var(--rule-soft); - border-radius: 8px; - pointer-events: none; - z-index: 0; - transition: - transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), - width 0.32s cubic-bezier(0.22, 1, 0.36, 1), - height 0.32s cubic-bezier(0.22, 1, 0.36, 1); - will-change: transform, width; -} -@media (prefers-reduced-motion: reduce) { - .nav-indicator { - transition: none; + .astryx-brand img { + width: 30px; + height: 30px; } } -.nav-link { - position: relative; - z-index: 1; - font-family: var(--body); - font-size: 0.92rem; - font-weight: 500; - padding: 0.5rem 0.9rem; - border-radius: 8px; - color: var(--ink-2); - cursor: pointer; - border: 1px solid transparent; - background: transparent; - transition: color 0.15s; - font-feature-settings: "ss01"; -} -.nav-link:hover { - color: var(--ink); - text-decoration: none; -} -.nav-link[data-active="true"] { - color: var(--ink); -} -.nav-cta { - margin-left: 0.5rem; - padding: 0.55rem 1rem; - border-radius: 8px; - background: var(--ink); - color: var(--paper); - font-weight: 600; - font-size: 0.9rem; - border: 1px solid var(--ink); - cursor: pointer; - display: inline-flex; - align-items: center; - gap: 0.45rem; - transition: transform 0.12s; -} -.nav-cta:hover { - text-decoration: none; - transform: translateY(-1px); -} - -/* Theme toggle button — sits immediately after the brand on every viewport. */ -.nav-mode { - margin-left: 0.5rem; - flex-shrink: 0; - width: 32px; - height: 32px; - border-radius: 8px; - background: transparent; - color: var(--ink-2); - border: 1px solid var(--rule-soft); - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - transition: - background 0.15s, - color 0.15s, - border-color 0.15s; -} -.nav-mode:hover { - background: var(--paper-2); - color: var(--ink); - border-color: var(--rule); -} -/* GitHub stars chip baked into the CTA */ -.nav-cta-github { - padding-right: 0.6rem; - gap: 0.55rem; -} -.nav-stars { - display: inline-flex; - align-items: center; - gap: 0.25rem; - padding: 0.15rem 0.5rem; - border-radius: 999px; - background: color-mix(in oklab, var(--paper) 18%, transparent); - font-family: var(--mono); - font-size: 0.72rem; - font-weight: 600; - letter-spacing: 0.02em; - color: var(--paper); -} -.nav-stars svg { - color: #f0b57a; -} -[data-theme="espresso"] .nav-stars svg { - color: #8a4a13; +.container { + width: 100%; + max-width: 1280px; + margin: 0 auto; + padding: 0 clamp(1.25rem, 3vw, 2.5rem); } /* ---------- hero ---------- */ @@ -536,6 +404,9 @@ p { gap: 3rem; align-items: center; } +.hero-grid > * { + min-width: 0; +} @media (min-width: 960px) { .hero-grid { grid-template-columns: 1.15fr 1fr; @@ -774,82 +645,37 @@ p { align-items: center; gap: 0.85rem; } -/* Generic tabbed command picker — used for both the OS picker - (Quick install card) and the package-manager picker (npm / Bun / - pnpm sections). sli.dev / vite.dev style. Each tab gets its brand - color via `[data-active="true"][data-key=""]` selectors so we - don't need a per-key class. Inactive tabs render in muted ink so - the selection is unambiguous. */ +/* Astryx owns the control behavior. This project-owned frame makes the + selector and its code result read as one warm-paper installation surface. */ .cmd-tabs-wrap { display: flex; flex-direction: column; + min-width: 0; + max-width: 100%; } .cmd-tabs { - display: flex; + width: fit-content; + max-width: 100%; gap: 0.25rem; padding: 0.25rem; background: var(--paper-2); border: 1px solid var(--rule-soft); - border-bottom: none; + border-bottom: 0; border-radius: 12px 12px 0 0; - width: fit-content; - max-width: 100%; - flex-wrap: wrap; -} -.cmd-tab { - display: inline-flex; - align-items: center; - gap: 0.5rem; - padding: 0.4rem 0.85rem; - background: transparent; - border: 1px solid transparent; - border-radius: 8px; - font-family: var(--body); - font-size: 0.85rem; - font-weight: 500; - color: var(--ink-3); - cursor: pointer; - transition: - background 0.15s, - color 0.15s, - border-color 0.15s; } -.cmd-tab:hover { - color: var(--ink-2); - background: color-mix(in oklab, var(--ink) 5%, transparent); -} -.cmd-tab[data-active="true"] { - background: var(--paper); - border-color: var(--rule-soft); - color: var(--ink); -} -/* Brand color for the icon in the active tab. Inactive tabs keep - the muted text color so the icon visually dims with the label. - Same pattern works for both PM (npm/bun/pnpm) and OS - (macos/linux/windows) pickers — the [data-key] attribute selector - doesn't care about the tab's domain. */ -.cmd-tab[data-active="true"][data-key="npm"] svg { - color: #cb3837; -} -.cmd-tab[data-active="true"][data-key="bun"] svg { - color: #f7a8b8; -} -.cmd-tab[data-active="true"][data-key="pnpm"] svg { - color: #f9ad00; -} -.cmd-tab[data-active="true"][data-key="macos"] svg, -.cmd-tab[data-active="true"][data-key="linux"] svg, -.cmd-tab[data-active="true"][data-key="windows"] svg { +.cmd-tabs [role="radio"][aria-checked="true"] { color: var(--ink); + background: var(--paper); + font-weight: 600; } -.cmd-tab svg { +.cmd-tabs [role="radio"] svg { flex-shrink: 0; } -/* The CopyableCommand inside the panel sits flush against the tab bar - above it — drop the top-left radius so they form one continuous - surface, with the active tab visually "extruded" from the panel. */ -.cmd-tabs-wrap .install-block { - border-radius: 0 12px 12px 12px; +.cmd-tabs-wrap[data-command-set="os"] .cmd-tabs [role="radio"] { + min-width: 6rem; +} +.cmd-tabs-wrap [role="tabpanel"] .astryx-codeblock { + border-start-start-radius: 0; } .install-meta { @@ -1101,7 +927,7 @@ p { } /* ---------- sections / cards ---------- */ -.section { +.content-section { padding: clamp(3rem, 6vw, 5rem) 0; position: relative; } @@ -1820,14 +1646,13 @@ a.info-term { gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); } -.feature { - padding: 1.5rem; - background: var(--paper-2); - border: 1px solid var(--rule-soft); - border-radius: 14px; - position: relative; +.paper-card { + --color-border-emphasized: var(--rule-soft); } -.feature .ficon { +.feature-card { + --radius-container: 14px; +} +.feature-card .ficon { width: 38px; height: 38px; display: grid; @@ -1835,43 +1660,21 @@ a.info-term { border-radius: 10px; background: var(--paper-3); border: 1px solid var(--rule-soft); - margin-bottom: 1rem; color: var(--accent); } -.feature h4 { - margin-bottom: 0.4rem; - color: var(--ink); -} -.feature p { - color: var(--ink-2); - font-size: 0.95rem; - margin: 0; -} -.faq-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: 0.85rem; -} -.faq-item { - border: 1px solid var(--rule-soft); - border-radius: 8px; - background: var(--paper-2); - padding: 1rem 1.1rem; +.faq-list { + --radius-container: 8px; } -.faq-item summary { - cursor: pointer; - color: var(--ink); - font-weight: 700; - list-style-position: outside; +.faq-list .astryx-collapsible + .astryx-collapsible { + border-top: 1px solid var(--rule-soft); } -.faq-item summary::marker { - color: var(--accent); +.faq-list .astryx-collapsible > button { + min-height: 3.25rem; + padding: 0.85rem 1rem; } -.faq-item p { - margin: 0.75rem 0 0; - color: var(--ink-2); - font-size: 0.95rem; +.faq-list .astryx-collapsible > button + div { + padding: 0 1rem 1rem; } /* decorative drop */ @@ -2034,16 +1837,67 @@ a.info-term { } /* ---------- docs ---------- */ +.docs-route { + padding-block: 2.5rem 4rem; +} .docs-shell { display: grid; grid-template-columns: 1fr; gap: 2rem; - padding: 2.5rem 0 4rem; + padding: 0; } @media (min-width: 900px) { .docs-shell { - grid-template-columns: 240px 1fr; - gap: 3rem; + grid-template-columns: minmax(220px, 260px) minmax(0, 1fr); + gap: clamp(2rem, 4vw, 3.5rem); + } +} +@media (min-width: 1180px) { + .docs-shell-astryx { + grid-template-columns: minmax(220px, 260px) minmax(0, 1fr) 210px; + } +} + +.docs-mobile-nav { + display: none; +} +.docs-outline { + position: sticky; + top: 5.5rem; + align-self: start; + max-height: calc(100vh - 7rem); + overflow-y: auto; +} +.docs-side-astryx { + padding-right: 0; +} +.docs-prose { + min-width: 0; +} +@media (max-width: 1179px) { + .docs-outline { + display: none; + } +} +@media (max-width: 899px) { + .docs-side-astryx { + display: none; + } + .docs-mobile-nav { + display: block; + margin-bottom: 1.5rem; + border: 1px solid var(--rule-soft); + border-radius: 10px; + background: var(--paper-2); + } + .docs-mobile-nav summary { + cursor: pointer; + padding: 0.8rem 1rem; + color: var(--ink); + font-weight: 600; + } + .docs-mobile-nav > :not(summary) { + border-top: 1px solid var(--rule-soft); } } @@ -2149,6 +2003,16 @@ a.info-term { font-size: 1rem; line-height: 1.7; } +.docs-markdown-image { + display: block; + width: auto; + max-width: min(180px, 100%); + height: auto; + margin-block: 0.5rem; +} +.docs-logo-image { + margin-block: -1rem; +} .docs-main h1 { font-size: clamp(2.5rem, 4vw, 3.5rem); margin-bottom: 0.4rem; @@ -2848,43 +2712,6 @@ a.info-term { user-select: none; } -/* Code block with a left-side line-number gutter. Used by the - Compiler Pipeline panels, EXCLUDED snippets, and any other static - code on the landing page so all rendered samples have one look. */ -.numbered-code { - display: flex; - background: var(--paper); - border: 1px solid var(--rule-soft); - border-radius: 8px; - overflow: hidden; -} -.numbered-code-gutter { - margin: 0; - padding: 0.85rem 0.5rem 0.85rem 0.7rem; - background: color-mix(in oklab, var(--paper-2) 70%, var(--paper-3)); - border-right: 1px solid var(--rule-soft); - color: var(--ink-3); - font-family: var(--mono); - font-size: 0.82rem; - line-height: 1.5; - text-align: right; - user-select: none; - white-space: pre; - min-width: 2.5ch; -} -.numbered-code-body { - margin: 0; - padding: 0.85rem 1rem; - flex: 1; - min-width: 0; - overflow-x: auto; - font-family: var(--mono); - font-size: 0.82rem; - line-height: 1.5; -} -.numbered-code-body code { - white-space: pre; -} .pg-log-line { padding: 0.12rem 0; border-bottom: 1px dashed transparent; diff --git a/website/src/app/opengraph-image.tsx b/website/src/app/opengraph-image.tsx index c7aeeb181..52c9f9ee4 100644 --- a/website/src/app/opengraph-image.tsx +++ b/website/src/app/opengraph-image.tsx @@ -1,11 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { ImageResponse } from "next/og"; -import { - CANONICAL_SITE_URL, - SITE_DESCRIPTION, - SITE_TITLE, -} from "@/lib/site-url"; +import { CANONICAL_SITE_URL, SITE_TITLE } from "@/lib/site-url"; export const alt = SITE_TITLE; export const size = { width: 1200, height: 630 }; @@ -83,7 +79,7 @@ export default async function OpenGraphImage() { -
+
- {SITE_DESCRIPTION} + + Sandbox-first ECMAScript runtime. + + + Portable. Embeddable. Host-controlled. +
@@ -142,10 +145,10 @@ export default async function OpenGraphImage() { justifyContent: "flex-end", alignItems: "center", fontFamily: "IBM Plex Sans", - fontSize: 24, + fontSize: 28, fontWeight: 600, color: "#7a6b4f", - borderTop: "1px solid #c9b98e", + borderTop: "2px solid #b9a779", paddingTop: 24, }} > diff --git a/website/src/app/sitemap.ts b/website/src/app/sitemap.ts index 1bcaadd33..238512728 100644 --- a/website/src/app/sitemap.ts +++ b/website/src/app/sitemap.ts @@ -1,45 +1,11 @@ -import fs from "node:fs"; -import path from "node:path"; import type { MetadataRoute } from "next"; -import { DOC_PAGES, type DocPage } from "@/lib/docs-data"; +import { type DocsPage, docsSource } from "@/lib/docs-source"; import { getSiteUrl } from "@/lib/site-url"; const APP_LAST_MODIFIED = new Date(); -function statMtime(filePath: string): Date | null { - try { - return fs.statSync(filePath).mtime; - } catch { - return null; - } -} - -function docSourceCandidates(page: DocPage): string[] { - const cwd = process.cwd(); - const parent = path.resolve(cwd, ".."); - const sourceFromWebsiteRoot = - page.file === "readme.md" - ? path.join(parent, "README.md") - : path.join(parent, "docs", page.file); - const sourceFromRepoRoot = - page.file === "readme.md" - ? path.join(cwd, "README.md") - : path.join(cwd, "docs", page.file); - - return [ - sourceFromWebsiteRoot, - sourceFromRepoRoot, - path.join(cwd, "content", "docs", page.file), - path.join(cwd, "website", "content", "docs", page.file), - ]; -} - -export function getDocLastModified(page: DocPage): Date { - for (const candidate of docSourceCandidates(page)) { - const mtime = statMtime(candidate); - if (mtime) return mtime; - } - return APP_LAST_MODIFIED; +export function getDocLastModified(page: DocsPage): Date { + return page.data.lastModified ?? APP_LAST_MODIFIED; } export function buildSitemap( @@ -51,7 +17,6 @@ export function buildSitemap( const top = [ "/", "/installation", - "/docs", "/compatibility", "/performance", "/playground", @@ -62,11 +27,11 @@ export function buildSitemap( changeFrequency: "weekly" as const, priority: path === "/" ? 1.0 : 0.8, })); - const docs = DOC_PAGES.filter((p) => p.id !== "readme").map((p) => ({ - url: `${normalizedSiteUrl}/docs/${p.id}`, - lastModified: getDocLastModified(p), + const docs = docsSource.getPages().map((page) => ({ + url: `${normalizedSiteUrl}${page.url}`, + lastModified: getDocLastModified(page), changeFrequency: "weekly" as const, - priority: 0.6, + priority: page.url === "/docs" ? 0.8 : 0.6, })); return [...top, ...docs]; } diff --git a/website/src/components/command-tabs.tsx b/website/src/components/command-tabs.tsx index eac55a462..7532863bb 100644 --- a/website/src/components/command-tabs.tsx +++ b/website/src/components/command-tabs.tsx @@ -1,14 +1,14 @@ "use client"; -import { type ComponentType, useEffect, useState } from "react"; +import { CodeBlock } from "@astryxdesign/core/CodeBlock"; import { - HighlightedGeneric, - HighlightedShell, -} from "@/components/highlighted-code"; + SegmentedControl, + SegmentedControlItem, +} from "@astryxdesign/core/SegmentedControl"; +import { type ComponentType, useEffect, useState } from "react"; import { AppleIcon, BunIcon, - CopyIcon, DenoIcon, LinuxIcon, NpmIcon, @@ -156,50 +156,14 @@ export function CopyableCommand({ command: string; language?: "shell" | "ts"; }) { - const [copyTick, setCopyTick] = useState(0); - const copy = async () => { - let ok = false; - try { - if (navigator.clipboard?.writeText) { - await navigator.clipboard.writeText(command); - ok = true; - } - } catch {} - if (ok) setCopyTick((t) => t + 1); - }; - useEffect(() => { - if (copyTick === 0) return; - const id = setTimeout(() => setCopyTick(0), 1500); - return () => clearTimeout(id); - }, [copyTick]); - const copied = copyTick > 0; return ( -
- -
-        
-          {language === "shell" ? (
-            
-          ) : (
-            
-          )}
-        
-      
-
+ ); } @@ -225,6 +189,11 @@ export function CommandTabs({ initialKey?: string; }) { const [active, setActive] = useState(tabs[0]?.key ?? ""); + const commandSet = tabs.some((tab) => tab.key === "macos") + ? "os" + : tabs.some((tab) => tab.key === "npm") + ? "package-manager" + : "generic"; useEffect(() => { if (typeof window === "undefined") return; @@ -267,28 +236,25 @@ export function CommandTabs({ }; return ( -
-
+
+ {tabs.map((t) => { - const selected = active === t.key; return ( - + value={t.key} + label={t.label} + icon={t.Icon ? : undefined} + /> ); })} -
+
diff --git a/website/src/components/docs-mdx.tsx b/website/src/components/docs-mdx.tsx new file mode 100644 index 000000000..a5d0200bb --- /dev/null +++ b/website/src/components/docs-mdx.tsx @@ -0,0 +1,130 @@ +import path from "node:path"; +import { Blockquote } from "@astryxdesign/core/Blockquote"; +import { Code, CodeBlock } from "@astryxdesign/core/CodeBlock"; +import Link from "next/link"; +import type { ComponentProps, ReactElement, ReactNode } from "react"; +import { isValidElement } from "react"; +import { MermaidBlock } from "@/components/mermaid-block"; +import { type DocsPage, docsSource } from "@/lib/docs-source"; + +const REPOSITORY_BLOB_URL = + "https://github.com/frostney/GocciaScript/blob/main"; + +function resolveRepositoryHref(href: string, page: DocsPage): string { + if ( + href.startsWith("#") || + href.startsWith("/") || + /^[a-z][a-z\d+.-]*:/i.test(href) + ) { + return docsSource.resolveHref(href, page); + } + + const match = href.match(/^([^?#]*)(\?[^#]*)?(#.*)?$/); + const pathname = match?.[1] ?? href; + const suffix = `${match?.[2] ?? ""}${match?.[3] ?? ""}`; + const repositoryPath = path.posix.normalize( + path.posix.join(path.posix.dirname(page.path), pathname), + ); + const candidates = [ + repositoryPath, + `${repositoryPath}.md`, + path.posix.join(repositoryPath, "README.md"), + ]; + const target = docsSource + .getPages() + .find((candidate) => candidates.includes(candidate.path)); + + if (target) return `${target.url}${suffix}`; + + // The docs collection intentionally exposes README.md and docs/**/*.md. + // Keep links to other repository files useful by sending them to GitHub + // instead of inventing website routes that cannot resolve. + return `${REPOSITORY_BLOB_URL}/${repositoryPath}${suffix}`; +} + +function textContent(value: ReactNode): string { + if (typeof value === "string" || typeof value === "number") { + return String(value); + } + if (Array.isArray(value)) return value.map(textContent).join(""); + if (isValidElement<{ children?: ReactNode }>(value)) { + return textContent(value.props.children); + } + return ""; +} + +function codeDetails(children: ReactNode): { code: string; language?: string } { + if (!isValidElement<{ children?: ReactNode; className?: string }>(children)) { + return { code: textContent(children) }; + } + const language = children.props.className?.match(/language-([^\s]+)/)?.[1]; + return { + code: textContent(children.props.children).replace(/\n$/, ""), + language, + }; +} + +export function docsMdxComponents(page: DocsPage) { + return { + a({ href = "", children, ...props }: ComponentProps<"a">) { + const resolved = resolveRepositoryHref(href, page); + const isInternal = resolved.startsWith("/") || resolved.startsWith("#"); + return isInternal ? ( + + {children} + + ) : ( + + {children} + + ); + }, + blockquote({ children, ...props }: ComponentProps<"blockquote">) { + return
{children}
; + }, + code({ children, className }: ComponentProps<"code">) { + return {children}; + }, + img({ src, alt = "", className, ...props }: ComponentProps<"img">) { + const isRepositoryLogo = + page.path === "README.md" && alt === "GocciaScript logo"; + const resolvedSrc = + typeof src === "string" + ? src === "./logo.png" + ? "/logo.png" + : src + : (src as { src?: string } | undefined)?.src; + // Repository docs can contain arbitrary image sizes, so a semantic img + // is the honest rendering primitive here; authors still own alt text. + return ( + // biome-ignore lint/performance/noImgElement: repository Markdown has no fixed dimensions + {alt} + ); + }, + pre({ children }: ComponentProps<"pre">) { + const { code, language } = codeDetails(children); + if (language === "mermaid") return ; + return ( + + ); + }, + } satisfies Record ReactElement>; +} diff --git a/website/src/components/docs-nav.tsx b/website/src/components/docs-nav.tsx new file mode 100644 index 000000000..473d55a04 --- /dev/null +++ b/website/src/components/docs-nav.tsx @@ -0,0 +1,66 @@ +import { + SideNav, + SideNavHeading, + SideNavItem, + SideNavSection, +} from "@astryxdesign/core/SideNav"; + +export type DocsNavPage = { + href: string; + path: string; + title: string; +}; + +const GROUPS = [ + { title: "Start here", matches: (path: string) => path === "README.md" }, + { + title: "Guides & reference", + matches: (path: string) => /^docs\/[^/]+\.md$/.test(path), + }, + { + title: "Contributing", + matches: (path: string) => path.startsWith("docs/contributing/"), + }, + { + title: "Architecture decisions", + matches: (path: string) => path === "docs/adr/README.md", + }, +] as const; + +export function DocsNav({ + pages, + activeHref, +}: { + pages: DocsNavPage[]; + activeHref: string; +}) { + return ( + + } + > + {GROUPS.map((group) => { + const items = pages.filter((page) => group.matches(page.path)); + if (items.length === 0) return null; + return ( + + {items.map((page) => ( + + ))} + + ); + })} + + ); +} diff --git a/website/src/components/docs.tsx b/website/src/components/docs.tsx deleted file mode 100644 index aaa353d52..000000000 --- a/website/src/components/docs.tsx +++ /dev/null @@ -1,103 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { useEffect, useMemo, useState } from "react"; -import { AnchorH2 } from "@/components/anchor-heading"; -import { Markdown } from "@/components/markdown"; -import { DOC_PAGES, type DocPage } from "@/lib/docs-data"; - -function findPage(id: string): DocPage { - return DOC_PAGES.find((p) => p.id === id) ?? DOC_PAGES[0]; -} - -export function Docs({ - pageId = "readme", - source, -}: { - pageId?: string; - /** Markdown source for the active page; null when the file isn't synced. */ - source: string | null; -}) { - const page = findPage(pageId); - const [navOpen, setNavOpen] = useState(false); - - // Auto-collapse the mobile sidebar whenever the active page changes. - // biome-ignore lint/correctness/useExhaustiveDependencies: closes on route change only - useEffect(() => { - setNavOpen(false); - }, [pageId]); - - const groups = useMemo(() => { - const g: Record = {}; - for (const p of DOC_PAGES) { - if (!g[p.group]) g[p.group] = []; - g[p.group].push(p); - } - return g; - }, []); - - return ( -
- - -
- {/* `null` is reserved for "file isn't synced". An empty string is a - real (if blank) doc and must still render via `Markdown`. */} - {source !== null ? ( - - ) : ( -
-

{page.title}

- {page.desc && ( -

{page.desc}

- )} - -
- Not synced. The markdown file for this page - wasn't found at content/docs/{page.file}. Run{" "} - bun run sync-docs from the website directory to pull - the latest content from the repo's docs/ folder. -
- - Meanwhile -

- You can read the Overview for a summary - of the feature set, or jump into the{" "} - Playground to try the language. -

-
- )} -
-
- ); -} diff --git a/website/src/components/landing.tsx b/website/src/components/landing.tsx index 55b6f5497..493f78bc9 100644 --- a/website/src/components/landing.tsx +++ b/website/src/components/landing.tsx @@ -1,5 +1,11 @@ "use client"; +import { Card } from "@astryxdesign/core/Card"; +import { CodeBlock } from "@astryxdesign/core/CodeBlock"; +import { Collapsible, CollapsibleGroup } from "@astryxdesign/core/Collapsible"; +import { Heading } from "@astryxdesign/core/Heading"; +import { Text } from "@astryxdesign/core/Text"; +import { VStack } from "@astryxdesign/core/VStack"; import Link from "next/link"; import { Fragment, @@ -13,10 +19,7 @@ import { AnchorH2, AnchorH3 } from "@/components/anchor-heading"; import { AnimatedOutput } from "@/components/animated-output"; import { useRunShortcut } from "@/components/command-tabs"; import { ConsolePanel } from "@/components/console-panel"; -import { - HighlightedCode, - HighlightedGeneric, -} from "@/components/highlighted-code"; +import { HighlightedCode } from "@/components/highlighted-code"; import { ArrowIcon, BookIcon, @@ -29,7 +32,6 @@ import { ShieldIcon, } from "@/components/icons"; import { LatestVersion } from "@/components/latest-version"; -import { NumberedCode } from "@/components/numbered-code"; import { QuickInstall } from "@/components/quick-install"; import type { OutputLine } from "@/lib/examples"; import { formatError } from "@/lib/format-error"; @@ -591,9 +593,15 @@ console.log("total:", total);`; {active === 0 && (
example.js
- - - +
)} @@ -710,12 +718,16 @@ console.log("total:", total);`; id={panelJsonId} aria-labelledby={tabJsonId} > - - - + code={RESULT} + language="json" + hasLanguageLabel={false} + hasLineNumbers + hasCopyButton={false} + size="sm" + width="100%" + />
)}
@@ -962,7 +974,7 @@ export function Landing({
-
+
Design principles
@@ -998,20 +1010,30 @@ export function Landing({ {FEATURES.map((f) => { const I = FEATURE_ICONS[f.icon]; return ( -
-
- -
-

{f.title}

-

{f.body}

-
+ + +
+ +
+ + {f.title} + + {f.body} + + +
+
); })}
-
+
@@ -1144,7 +1166,7 @@ export function Landing({
-
+
FAQ
@@ -1154,18 +1176,25 @@ export function Landing({ embedding the runtime.

-
- {FAQ_ITEMS.map((item) => ( -
- {item.question} -

{item.answer}

-
- ))} -
+ + + {FAQ_ITEMS.map((item) => ( + + + {item.answer} + + + ))} + +
-
+
Compiler pipeline
diff --git a/website/src/components/markdown.tsx b/website/src/components/markdown.tsx deleted file mode 100644 index 1a7e8375d..000000000 --- a/website/src/components/markdown.tsx +++ /dev/null @@ -1,459 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { Fragment, type ReactNode, useMemo } from "react"; -import { slugify } from "@/components/anchor-heading"; -import { - HighlightedCode, - HighlightedGeneric, - HighlightedJson, - HighlightedShell, -} from "@/components/highlighted-code"; -import { MermaidBlock } from "@/components/mermaid-block"; -import { DOC_HREF_MAP } from "@/lib/docs-data"; - -/** Map markdown fence info-strings to one of the highlighter dispatchers. */ -function renderHighlighted(code: string, lang: string): ReactNode { - const l = lang.toLowerCase().trim(); - if (l === "javascript" || l === "js" || l === "jsx") { - return ; - } - if (l === "typescript" || l === "ts" || l === "tsx") { - return ; - } - if (l === "pascal" || l === "pas" || l === "fpc" || l === "delphi") { - return ; - } - if (l === "csharp" || l === "cs" || l === "c#") { - return ; - } - if (l === "cpp" || l === "c++" || l === "cxx") { - return ; - } - if (l === "crystal" || l === "cr") { - return ; - } - if (l === "json") { - return ; - } - if (l === "shell" || l === "sh" || l === "bash" || l === "zsh") { - return ; - } - return code; -} - -type InlineToken = - | { kind: "text"; text: string } - | { kind: "code"; text: string } - | { kind: "strong"; text: string } - | { kind: "em"; text: string } - | { kind: "image"; alt: string; src: string } - | { kind: "link"; text: string; href: string }; - -const PATTERNS: { re: RegExp; build: (m: RegExpMatchArray) => InlineToken }[] = - [ - { re: /`([^`]+)`/, build: (m) => ({ kind: "code", text: m[1] }) }, - { re: /\*\*([^*]+)\*\*/, build: (m) => ({ kind: "strong", text: m[1] }) }, - { re: /\*([^*]+)\*/, build: (m) => ({ kind: "em", text: m[1] }) }, - { - re: /!\[([^\]]*)\]\(([^)]+)\)/, - build: (m) => ({ kind: "image", alt: m[1], src: m[2] }), - }, - { - re: /\[([^\]]+)\]\(([^)]+)\)/, - build: (m) => ({ kind: "link", text: m[1], href: m[2] }), - }, - ]; - -/** Schemes we allow in fallback `` rendering. Anything else - * (e.g. `javascript:`, `data:`, `vbscript:`) falls through to plain - * text — defense in depth even though our docs corpus is trusted. */ -const SAFE_HREF_SCHEMES = new Set(["http", "https", "mailto", "tel"]); - -/** Return `href` if it's safe to use directly, otherwise `null`. - * Safe forms: in-page anchor (`#…`), root-relative path (`/…`), - * protocol-relative (`//…`), or one of the allowlisted schemes. - * - * Browsers strip TAB / CR / LF / leading whitespace from `href` values - * before navigating, so a naïve scheme regex on the raw input would let - * `" javascript:"` or `"j\tavascript:"` slip through and fire the - * dangerous scheme. We reject anything containing control characters - * outright, then trim and validate. */ -export function safeHref(href: string): string | null { - if (!href) return null; - // C0 control range (0x00–0x1F) + DEL (0x7F). If browsers would silently - // strip these to reach a different scheme, treat the href as unsafe - // rather than try to mirror the stripping algorithm exactly. - for (let i = 0; i < href.length; i++) { - const code = href.charCodeAt(i); - // C0 controls (0x00-0x1F) plus DEL (0x7F). - if (code < 0x20 || code === 0x7f) return null; - } - const h = href.trim(); - if (!h) return null; - if (h.startsWith("#") || h.startsWith("/")) return h; - const m = h.match(/^([a-z][a-z0-9+.-]*):/i); - if (!m) return h; // bare relative path or fragment-relative - return SAFE_HREF_SCHEMES.has(m[1].toLowerCase()) ? h : null; -} - -/** True when `line` is a markdown horizontal rule (`---`, `------`, etc.). */ -function isHr(line: string): boolean { - return /^---+\s*$/.test(line); -} - -/** True when `line` + `next` together form a markdown table head + delimiter - * (a `|`-bearing header line followed by a `---`-bearing delimiter line). */ -function isTableStart(line: string, next: string): boolean { - return /\|/.test(line) && /\|/.test(next) && /-{3,}/.test(next); -} - -function tokenizeInline(text: string): InlineToken[] { - const out: InlineToken[] = []; - let rest = text; - while (rest.length) { - let earliest: { - idx: number; - m: RegExpMatchArray; - build: (m: RegExpMatchArray) => InlineToken; - } | null = null; - for (const p of PATTERNS) { - const m = rest.match(p.re); - if ( - m && - m.index !== undefined && - (earliest === null || m.index < earliest.idx) - ) { - earliest = { idx: m.index, m, build: p.build }; - } - } - if (!earliest) { - out.push({ kind: "text", text: rest }); - break; - } - if (earliest.idx > 0) { - out.push({ kind: "text", text: rest.slice(0, earliest.idx) }); - } - out.push(earliest.build(earliest.m)); - rest = rest.slice(earliest.idx + earliest.m[0].length); - } - return out; -} - -/** Resolve a markdown link to an internal `/docs/...` path. Returns null - * when the link is external or doesn't map to any known doc. */ -function resolveDocHref(href: string): string | null { - // Skip protocol-prefixed (https://, mailto:, etc.) and pure anchors. - if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return null; - if (href.startsWith("#")) return null; - - // Split off any anchor fragment so it carries through to the resolved URL. - const hashIdx = href.indexOf("#"); - const pathPart = hashIdx >= 0 ? href.slice(0, hashIdx) : href; - const hash = hashIdx >= 0 ? href.slice(hashIdx) : ""; - - // Normalize path: - // ../docs/architecture.md → architecture - // docs/architecture.md → architecture - // architecture.md → architecture - // contributing/workflow.md → contributing/workflow - const normalized = pathPart - .replace(/^\.\.\//, "") - .replace(/^\.\//, "") - .replace(/^docs\//, "") - .replace(/\.md$/i, ""); - - if (!normalized) return null; - const id = DOC_HREF_MAP[normalized]; - return id ? `/docs/${id}${hash}` : null; -} - -function renderInline(text: string): ReactNode[] { - return tokenizeInline(text).map((tok, i) => { - switch (tok.kind) { - case "text": - return {tok.text}; - case "code": - return {tok.text}; - case "strong": - return {tok.text}; - case "em": - return {tok.text}; - case "image": { - let src = tok.src; - if (src === "./logo.png") src = "/logo.png"; - return ( - // biome-ignore lint/performance/noImgElement: markdown body inserts dynamic images, no fixed dimensions known - {tok.alt} - ); - } - case "link": { - // Pure in-page anchors stay in-page. - if (tok.href.startsWith("#")) { - return ( - - {tok.text} - - ); - } - const docHref = resolveDocHref(tok.href); - if (docHref) { - return ( - - {tok.text} - - ); - } - // External / unknown link. Sanitize the scheme — `javascript:` - // and friends fall through to plain text rather than becoming a - // navigable element. Markdown body comes from our own docs, so - // this is defense in depth, not the primary defense. - const safe = safeHref(tok.href); - if (!safe) { - return {tok.text}; - } - return ( - - {tok.text} - - ); - } - default: - return null; - } - }); -} - -export type Block = - | { kind: "heading"; level: number; text: string } - | { kind: "hr" } - | { kind: "quote"; text: string } - | { kind: "list"; items: string[] } - | { kind: "table"; hdr: string[]; rows: string[][] } - | { kind: "code"; lang: string; code: string } - | { kind: "p"; text: string }; - -export function parseMarkdown(source: string): Block[] { - const lines = source.replace(/\r\n/g, "\n").split("\n"); - const blocks: Block[] = []; - let i = 0; - while (i < lines.length) { - const line = lines[i]; - - if (/^```/.test(line)) { - const lang = line.slice(3).trim(); - const buf: string[] = []; - i++; - while (i < lines.length && !/^```/.test(lines[i])) { - buf.push(lines[i]); - i++; - } - i++; - blocks.push({ kind: "code", lang, code: buf.join("\n") }); - continue; - } - - const hm = line.match(/^(#{1,4})\s+(.*)$/); - if (hm) { - blocks.push({ kind: "heading", level: hm[1].length, text: hm[2] }); - i++; - continue; - } - - if (isHr(line)) { - blocks.push({ kind: "hr" }); - i++; - continue; - } - - if (/^>\s?/.test(line)) { - const buf: string[] = []; - while (i < lines.length && /^>\s?/.test(lines[i])) { - buf.push(lines[i].replace(/^>\s?/, "")); - i++; - } - blocks.push({ kind: "quote", text: buf.join("\n") }); - continue; - } - - if (isTableStart(line, lines[i + 1] || "")) { - // Split table cells on `|`, but treat `\|` as a literal pipe. - // Uses a long, unlikely sentinel rather than a single control char - // so collisions with arbitrary input remain effectively impossible - // even for content that happens to use control characters. - const PIPE_PLACEHOLDER = "__GOCCIA_MD_ESCAPED_PIPE__"; - const PIPE_RESTORE = new RegExp(PIPE_PLACEHOLDER, "g"); - const splitRow = (raw: string) => - raw - .replace(/\\\|/g, PIPE_PLACEHOLDER) - .split("|") - .map((c) => c.trim().replace(PIPE_RESTORE, "|")); - // Strip ONLY the leading/trailing empty cells produced by the - // outer `|` markers. Using `filter(Boolean)` for the header would - // also drop intentional empty cells (`| a | | b |`) and - // desynchronize column counts vs. the rows array. - const stripOuterEmpty = (cells: string[]) => - cells.filter( - (c, idx, arr) => - !(idx === 0 && c === "") && !(idx === arr.length - 1 && c === ""), - ); - const hdr = stripOuterEmpty(splitRow(line)); - i += 2; - const rows: string[][] = []; - while (i < lines.length && /\|/.test(lines[i]) && lines[i].trim()) { - rows.push(stripOuterEmpty(splitRow(lines[i]))); - i++; - } - blocks.push({ kind: "table", hdr, rows }); - continue; - } - - if (/^[-*]\s+/.test(line)) { - const items: string[] = []; - while (i < lines.length && /^[-*]\s+/.test(lines[i])) { - items.push(lines[i].replace(/^[-*]\s+/, "")); - i++; - } - blocks.push({ kind: "list", items }); - continue; - } - - if (!line.trim()) { - i++; - continue; - } - - const buf = [line]; - i++; - while ( - i < lines.length && - lines[i].trim() && - // Block-starting markers: heading, fence, blockquote, list item. - !/^(#{1,4}\s|```|>\s?|[-*]\s)/.test(lines[i]) && - // A bare `---` line is a horizontal rule, not a paragraph - // continuation — without this guard, paragraphs swallow HRs. - !isHr(lines[i]) && - // The header row of a `| col | col |` / `| --- | --- |` table is - // a paragraph-shaped line and would otherwise be slurped. - !isTableStart(lines[i], lines[i + 1] || "") - ) { - buf.push(lines[i]); - i++; - } - blocks.push({ kind: "p", text: buf.join(" ") }); - } - return blocks; -} - -export function Markdown({ source }: { source: string }) { - const blocks = useMemo(() => parseMarkdown(source), [source]); - - // Counter shared across all headings in this document. Repeated heading - // text would otherwise produce duplicate `id` attributes — anchors then - // jump to the first occurrence only. Append `-2`, `-3`, … for each - // subsequent appearance so every heading id is unique and the anchor - // href matches the final id. - const seenSlugs = new Map(); - const uniqueSlug = (text: string): string => { - const base = slugify(text); - const count = (seenSlugs.get(base) ?? 0) + 1; - seenSlugs.set(base, count); - return count === 1 ? base : `${base}-${count}`; - }; - - return ( -
- {blocks.map((b, idx) => { - switch (b.kind) { - case "heading": { - const level = Math.min(b.level, 4); - const inline = renderInline(b.text); - // h1 is the document/page title; keep it plain (no anchor link). - if (level === 1) return

{inline}

; - const slug = uniqueSlug(b.text); - const anchor = ( - - # - - ); - if (level === 2) - return ( -

- {inline} - {anchor} -

- ); - if (level === 3) - return ( -

- {inline} - {anchor} -

- ); - return ( -

- {inline} - {anchor} -

- ); - } - case "hr": - return
; - case "quote": - return
{renderInline(b.text)}
; - case "list": - return ( -
    - {b.items.map((it, j) => ( -
  • {renderInline(it)}
  • - ))} -
- ); - case "table": - return ( - - - - {b.hdr.map((h, j) => ( - - ))} - - - - {b.rows.map((r, j) => ( - - {r.map((c, k) => ( - - ))} - - ))} - -
{renderInline(h)}
{renderInline(c)}
- ); - case "code": - if ((b.lang || "").trim().toLowerCase() === "mermaid") { - return ; - } - return ( -
-                {renderHighlighted(b.code, b.lang)}
-              
- ); - case "p": - return

{renderInline(b.text)}

; - default: - return null; - } - })} -
- ); -} diff --git a/website/src/components/numbered-code.tsx b/website/src/components/numbered-code.tsx deleted file mode 100644 index 361882460..000000000 --- a/website/src/components/numbered-code.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { ReactNode } from "react"; - -/** Code block with a left-side line-number gutter — used across the - * homepage so all rendered code samples have the same look. The - * `lineCount` is supplied by the caller (from the source string or - * rendered text) so we don't have to introspect React children to - * count newlines. - * - * Wrap the actual highlighted content as `children`; the component - * provides only the gutter + container chrome. */ -export function NumberedCode({ - children, - lineCount, - className = "", -}: { - children: ReactNode; - lineCount: number; - className?: string; -}) { - const gutter = Array.from({ length: lineCount }, (_, i) => i + 1).join("\n"); - return ( -
- -
-        {children}
-      
-
- ); -} diff --git a/website/src/components/performance-dashboard.tsx b/website/src/components/performance-dashboard.tsx index 9162b8c76..c48fcef09 100644 --- a/website/src/components/performance-dashboard.tsx +++ b/website/src/components/performance-dashboard.tsx @@ -1,5 +1,9 @@ "use client"; +import { + SegmentedControl, + SegmentedControlItem, +} from "@astryxdesign/core/SegmentedControl"; import { useMemo, useState } from "react"; import type { PerformanceDashboardData, @@ -461,19 +465,21 @@ export function PerformanceDashboard({ versions change.
-
- Choose timeline range + setRange(value as TimelineRange)} + size="sm" + > {RANGE_OPTIONS.map((option) => ( - + value={option.value} + label={option.label} + /> ))} -
+
{(["awfy", "jetstream"] as const).map((suite) => { diff --git a/website/src/components/playground.tsx b/website/src/components/playground.tsx index 874d97903..b57de96eb 100644 --- a/website/src/components/playground.tsx +++ b/website/src/components/playground.tsx @@ -1,5 +1,12 @@ "use client"; +import { Button } from "@astryxdesign/core/Button"; +import { CheckboxInput } from "@astryxdesign/core/CheckboxInput"; +import { IconButton } from "@astryxdesign/core/IconButton"; +import { + SegmentedControl, + SegmentedControlItem, +} from "@astryxdesign/core/SegmentedControl"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useId, useRef, useState } from "react"; import { AnimatedOutput } from "@/components/animated-output"; @@ -1031,100 +1038,74 @@ export function Playground({
- - - + />
-
setBackend(value as Backend)} + size="sm" > -
+ + + -
- Flags - - - + id={compatFunctionId} + label="function" + value={compatFunction} + onChange={setCompatFunction} + size="sm" + />
{/* The dropdown enumerates whatever `vendor/manifest.json` advertised @@ -1152,22 +1133,24 @@ export function Playground({
- - - +
@@ -1204,12 +1191,12 @@ export function Playground({
{EXAMPLES.filter((e) => e.id !== "blank").map((e) => ( - + ))}
{(() => { diff --git a/website/src/components/sandbox.tsx b/website/src/components/sandbox.tsx index e418de176..747e48b5b 100644 --- a/website/src/components/sandbox.tsx +++ b/website/src/components/sandbox.tsx @@ -1,5 +1,7 @@ "use client"; +import { Button } from "@astryxdesign/core/Button"; +import { CheckboxInput } from "@astryxdesign/core/CheckboxInput"; import { useCallback, useEffect, useId, useRef, useState } from "react"; import { AnchorH2 } from "@/components/anchor-heading"; import { AnimatedOutput } from "@/components/animated-output"; @@ -63,15 +65,14 @@ function ToolCallComparison() { return ( <>
- + Each step expands to the request body for that turn — instructions, tool definitions, prior function calls, and function-call outputs. @@ -825,19 +826,21 @@ export function Sandbox() {

- + isDisabled={running} + label={running ? "Running" : "Run"} + icon={} + tooltip={`Run · ${runShortcut.long}`} + variant="primary" + size="sm" + endContent={ + + } + />
diff --git a/website/src/components/site-shell.tsx b/website/src/components/site-shell.tsx index 21fc608de..e52fa6f25 100644 --- a/website/src/components/site-shell.tsx +++ b/website/src/components/site-shell.tsx @@ -1,64 +1,33 @@ "use client"; +import { AppShell } from "@astryxdesign/core/AppShell"; +import { Button } from "@astryxdesign/core/Button"; +import { IconButton } from "@astryxdesign/core/IconButton"; +import { LinkProvider } from "@astryxdesign/core/Link"; +import { MobileNav } from "@astryxdesign/core/MobileNav"; +import { SideNavItem, SideNavSection } from "@astryxdesign/core/SideNav"; +import { TopNav, TopNavHeading, TopNavItem } from "@astryxdesign/core/TopNav"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import { - useCallback, - useEffect, - useLayoutEffect, - useRef, - useState, -} from "react"; -import { - CloseIcon, - GithubIcon, - MenuIcon, - MoonIcon, - StarIcon, - SunIcon, -} from "@/components/icons"; +import { useEffect, useRef, useState } from "react"; +import { GithubIcon, MoonIcon, StarIcon, SunIcon } from "@/components/icons"; import { formatStars, GITHUB_REPO_URL } from "@/lib/github"; -const useIsoLayoutEffect = - typeof window !== "undefined" ? useLayoutEffect : useEffect; - -type IndicatorRect = { - left: number; - top: number; - width: number; - height: number; -}; - type Theme = "cream" | "espresso"; -type Tab = - | "home" - | "install" - | "docs" - | "compatibility" - | "performance" - | "playground" - | "sandbox"; - -const TABS: { id: Tab; label: string; href: string }[] = [ - { id: "home", label: "Home", href: "/" }, - { id: "install", label: "Installation", href: "/installation" }, - { id: "docs", label: "Docs", href: "/docs" }, - { id: "compatibility", label: "Compatibility", href: "/compatibility" }, - { id: "performance", label: "Performance", href: "/performance" }, - { id: "playground", label: "Playground", href: "/playground" }, - { id: "sandbox", label: "Sandbox", href: "/sandbox" }, -]; -function activeTab(pathname: string): Tab { - if (pathname === "/" || pathname === "") return "home"; - if (pathname.startsWith("/installation")) return "install"; - if (pathname.startsWith("/docs")) return "docs"; - if (pathname.startsWith("/compatibility")) return "compatibility"; - if (pathname.startsWith("/performance")) return "performance"; - if (pathname.startsWith("/playground")) return "playground"; - if (pathname.startsWith("/sandbox")) return "sandbox"; - return "home"; +const TABS = [ + { label: "Home", href: "/" }, + { label: "Installation", href: "/installation" }, + { label: "Docs", href: "/docs" }, + { label: "Compatibility", href: "/compatibility" }, + { label: "Performance", href: "/performance" }, + { label: "Playground", href: "/playground" }, + { label: "Sandbox", href: "/sandbox" }, +] as const; + +function isActive(pathname: string, href: string): boolean { + return href === "/" ? pathname === "/" : pathname.startsWith(href); } export function SiteShell({ @@ -71,231 +40,171 @@ export function SiteShell({ const [theme, setTheme] = useState("cream"); const userOverrideRef = useRef(false); const pathname = usePathname() ?? "/"; - const active = activeTab(pathname); - - const tabBarRef = useRef(null); - const [indicator, setIndicator] = useState(null); - // `data-ready` flips on once the *first* indicator transition lands so - // the CSS can opt the element into transitions only after the initial - // jump-to-position. Has to be state, not a ref — mutating a ref does - // not re-render, so the attribute would never make it onto the DOM. - const [indicatorReady, setIndicatorReady] = useState(false); - const [menuOpen, setMenuOpen] = useState(false); - - // Reposition the active-tab indicator. Reads layout from the DOM so - // we follow whatever the browser actually painted — Flexbox gaps, - // font-loading shifts, sub-pixel rounding all resolve correctly. - // `useCallback([])` so the function identity is stable across renders - // and the ResizeObserver effect below doesn't re-run on every parent - // re-render. - const measureIndicator = useCallback(() => { - const bar = tabBarRef.current; - if (!bar) return; - const activeEl = bar.querySelector( - '.nav-link[data-active="true"]', - ); - if (!activeEl) return; - const barRect = bar.getBoundingClientRect(); - const r = activeEl.getBoundingClientRect(); - setIndicator({ - left: r.left - barRect.left, - top: r.top - barRect.top, - width: r.width, - height: r.height, - }); - }, []); - - // Re-measure on active-tab change. Layout effect (not regular effect) - // so the indicator's transform updates in the same frame as the tab - // becoming active — no flash of "indicator at old position". - useIsoLayoutEffect(() => { - measureIndicator(); - }, [active, measureIndicator]); - - // Re-measure whenever the nav bar's box changes — covers the obvious - // window resize, but also: font-loading shifts that change link - // widths, the GitHub stars badge populating after `fetchStars()` - // resolves, the mobile hamburger menu opening/closing, and any other - // layout reflow that wouldn't fire a `window.resize` event. - useEffect(() => { - const bar = tabBarRef.current; - if (!bar || typeof ResizeObserver === "undefined") return; - const ro = new ResizeObserver(() => measureIndicator()); - ro.observe(bar); - return () => ro.disconnect(); - }, [measureIndicator]); - - // Close mobile menu on route change. - // biome-ignore lint/correctness/useExhaustiveDependencies: only path change - useEffect(() => { - setMenuOpen(false); - }, [pathname]); - // Close mobile menu on Escape. - useEffect(() => { - if (!menuOpen) return; - const onKey = (e: KeyboardEvent) => { - if (e.key === "Escape") setMenuOpen(false); - }; - window.addEventListener("keydown", onKey); - return () => window.removeEventListener("keydown", onKey); - }, [menuOpen]); - - // Mount once: sync state with the user's system theme and listen for later - // changes until the user makes an explicit choice. useEffect(() => { const fromDom = document.documentElement.dataset.theme; if (fromDom === "espresso" || fromDom === "cream") setTheme(fromDom); - if (!window.matchMedia) return; - const mq = window.matchMedia("(prefers-color-scheme: dark)"); + const media = window.matchMedia?.("(prefers-color-scheme: dark)"); + if (!media) return; if (fromDom !== "espresso" && fromDom !== "cream") { - setTheme(mq.matches ? "espresso" : "cream"); + setTheme(media.matches ? "espresso" : "cream"); } - const onChange = (e: MediaQueryListEvent) => { - if (!userOverrideRef.current) setTheme(e.matches ? "espresso" : "cream"); + const onChange = (event: MediaQueryListEvent) => { + if (!userOverrideRef.current) { + setTheme(event.matches ? "espresso" : "cream"); + } }; - mq.addEventListener("change", onChange); - return () => mq.removeEventListener("change", onChange); + media.addEventListener("change", onChange); + return () => media.removeEventListener("change", onChange); }, []); useEffect(() => { document.documentElement.dataset.theme = theme; }, [theme]); - return ( -
-
-
- - - - GocciaScript - - - - - -
-
+ const navigation = ( +
+ {TABS.map((tab) => ( + + ))} +
+ ); -
{children}
+ const mobileNavigation = ( + + + {TABS.map((tab) => ( + + ))} + } + /> + + + ); -
-
-
-
- - - GocciaScript - + return ( + +
+ + + + GocciaScript + + + } + logoLabel="GocciaScript home" + headingHref="/" + /> + } + startContent={navigation} + endContent={ +
+ + ) : ( + + ) + } + variant="ghost" + size="sm" + aria-pressed={theme === "espresso"} + onClick={() => { + userOverrideRef.current = true; + setTheme(theme === "espresso" ? "cream" : "espresso"); + }} + /> +
+
+
+ } + /> + } + > + {children} +
+ +
+
+
+
+ + + GocciaScript + +
+ + — A drop of JavaScript, sandboxed by default. +
- - — A drop of JavaScript, sandboxed by default. -
-
-
-
-
- © 2026 GocciaScript contributors +
+
+
+ © 2026 GocciaScript contributors +
-
-
-
+ + + ); } diff --git a/website/src/components/test262-dashboard.tsx b/website/src/components/test262-dashboard.tsx index 0aca73591..a0645f3d9 100644 --- a/website/src/components/test262-dashboard.tsx +++ b/website/src/components/test262-dashboard.tsx @@ -1,5 +1,9 @@ "use client"; +import { + SegmentedControl, + SegmentedControlItem, +} from "@astryxdesign/core/SegmentedControl"; import { type CSSProperties, useMemo, useRef, useState } from "react"; import type { Test262CategorySummary, @@ -701,19 +705,21 @@ function TimelineRangeControl({ {fmt(visiblePoints.length)} of {fmt(totalPoints)} daily points -
- Timeline range + onChange(next as TimelineRange)} + size="sm" + > {TIMELINE_RANGES.map((option) => ( - + value={option.value} + label={option.label} + /> ))} -
+
); } diff --git a/website/src/lib/agent-discovery-header.mjs b/website/src/lib/agent-discovery-header.mjs new file mode 100644 index 000000000..7fc24ff9f --- /dev/null +++ b/website/src/lib/agent-discovery-header.mjs @@ -0,0 +1,8 @@ +export const AGENT_DISCOVERY_LINK_HEADER = [ + '; rel="api-catalog"; type="application/linkset+json"; profile="https://www.rfc-editor.org/info/rfc9727"', + '; rel="oauth-protected-resource"; type="application/json"', + '; rel="mcp-server-card"; type="application/json"', + '; rel="agent-skills"; type="application/json"', + '; rel="alternate"; type="text/plain"', + '; rel="service-doc"; type="text/html"', +].join(", "); diff --git a/website/src/lib/agent-discovery.ts b/website/src/lib/agent-discovery.ts index b62b1e67b..48497573b 100644 --- a/website/src/lib/agent-discovery.ts +++ b/website/src/lib/agent-discovery.ts @@ -1,4 +1,7 @@ import { createHash } from "node:crypto"; + +export { AGENT_DISCOVERY_LINK_HEADER } from "./agent-discovery-header.mjs"; + import { gocciaRunInputSchema, MAX_GOCCIA_CODE_BYTES, @@ -23,15 +26,6 @@ export const GOCCIA_API_SKILL_PATH = export const COMPATIBILITY_MARKDOWN_PATH = "/compatibility.md"; export const LLMS_TEXT_PATH = "/llms.txt"; -export const AGENT_DISCOVERY_LINK_HEADER = [ - `<${API_CATALOG_PATH}>; rel="api-catalog"; type="application/linkset+json"; profile="https://www.rfc-editor.org/info/rfc9727"`, - `<${OAUTH_PROTECTED_RESOURCE_PATH}>; rel="oauth-protected-resource"; type="application/json"`, - `<${MCP_SERVER_CARD_PATH}>; rel="mcp-server-card"; type="application/json"`, - `<${AGENT_SKILLS_INDEX_PATH}>; rel="agent-skills"; type="application/json"`, - `<${LLMS_TEXT_PATH}>; rel="alternate"; type="text/plain"`, - `; rel="service-doc"; type="text/html"`, -].join(", "); - const API_ENDPOINTS = [ { href: "/api/execute", type: "application/json" }, { href: "/api/test", type: "application/json" }, diff --git a/website/src/lib/doc-source.ts b/website/src/lib/doc-source.ts deleted file mode 100644 index 75d412be9..000000000 --- a/website/src/lib/doc-source.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Server-only — relies on Node `fs`. Importing this from a client component -// will fail at bundle time (no fallback for `node:fs/promises`), which is the -// behaviour we want. -import fs from "node:fs/promises"; -import path from "node:path"; -import { DOC_PAGES } from "@/lib/docs-data"; - -const DOCS_ROOT = path.join(process.cwd(), "content", "docs"); - -/** - * Read the markdown source for a doc page from the synced `content/docs/` - * tree. Returns `null` when the file is missing — callers render a stub. - */ -export async function readDocSource(id: string): Promise { - const page = DOC_PAGES.find((p) => p.id === id); - if (!page?.file) return null; - const full = path.join(DOCS_ROOT, page.file); - try { - return await fs.readFile(full, "utf8"); - } catch (err) { - // "Missing" is the documented stub-rendering case; anything else - // (permissions, disk error, …) is a real problem the operator should - // see, not silently mask as "not synced". - if ( - err && - typeof err === "object" && - "code" in err && - (err as { code?: string }).code === "ENOENT" - ) { - return null; - } - throw err; - } -} diff --git a/website/src/lib/docs-data.ts b/website/src/lib/docs-data.ts deleted file mode 100644 index e29ac66f8..000000000 --- a/website/src/lib/docs-data.ts +++ /dev/null @@ -1,239 +0,0 @@ -export type DocPage = { - /** URL slug; the `/docs/` route. */ - id: string; - /** Sidebar / metadata title. */ - title: string; - /** Sidebar group heading. */ - group: string; - /** Short description used on stub fallbacks. */ - desc?: string; - /** Path of the markdown file under `content/docs/`, relative to that root. */ - file: string; -}; - -export const DOC_PAGES: DocPage[] = [ - { - id: "readme", - title: "Overview", - group: "Start here", - file: "readme.md", - }, - { - id: "tutorial", - title: "Tutorial", - group: "Start here", - desc: "A guided walkthrough writing your first GocciaScript program.", - file: "tutorial.md", - }, - { - id: "language", - title: "Language", - group: "Language", - desc: "ECMAScript support, recommended defaults, compatibility flags, and rationale.", - file: "language.md", - }, - { - id: "language-tables", - title: "Language Tables", - group: "Language", - desc: "Quick-reference: feature matrix and TC39 proposal status.", - file: "language-tables.md", - }, - { - id: "built-ins", - title: "Built-in Objects", - group: "Language", - desc: "Complete API reference for every built-in global.", - file: "built-ins.md", - }, - { - id: "built-ins-temporal", - title: "Temporal Built-ins", - group: "Language", - desc: "Temporal API: dates, times, durations, time zones.", - file: "built-ins-temporal.md", - }, - { - id: "built-ins-binary-data", - title: "Binary Data Built-ins", - group: "Language", - desc: "ArrayBuffer, SharedArrayBuffer, TypedArray API.", - file: "built-ins-binary-data.md", - }, - { - id: "built-ins-data-formats", - title: "Data Format Built-ins", - group: "Language", - desc: "JSON, JSON5, TOML, YAML, JSONL, CSV, TSV parsers.", - file: "built-ins-data-formats.md", - }, - { - id: "errors", - title: "Errors", - group: "Language", - desc: "Error types, parser/runtime display, JSON output, Error.cause.", - file: "errors.md", - }, - { - id: "architecture", - title: "Architecture", - group: "Internals", - desc: "Pipelines, main layers, design direction, duplication boundaries.", - file: "architecture.md", - }, - { - id: "interpreter", - title: "Interpreter", - group: "Internals", - desc: "Tree-walk backend.", - file: "interpreter.md", - }, - { - id: "bytecode-vm", - title: "Bytecode VM", - group: "Internals", - desc: "Register-based bytecode backend and VM.", - file: "bytecode-vm.md", - }, - { - id: "core-patterns", - title: "Core Patterns", - group: "Internals", - desc: "Recurring implementation patterns and internal terminology.", - file: "core-patterns.md", - }, - { - id: "value-system", - title: "Value System", - group: "Internals", - desc: "Type hierarchy, virtual property access, primitives, objects.", - file: "value-system.md", - }, - { - id: "garbage-collector", - title: "Garbage Collector", - group: "Internals", - desc: "Mark-and-sweep GC: architecture, contributor rules, design rationale.", - file: "garbage-collector.md", - }, - { - id: "adr", - title: "Architecture Decision Records", - group: "Internals", - desc: "Durable architectural decisions and trade-offs.", - file: "adr/README.md", - }, - { - id: "adding-built-in-types", - title: "Adding Built-in Types", - group: "Internals", - desc: "How to add new built-in types to the engine.", - file: "adding-built-in-types.md", - }, - { - id: "embedding", - title: "Embedding the Engine", - group: "Tooling", - desc: "Embedding GocciaScript in FreePascal applications.", - file: "embedding.md", - }, - { - id: "testing", - title: "Testing", - group: "Tooling", - desc: "Test organization, running tests, coverage, CI.", - file: "testing.md", - }, - { - id: "test262", - title: "test262 Harness", - group: "Tooling", - desc: "ECMAScript conformance runner, CI reports, and baseline contract.", - file: "test262.md", - }, - { - id: "testing-api", - title: "Testing API", - group: "Tooling", - desc: "test/describe/expect API — Vitest/Jest-compatible.", - file: "testing-api.md", - }, - { - id: "benchmarks", - title: "Benchmarks", - group: "Tooling", - desc: "Benchmark runner, output formats, writing benchmarks.", - file: "benchmarks.md", - }, - { - id: "build-system", - title: "Build System", - group: "Tooling", - desc: "Build commands, compiler configuration, CI/CD.", - file: "build-system.md", - }, - { - id: "profiling", - title: "Profiling", - group: "Tooling", - desc: "Bytecode VM profiling — opcodes, functions, flame graphs.", - file: "profiling.md", - }, - { - id: "contributing-workflow", - title: "Contributing — Workflow", - group: "Community", - desc: "Branching, commits, PRs, testing, formatting.", - file: "contributing/workflow.md", - }, - { - id: "contributing-code-style", - title: "Contributing — Code Style", - group: "Community", - desc: "FreePascal style, naming, structure.", - file: "contributing/code-style.md", - }, - { - id: "contributing-tooling", - title: "Contributing — Tooling", - group: "Community", - desc: "Build tools, formatter, IDE setup.", - file: "contributing/tooling.md", - }, -]; - -// Mapping used by markdown.tsx to resolve cross-doc links like -// `docs/architecture.md` (or `CONTRIBUTING.md`) → /docs/. -// -// Generated from `DOC_PAGES` so a new doc only requires adding one entry. -// The basename (the page's `file` minus extension) maps to its `id`, the -// full slashed path also maps to the `id`, and a small set of legacy -// alias keys (`CONTRIBUTING`, `AGENTS`, `LICENSE`) are merged in -// explicitly so cross-links from synced repo-root files keep resolving. -const LEGACY_ALIASES: Record = { - README: "readme", - CONTRIBUTING: "contributing-workflow", - AGENTS: "contributing-workflow", - LICENSE: "contributing-workflow", -}; - -function buildDocHrefMap(): Record { - const map: Record = {}; - for (const page of DOC_PAGES) { - const stripped = page.file.replace(/\.md$/i, ""); - map[stripped] = page.id; - const lastSlash = stripped.lastIndexOf("/"); - if (lastSlash >= 0) { - // Also accept the basename alone, so links like `tooling.md` from a - // sibling file resolve to the right page. Nested README files are only - // addressable by their full path so they cannot shadow the root README. - const basename = stripped.slice(lastSlash + 1); - if (basename.toLowerCase() !== "readme") { - map[basename] = page.id; - } - } - } - return { ...map, ...LEGACY_ALIASES }; -} - -export const DOC_HREF_MAP: Record = buildDocHrefMap(); diff --git a/website/src/lib/docs-source.ts b/website/src/lib/docs-source.ts new file mode 100644 index 000000000..96be144ac --- /dev/null +++ b/website/src/lib/docs-source.ts @@ -0,0 +1,34 @@ +import { loader } from "fumadocs-core/source"; +import { docs } from "../../.source/server"; + +function slugsFromRepositoryPath(file: { path: string }): string[] { + if (file.path === "README.md") return []; + + const relative = file.path.replace(/^docs\//, "").replace(/\.mdx?$/i, ""); + const segments = relative.split("/"); + + // Keep the site's established flat URLs while deriving them mechanically + // from the source tree: docs/contributing/workflow.md becomes + // /docs/contributing-workflow and docs/adr/README.md becomes /docs/adr. + if (segments.at(-1)?.toLowerCase() === "readme") segments.pop(); + return segments.length === 0 ? [] : [segments.join("-")]; +} + +export const docsSource = loader({ + baseUrl: "/docs", + source: docs.toFumadocsSource(), + slugs: slugsFromRepositoryPath, +}); + +export type DocsPage = NonNullable>; + +export function docsPageTitle(page: DocsPage): string { + return page.data.title || "GocciaScript documentation"; +} + +export function docsPageDescription(page: DocsPage): string { + return ( + page.data.description || + "Reference documentation for GocciaScript — language, built-ins, architecture, and embedding." + ); +} diff --git a/website/src/lib/goccia-api.ts b/website/src/lib/goccia-api.ts index 438a41664..45f2fe200 100644 --- a/website/src/lib/goccia-api.ts +++ b/website/src/lib/goccia-api.ts @@ -215,7 +215,11 @@ function resolveBinaryPath( config.kind === "execute" ? entry.binaries.loader : entry.binaries.testRunner; - const abs = path.join(process.cwd(), "vendor", rel); + const abs = path.join( + /* turbopackIgnore: true */ process.cwd(), + "vendor", + rel, + ); if (existsSync(/* turbopackIgnore: true */ abs)) { return { ok: true, @@ -335,8 +339,8 @@ async function prepareInvocation( const dir = await mkdtemp( path.join(/* turbopackIgnore: true */ tmpdir(), "goccia-test-api-"), ); - const testFile = path.join(dir, "inline-test.js"); - const resultFile = path.join(dir, "results.json"); + const testFile = path.join(/* turbopackIgnore: true */ dir, "inline-test.js"); + const resultFile = path.join(/* turbopackIgnore: true */ dir, "results.json"); await writeFile(/* turbopackIgnore: true */ testFile, code, "utf8"); // Build the test-runner arg list defensively: each flag is gated against diff --git a/website/src/lib/site-markdown.ts b/website/src/lib/site-markdown.ts index dcbaf1b2e..21bd3e6d5 100644 --- a/website/src/lib/site-markdown.ts +++ b/website/src/lib/site-markdown.ts @@ -1,5 +1,8 @@ -import { readDocSource } from "@/lib/doc-source"; -import { DOC_PAGES, type DocPage } from "@/lib/docs-data"; +import { + docsPageDescription, + docsPageTitle, + docsSource, +} from "@/lib/docs-source"; import { EXAMPLES, type Example } from "@/lib/examples"; import { fetchLatestRelease, @@ -70,8 +73,8 @@ function releaseSummary(release: ReleaseInfo | null): string { return `Latest release: [${release.tagName}](${release.htmlUrl})${published}.${prestable}`; } -function docPageForId(id: string): DocPage | undefined { - return DOC_PAGES.find((page) => page.id === id); +function docPageForId(id: string) { + return docsSource.getPage(id === "readme" ? undefined : [id]); } function pickExample( @@ -112,23 +115,18 @@ export function resolveMarkdownRoute( async function docsMarkdown(id: string): Promise { const page = docPageForId(id); if (!page) return null; - - const source = await readDocSource(id); - if (source !== null) return source; + const markdown = await page.data.getText("processed"); + if (markdown) return markdown; return [ frontmatter( - `${page.title} - Docs - GocciaScript`, - page.desc ?? "Reference documentation for GocciaScript.", + `${docsPageTitle(page)} - Docs - GocciaScript`, + docsPageDescription(page), ), "", - `# ${page.title}`, - "", - page.desc ?? "Reference documentation for GocciaScript.", - "", - `The markdown source file for this page was not found at \`content/docs/${page.file}\`.`, + `# ${docsPageTitle(page)}`, "", - "Run `bun run sync-docs` from the website directory to sync the docs source.", + docsPageDescription(page), ].join("\n"); } diff --git a/website/src/lib/vendor-manifest-server.ts b/website/src/lib/vendor-manifest-server.ts index a6079bdc1..da77a43d5 100644 --- a/website/src/lib/vendor-manifest-server.ts +++ b/website/src/lib/vendor-manifest-server.ts @@ -17,13 +17,19 @@ let cached: VendorManifest | null = null; * is missing — local dev without `prebuild` shouldn't 500. */ export function getVendorManifest(): VendorManifest { if (cached) return cached; - const file = path.join(process.cwd(), "vendor", "manifest.json"); - if (!existsSync(file)) { + const file = path.join( + /* turbopackIgnore: true */ process.cwd(), + "vendor", + "manifest.json", + ); + if (!existsSync(/* turbopackIgnore: true */ file)) { cached = EMPTY_MANIFEST; return cached; } try { - cached = normalizeManifest(JSON.parse(readFileSync(file, "utf8"))); + cached = normalizeManifest( + JSON.parse(readFileSync(/* turbopackIgnore: true */ file, "utf8")), + ); } catch { cached = EMPTY_MANIFEST; }