diff --git a/.github/workflows/gitbook-pages.yml b/.github/workflows/gitbook-pages.yml new file mode 100644 index 0000000..27472a8 --- /dev/null +++ b/.github/workflows/gitbook-pages.yml @@ -0,0 +1,52 @@ +name: Deploy GitBook to 9router.github.io + +on: + push: + branches: [main, master] + paths: + - "gitbook/**" + - ".github/workflows/gitbook-pages.yml" + workflow_dispatch: + +concurrency: + group: gitbook-pages + cancel-in-progress: true + +jobs: + build-deploy: + runs-on: ubuntu-latest + defaults: + run: + working-directory: gitbook + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: gitbook/package-lock.json + + - name: Install deps + run: npm ci || npm install + + - name: Build static export + run: npm run build + env: + NODE_ENV: production + NEXT_PUBLIC_BASE_PATH: "" + + - name: Add .nojekyll + run: touch out/.nojekyll + + - name: Deploy to 9router.github.io + uses: peaceiris/actions-gh-pages@v4 + with: + deploy_key: ${{ secrets.GH_PAGES_DEPLOY_KEY }} + external_repository: 9router/9router.github.io + publish_branch: main + publish_dir: gitbook/out + force_orphan: true + user_name: github-actions[bot] + user_email: github-actions[bot]@users.noreply.github.com + commit_message: "deploy: ${{ github.sha }}" diff --git a/.gitignore b/.gitignore index b1a63e6..07e7f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,4 @@ ecosystem.config.* scripts/agSniffer/* gitbooks/* +gitbook/README.md diff --git a/gitbook/.gitignore b/gitbook/.gitignore new file mode 100644 index 0000000..d972524 --- /dev/null +++ b/gitbook/.gitignore @@ -0,0 +1,44 @@ +# Dependencies +node_modules/ +.pnp +.pnp.js + +# Testing +coverage/ + +# Next.js +.next/ +out/ +build/ +dist/ + +# Production +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Misc +*.tsbuildinfo + +# Wrangler +.wrangler/ +.dev.vars diff --git a/gitbook/app/[lang]/[...slug]/page.js b/gitbook/app/[lang]/[...slug]/page.js new file mode 100644 index 0000000..792856d --- /dev/null +++ b/gitbook/app/[lang]/[...slug]/page.js @@ -0,0 +1,36 @@ +import DocsLayout from "@/components/DocsLayout"; +import DocsContent from "@/components/DocsContent"; +import { extractHeadings } from "@/utils/markdown"; +import { loadContent, getAllSlugs } from "@/lib/content"; +import { LANG_CODES, isValidLang, DEFAULT_LANG } from "@/constants/languages"; +import { notFound } from "next/navigation"; + +export const dynamicParams = false; + +export async function generateStaticParams() { + // Build params for every (lang × slug) combination based on default language slugs. + const slugs = getAllSlugs(DEFAULT_LANG); + const params = []; + for (const lang of LANG_CODES) { + for (const slug of slugs) { + params.push({ lang, slug }); + } + } + return params; +} + +export default async function DocPage({ params }) { + const { lang, slug } = await params; + if (!isValidLang(lang)) notFound(); + + const content = loadContent(lang, slug); + if (!content) notFound(); + + const headings = extractHeadings(content); + + return ( + + + + ); +} diff --git a/gitbook/app/[lang]/page.js b/gitbook/app/[lang]/page.js new file mode 100644 index 0000000..4da007c --- /dev/null +++ b/gitbook/app/[lang]/page.js @@ -0,0 +1,26 @@ +import DocsLayout from "@/components/DocsLayout"; +import DocsContent from "@/components/DocsContent"; +import { extractHeadings } from "@/utils/markdown"; +import { loadContent } from "@/lib/content"; +import { LANG_CODES, isValidLang } from "@/constants/languages"; +import { notFound } from "next/navigation"; + +export const dynamicParams = false; + +export async function generateStaticParams() { + return LANG_CODES.map(lang => ({ lang })); +} + +export default async function LangHomePage({ params }) { + const { lang } = await params; + if (!isValidLang(lang)) notFound(); + + const content = loadContent(lang, "index") || "# 9Router Documentation\n\nContent coming soon..."; + const headings = extractHeadings(content); + + return ( + + + + ); +} diff --git a/gitbook/app/globals.css b/gitbook/app/globals.css new file mode 100644 index 0000000..8ac76c1 --- /dev/null +++ b/gitbook/app/globals.css @@ -0,0 +1,190 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); +@import "tailwindcss"; + +/* Base styles */ +* { + box-sizing: border-box; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + margin: 0; + padding: 0; +} + +/* Scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: #CBD5E1; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #E68A6E; +} + +/* Code highlighting */ +pre { + background: #F1F5F9 !important; + border-radius: 8px; + padding: 1rem; + overflow-x: auto; +} + +code { + font-family: 'Monaco', 'Menlo', 'Courier New', monospace; + font-size: 0.875rem; + line-height: 1.5; +} + +/* Inline code */ +:not(pre) > code { + background: #F1F5F9; + color: #E68A6E; + padding: 0.125rem 0.375rem; + border-radius: 4px; + font-size: 0.875em; +} + +/* Markdown content styles */ +.markdown-content h1 { + font-size: 2.5rem; + font-weight: 800; + color: #E68A6E; + /* margin-top: 2rem; */ + margin-bottom: 1rem; + line-height: 1.2; + display: flex; + align-items: center; +} + +.markdown-content h1 svg { + width: 2.5rem; + height: 2.5rem; + flex-shrink: 0; +} + +.markdown-content h2 { + font-size: 2rem; + font-weight: 700; + color: #000000; + margin-top: 2rem; + margin-bottom: 1rem; + line-height: 1.3; + border-bottom: 1px solid #E5E7EB; + padding-bottom: 0.5rem; +} + +.markdown-content h3 { + font-size: 1.5rem; + font-weight: 600; + color: #000000; + margin-top: 1.5rem; + margin-bottom: 0.75rem; + line-height: 1.4; +} + +.markdown-content p { + font-size: 1.125rem; + line-height: 1.75; + margin-bottom: 1rem; + color: #6B7280; +} + +.markdown-content ul, +.markdown-content ol { + margin-left: 1.5rem; + margin-bottom: 1rem; +} + +.markdown-content li { + font-size: 1.125rem; + line-height: 1.75; + margin-bottom: 0.5rem; + color: #6B7280; +} + +.markdown-content a { + color: #E68A6E; + text-decoration: underline; + transition: opacity 0.2s; +} + +.markdown-content a:hover { + opacity: 0.8; +} + +.markdown-content blockquote { + border-left: 4px solid #E68A6E; + padding-left: 1rem; + margin: 1rem 0; + font-style: italic; + color: #6B7280; +} + +.markdown-content strong { + font-weight: 600; + color: #000000; +} + +/* Smooth scroll */ +html { + scroll-behavior: smooth; + scroll-padding-top: 5rem; /* Offset for sticky header (64px + padding) */ +} + +/* Heading anchor offset for sticky header */ +.markdown-content h1, +.markdown-content h2, +.markdown-content h3 { + scroll-margin-top: 5rem; +} + +/* Mobile menu overlay */ +.mobile-menu-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 40; + animation: fadeIn 0.2s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.mobile-menu-drawer { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 280px; + background: white; + z-index: 50; + animation: slideInLeft 0.3s ease-out; + overflow-y: auto; +} + +@keyframes slideInLeft { + from { + transform: translateX(-100%); + } + to { + transform: translateX(0); + } +} diff --git a/gitbook/app/layout.js b/gitbook/app/layout.js new file mode 100644 index 0000000..aa49925 --- /dev/null +++ b/gitbook/app/layout.js @@ -0,0 +1,21 @@ +import { DOCS_CONFIG } from "@/constants/docsConfig"; +import "./globals.css"; + +export const metadata = { + title: DOCS_CONFIG.title, + description: DOCS_CONFIG.description, +}; + +export default function RootLayout({ children }) { + return ( + + + + + + + {children} + + + ); +} diff --git a/gitbook/app/page.js b/gitbook/app/page.js new file mode 100644 index 0000000..71df826 --- /dev/null +++ b/gitbook/app/page.js @@ -0,0 +1,26 @@ +import { DEFAULT_LANG } from "@/constants/languages"; + +// Static-friendly redirect to default language (meta refresh + client script) +export const metadata = { + title: "Redirecting...", + other: { + "http-equiv:refresh": `0; url=/${DEFAULT_LANG}/` + } +}; + +export default function HomePage() { + const target = `/${DEFAULT_LANG}/`; + return ( + <> +