From c83704de384da9f7f7fe04dc8a5e271d68797cd0 Mon Sep 17 00:00:00 2001 From: jcstein <46639943+jcstein@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:33:18 -0600 Subject: [PATCH 1/5] Migrate docs to Vocs v2 and Bun --- .github/workflows/README.md | 16 +- .github/workflows/deploy-cloudflare.yml | 68 + .github/workflows/deploy.yml | 22 +- .github/workflows/lint.yaml | 22 +- .github/workflows/preview.yaml | 17 +- .gitignore | 17 +- README.md | 43 +- app/build/rpc/components/CopyIcon.tsx | 4 +- app/build/rpc/components/DynamicRPCTOC.tsx | 284 - app/build/rpc/components/NodeAPIContent.tsx | 144 +- .../rpc/components/NotificationModal.tsx | 5 +- app/build/rpc/components/ParamModal.tsx | 102 +- app/build/rpc/components/Playground.tsx | 81 +- app/build/rpc/components/RPCMethod.tsx | 119 +- app/build/rpc/components/rpc-api.css | 374 + app/build/rpc/hooks/useDarkMode.ts | 35 +- app/build/rpc/lib/helper.ts | 4 +- app/build/rpc/node-api/page.mdx | 30 +- app/layout.tsx | 259 - app/operate/_page._mdx | 27 +- bun.lock | 1797 ++++ components/BaseImage.tsx | 16 +- components/ContentFooter.tsx | 8 +- components/FontStyles.tsx | 50 - components/WrapperWithFooter.tsx | 47 - eslint.config.mjs | 89 +- loaders/strip-mdx-route-metadata.cjs | 13 - mdx-components.js | 18 - next.config.mjs | 69 - package.json | 55 +- plugins/remark-replace-variables.mjs | 4 +- public/specs/openrpc-v0.31.3.json | 9483 +++++++++++++++++ scripts/check-links.mjs | 4 +- scripts/generate-llms.js | 11 +- scripts/prepare-vocs-pages.mjs | 145 + src/pages.gen.ts | 95 + src/vocs/compat/next-image.tsx | 46 + src/vocs/compat/next-link.tsx | 47 + src/vocs/compat/nextra-components.tsx | 67 + src/vocs/layout.tsx | 81 + src/vocs/root.css | 56 + tsconfig.json | 11 +- vite.config.ts | 19 + vocs.config.ts | 229 + worker/index.js | 243 + wrangler.jsonc | 12 + yarn.lock | 7094 ------------ 47 files changed, 13168 insertions(+), 8314 deletions(-) create mode 100644 .github/workflows/deploy-cloudflare.yml delete mode 100644 app/build/rpc/components/DynamicRPCTOC.tsx create mode 100644 app/build/rpc/components/rpc-api.css delete mode 100644 app/layout.tsx create mode 100644 bun.lock delete mode 100644 components/FontStyles.tsx delete mode 100644 components/WrapperWithFooter.tsx delete mode 100644 loaders/strip-mdx-route-metadata.cjs delete mode 100644 mdx-components.js delete mode 100644 next.config.mjs create mode 100644 public/specs/openrpc-v0.31.3.json create mode 100644 scripts/prepare-vocs-pages.mjs create mode 100644 src/pages.gen.ts create mode 100644 src/vocs/compat/next-image.tsx create mode 100644 src/vocs/compat/next-link.tsx create mode 100644 src/vocs/compat/nextra-components.tsx create mode 100644 src/vocs/layout.tsx create mode 100644 src/vocs/root.css create mode 100644 vite.config.ts create mode 100644 vocs.config.ts create mode 100644 worker/index.js create mode 100644 wrangler.jsonc delete mode 100644 yarn.lock diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 9e15661252d..b93f16e0914 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,12 +1,18 @@ # GitHub Actions workflows -This directory contains the workflows used to lint, deploy, and keep release metadata up to date for the docs site. +This directory contains the workflows used to lint, deploy, preview, and keep release metadata up to date for the docs site. -## `deploy.yml` — Deploy Docs (Nextra) to GitHub Pages +## `deploy.yml` — Deploy Docs (Vocs) to GitHub Pages - **Triggers:** `push` to `main`, or manual `workflow_dispatch`. -- **What it does:** installs deps (Node 20 + Yarn), runs `yarn generate:llms`, builds the static site (`yarn build`), then publishes `out/` to the `gh-pages` branch via `peaceiris/actions-gh-pages`. -- **Notes:** writes `out/.nojekyll` and sets `cname: docs.celestia.org`. +- **What it does:** installs deps with Bun, builds the static site (`bun run build`), then publishes `out/public/` to the `gh-pages` branch via `peaceiris/actions-gh-pages`. +- **Notes:** writes `out/public/.nojekyll` and sets `cname: docs.celestia.org`. + +## `deploy-cloudflare.yml` — Deploy to Cloudflare Pages + +- **Triggers:** manual `workflow_dispatch`, plus same-repo pull requests. +- **What it does:** builds the full-static Vocs site, copies `worker/index.js` into `out/public/_worker.js` for the Pages MCP endpoint, then deploys `out/public/` to Cloudflare Pages. +- **Required secrets:** `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`. ## `preview.yaml` — Deploy PR Preview @@ -18,7 +24,7 @@ This directory contains the workflows used to lint, deploy, and keep release met ## `lint.yaml` — Lint & Link Check - **Triggers:** `push`/`pull_request` on `main`, plus a weekly schedule (`0 9 * * 1`). -- **What it does:** runs `npm run lint` and `npm run check-links` (Node 20). +- **What it does:** runs `bun run lint` and `bun run check-links`. ## `latest-tags.yaml` — Latest Tags diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml new file mode 100644 index 00000000000..2776a8c8430 --- /dev/null +++ b/.github/workflows/deploy-cloudflare.yml @@ -0,0 +1,68 @@ +# Optional Cloudflare Pages deploy for the Vocs migration branch. +# +# Mirrors the Eden docs pattern: build a full-static Vocs site, copy a Pages +# advanced-mode Worker into the output as _worker.js for /mcp and /api/mcp, +# then deploy the output directory to Cloudflare Pages. +# +# Requires CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID repo secrets. +name: Deploy to Cloudflare Pages + +on: + workflow_dispatch: + # Temporary branch trigger for testing the Vocs migration before this + # workflow exists on the default branch. + push: + branches: + - codex/jcs/migrate-vocs-v2-bun + pull_request: + types: + - opened + - reopened + - synchronize + +concurrency: + group: deploy-cloudflare-${{ github.ref }} + cancel-in-progress: true + +env: + CLOUDFLARE_PAGES_PROJECT: ${{ vars.CLOUDFLARE_PAGES_PROJECT || 'celestia-docs-vocs-test' }} + +jobs: + deploy: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build site and add MCP worker + run: bun run prepare:cloudflare + + - name: Check Cloudflare secrets + id: cloudflare + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + if [[ -z "$CLOUDFLARE_API_TOKEN" || -z "$CLOUDFLARE_ACCOUNT_ID" ]]; then + echo "::notice::Cloudflare secrets are not set; skipping deploy." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Deploy + if: steps.cloudflare.outputs.skip != 'true' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy out/public --project-name ${{ env.CLOUDFLARE_PAGES_PROJECT }} --branch ${{ github.head_ref || github.ref_name }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f0eeebd459c..d6f76ea4e41 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Deploy Docs (Nextra) to GitHub Pages +name: Deploy Docs (Vocs) to GitHub Pages on: push: @@ -23,31 +23,27 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Setup Node - uses: actions/setup-node@v6 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: 20 - cache: yarn + bun-version: latest - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Generate llms.txt - run: yarn generate:llms + run: bun install --frozen-lockfile - name: Build static site # Note: production deploy uses the root base path (custom domain). - # Preview deploys may override BASE / NEXT_PUBLIC_BASE_PATH. - run: yarn build + # Preview deploys may override BASE. + run: bun run build - name: Disable Jekyll - run: touch out/.nojekyll + run: touch out/public/.nojekyll - name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./out + publish_dir: ./out/public force_orphan: true # The following lines assign commit authorship to the official # GH-Actions bot for deploys to `gh-pages` branch: diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 2d05ceb19b5..6be17c60500 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -21,17 +21,16 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Setup Node - uses: actions/setup-node@v6 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: 20 - cache: npm + bun-version: latest - name: Install dependencies - run: npm install --no-package-lock --legacy-peer-deps + run: bun install --frozen-lockfile - name: Run lint - run: npm run lint + run: bun run lint link-check: runs-on: ubuntu-latest @@ -39,14 +38,13 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Setup Node - uses: actions/setup-node@v6 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: 20 - cache: npm + bun-version: latest - name: Install dependencies - run: npm install --no-package-lock --legacy-peer-deps + run: bun install --frozen-lockfile - name: Run Link Checker - run: npm run check-links -- --all --concurrency=2 --skip="celestia.org,nexus.hyperlane.xyz" + run: bun run check-links -- --all --concurrency=2 --skip="celestia.org,nexus.hyperlane.xyz" diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml index 86ee2c685c4..d6c56755477 100644 --- a/.github/workflows/preview.yaml +++ b/.github/workflows/preview.yaml @@ -23,23 +23,18 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Setup Node - uses: actions/setup-node@v6 + - name: Setup Bun + uses: oven-sh/setup-bun@v2 with: - node-version: 20 - cache: yarn + bun-version: latest - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Generate llms.txt - run: yarn generate:llms + run: bun install --frozen-lockfile - name: Build static site env: BASE: /docs-preview/pr-${{ github.event.number }}/ - NEXT_PUBLIC_BASE_PATH: /docs-preview/pr-${{ github.event.number }} - run: yarn build + run: bun run build - name: Checkout docs-preview repository uses: actions/checkout@v6 @@ -58,7 +53,7 @@ jobs: rm -rrf "$TARGET_DIR"/* # Copy built files - cp -r out/* "$TARGET_DIR"/ + cp -r out/public/* "$TARGET_DIR"/ # Ensure .nojekyll exists in root touch docs-preview/.nojekyll diff --git a/.gitignore b/.gitignore index c0a76da121f..68de5afabe1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,16 +5,14 @@ /.pnp .pnp.* .yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions # testing /coverage -# next.js -/.next/ +# vocs +/.vocs/ +/dist/ +/src/pages/ /out/ # production @@ -29,6 +27,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* .pnpm-debug.log* +bun-debug.log* # env files (can opt-in for committing if needed) .env* @@ -36,11 +35,13 @@ yarn-error.log* # vercel .vercel +# cloudflare +.wrangler + # typescript *.tsbuildinfo -next-env.d.ts -# nextra +# generated search _pagefind/ # agents llms diff --git a/README.md b/README.md index 5fcc86c53a9..7d85b220fa3 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Celestia Documentation Access: - LLMs.txt: https://docs.celestia.org/llms.txt - Skill file: https://docs.celestia.org/SKILL.md (served from `public/SKILL.md`) - CIPs (Celestia Improvement Proposals): https://cips.celestia.org -- Built with: Next.js + Nextra (MDX), exported as a static site. +- Built with: Vocs v2 (MDX), Vite, and Bun, exported as a static site. - **LLM-ready**: Add `.md` to any URL to get clean markdown (e.g., `https://docs.celestia.org/learn/TIA/overview` → `https://docs.celestia.org/learn/TIA/overview.md`) - DeepWikis for @celestiaorg: - https://deepwiki.com/celestiaorg/docs @@ -39,50 +39,63 @@ Celestia Documentation Access: ## Local development -Prereqs: Node.js 20+ and Yarn. +Prereqs: Bun 1.3+. ```bash -yarn install -yarn dev +bun install +bun run dev ``` -Open http://localhost:3000 +Open http://localhost:5173 ## Build & preview the static export -`next.config.mjs` is configured for static export (`output: 'export'`). Builds write to `out/` and generate a Pagefind search index in `out/_pagefind/`. +Vocs is configured for full static rendering. Builds write the browser-ready site to `out/public/`. ```bash -yarn build -yarn start +bun run build +bun run preview +``` + +## Cloudflare Pages preview + +The Cloudflare Pages setup mirrors Eden docs: the Vocs site stays fully static, +and `worker/index.js` is copied into the output as `_worker.js` to serve `/mcp` +and `/api/mcp` from the same origin. + +```bash +bun run prepare:cloudflare +bun run deploy:cloudflare -- --project-name celestia-docs-vocs-test --branch ``` ## Base paths (deploying under a subpath) For deployments that live under a subdirectory (e.g. GitHub Pages previews), set: -- `BASE` with a trailing slash (used for `assetPrefix`) -- `NEXT_PUBLIC_BASE_PATH` without a trailing slash (used by client-side components for asset URLs) +- `BASE` with a trailing slash (used by Vocs as the base path) ```bash -BASE=/docs-preview/new_docs/ NEXT_PUBLIC_BASE_PATH=/docs-preview/new_docs yarn build +BASE=/docs-preview/new_docs/ bun run build ``` ## Content & structure - `app/**/page.mdx`: documentation pages - `app/**/_meta.js`: sidebar order/titles +- `src/vocs/`: Vocs runtime glue, compatibility components, and generated-page templates +- `src/pages/`: generated Vocs page tree, created by `bun run prepare:vocs` and ignored by git - `public/`: static assets, including the published agent skill at `public/SKILL.md` - `constants/*.json`: shared values referenced in MDX (e.g. `{{mainnetVersions['app-latest-tag']}}`), replaced by `plugins/remark-replace-variables.mjs` ## Useful scripts -- `yarn lint`: lint the codebase (also runs on `git push` via hook) -- `yarn check-links -- --all`: validate internal + external links (see `scripts/check-links.mjs --help`) -- `yarn generate:llms`: generate LLM-ready markdown files from MDX sources +- `bun run prepare:vocs`: generate the Vocs `src/pages` tree from `app` +- `bun run lint`: lint the codebase (also runs on `git push` via hook) +- `bun run check-links -- --all`: validate internal + external links (see `scripts/check-links.mjs --help`) +- `bun run generate:llms`: generate LLM-ready markdown files from MDX sources - Creates clean `.md` versions of all documentation pages - Removes JSX components, imports, and MDX-specific syntax - - Automatically runs during build process (`yarn build`) + - Automatically runs during build process (`bun run build`) - Access any doc page as markdown by adding `.md` to the URL ## Contribution guidelines diff --git a/app/build/rpc/components/CopyIcon.tsx b/app/build/rpc/components/CopyIcon.tsx index 3f699bbc10c..24558bd9c1c 100644 --- a/app/build/rpc/components/CopyIcon.tsx +++ b/app/build/rpc/components/CopyIcon.tsx @@ -8,7 +8,8 @@ export default function CopyIcon() { viewBox='0 0 24 24' strokeWidth={1.5} stroke='currentColor' - className='nx-h-6 w-6 text-gray-500 hover:text-blue-500' + width={22} + height={22} > ); } - diff --git a/app/build/rpc/components/DynamicRPCTOC.tsx b/app/build/rpc/components/DynamicRPCTOC.tsx deleted file mode 100644 index 18a70308d3d..00000000000 --- a/app/build/rpc/components/DynamicRPCTOC.tsx +++ /dev/null @@ -1,284 +0,0 @@ -/** - * NEXTRA FRAMEWORK WORKAROUND - Dynamic TOC for Node API - * - * This component uses DOM manipulation to inject dynamic headings into Nextra's - * static Table of Contents. This is necessary because Nextra's TOC is generated at - * build-time from MDX AST, not from runtime-rendered React components. - * - * @nextra-compatibility 4.6.0 - * @last-verified 2025-11-06 - * @breaking-change-risk MEDIUM - Relies on Nextra's CSS class structure and selectors - * - * === HOW IT WORKS === - * 1. MDX has a hidden "## Celestia Node API" heading → Nextra renders a TOC with one item - * 2. This component finds that TOC list, caches its styling, and replaces items with dynamic content - * 3. MutationObserver watches for content changes (e.g., version switches) and updates the TOC - * 4. IntersectionObserver provides scroll-spy functionality for active highlighting - * - * === WHY THIS APPROACH === - * - Zero CSS duplication - inherits all styling from Nextra's rendered TOC - * - Auto-updates when Nextra changes - we copy classes, not hardcode them - * - Simple - just swap list items, don't rebuild the entire TOC structure - * - Isolated - all workaround logic contained in this single file - * - * === ALTERNATIVES CONSIDERED === - * ❌ Custom MDX plugin: Too complex, breaks hot reload, maintenance burden - * ❌ Server-side TOC generation: Loses dynamic content capability - * ❌ Fork Nextra: Unsustainable maintenance burden - * ✅ DOM manipulation: Pragmatic, isolated, well-documented - * - * === TROUBLESHOOTING === - * If TOC stops working after a Nextra upgrade, check: - * - * 1. **CSS Class Changes**: - * - Nextra's TOC classes (x:, nextra-toc, subheading-anchor) - * - Run: document.querySelector('.nextra-toc a').className in DevTools - * - * 2. **Selector Changes** (see TOC_CONFIG below): - * - .nextra-toc ul (the TOC list container) - * - .nextra-toc a[href="#celestia-node-api"] (placeholder item) - * - main h2[id] (content headings) - * - * 3. **New Nextra TOC API**: - * - Check Nextra docs for runtime TOC customization APIs - * - If available, migrate away from DOM manipulation - * - * 4. **Test Cases**: - * - TOC appears on page load - * - TOC updates when version selector changes - * - Active item highlights on scroll - * - "Scroll to top" button appears/disappears correctly - * - * === PERFORMANCE === - * - MutationObserver overhead: ~1-2ms per DOM change (negligible) - * - IntersectionObserver: Highly optimized, no scroll event listeners - * - Retries with backoff: 3 attempts (500ms, 1000ms, 2000ms) for robustness - */ - -'use client' - -import { useEffect } from 'react' - -interface Heading { - id: string - value: string - depth: number -} - -// Configuration - centralized to reduce brittleness -const TOC_CONFIG = { - placeholderHeadingId: 'celestia-node-api', - selectors: { - tocList: '.nextra-toc ul', - placeholderItem: '.nextra-toc a[href="#celestia-node-api"]', - contentHeadings: 'main h2[id]', - scrollButton: '.nextra-toc button[type="button"]', - mainContent: 'main', - }, - retryDelays: [500, 1000, 2000], - scrollToTopThreshold: 2, // Show button after Nth heading (0-indexed) -} as const - -// Extract headings from DOM (excluding the placeholder) -const extractHeadingsFromDOM = (): Heading[] => { - const h2Elements = document.querySelectorAll(TOC_CONFIG.selectors.contentHeadings) - return Array.from(h2Elements) - .filter(el => el.id && el.textContent && el.id !== TOC_CONFIG.placeholderHeadingId) - .map(el => ({ - id: el.id, - value: el.textContent!.trim(), - depth: 2, - })) -} - -// Toggle scroll button visibility -const toggleScrollButton = (button: HTMLButtonElement, show: boolean) => { - if (show) { - button.removeAttribute('aria-hidden') - button.removeAttribute('hidden') - button.disabled = false - button.classList.remove('x:opacity-0') - button.classList.add('x:opacity-100') - } else { - button.setAttribute('aria-hidden', 'true') - button.setAttribute('hidden', '') - button.disabled = true - button.classList.remove('x:opacity-100') - button.classList.add('x:opacity-0') - } -} - -export function DynamicRPCTOC() { - useEffect(() => { - // Cache the classes from Nextra's rendered "celestia-node-api" item - // We need to grab these ONCE before we remove it - let cachedLinkClasses = '' - let cachedLiClasses = '' - - const findTOCList = () => - document.querySelector(TOC_CONFIG.selectors.tocList) - - const updateTOCList = () => { - const tocList = findTOCList() - if (!tocList) return - - const headings = extractHeadingsFromDOM() - if (headings.length === 0) return - - // Cache classes from placeholder on first run - if (!cachedLinkClasses) { - const placeholderLink = document.querySelector(TOC_CONFIG.selectors.placeholderItem) - const placeholderLi = placeholderLink?.parentElement as HTMLLIElement - - if (placeholderLink && placeholderLi) { - cachedLinkClasses = placeholderLink.className - cachedLiClasses = placeholderLi.className - } else { - return // Can't proceed without cached classes - } - } - - // Replace list items - tocList.innerHTML = '' - headings.forEach(({ id, value }) => { - const li = document.createElement('li') - li.className = cachedLiClasses - - const a = document.createElement('a') - a.href = `#${id}` - a.textContent = value - a.className = cachedLinkClasses - - li.appendChild(a) - tocList.appendChild(li) - }) - } - - // Try multiple times with delays for dynamic content - updateTOCList() - const timeouts = TOC_CONFIG.retryDelays.map(delay => setTimeout(updateTOCList, delay)) - - // Watch for content changes - const observer = new MutationObserver(updateTOCList) - const mainContent = document.querySelector(TOC_CONFIG.selectors.mainContent) - if (mainContent) { - observer.observe(mainContent, { childList: true, subtree: true }) - } - - return () => { - timeouts.forEach(clearTimeout) - observer.disconnect() - } - }, []) - - // Handle "Scroll to top" button visibility and behavior - useEffect(() => { - let observer: IntersectionObserver | null = null - let scrollHandler: (() => void) | null = null - let clickHandler: ((e: Event) => void) | null = null - let buttonRef: HTMLButtonElement | null = null - let mutationObserver: MutationObserver | null = null - let setupDebounceTimer: NodeJS.Timeout | null = null - - const cleanupObservers = () => { - observer?.disconnect() - if (scrollHandler) window.removeEventListener('scroll', scrollHandler) - if (clickHandler && buttonRef) { - buttonRef.removeEventListener('click', clickHandler, true) - } - } - - const setupScrollButton = () => { - // Clean up previous observers but not the mutation observer - cleanupObservers() - - const scrollButton = document.querySelector(TOC_CONFIG.selectors.scrollButton) - if (!scrollButton) return false - - const headings = extractHeadingsFromDOM() - const targetIndex = TOC_CONFIG.scrollToTopThreshold - - // Hide button if not enough visible headings - if (headings.length <= targetIndex) { - toggleScrollButton(scrollButton, false) - return false - } - - const targetHeading = document.getElementById(headings[targetIndex].id) - if (!targetHeading) { - toggleScrollButton(scrollButton, false) - return false - } - - buttonRef = scrollButton - - // Click handler - scroll to top - clickHandler = (e: Event) => { - e.preventDefault() - e.stopPropagation() - window.scrollTo({ top: 0, behavior: 'smooth' }) - } - scrollButton.addEventListener('click', clickHandler, true) - - // Visibility handler - show when scrolled past target heading - const updateButtonVisibility = () => { - // Re-query headings to handle dynamic changes - const currentHeadings = extractHeadingsFromDOM() - if (currentHeadings.length <= targetIndex) { - toggleScrollButton(scrollButton, false) - return - } - - const el = document.getElementById(currentHeadings[targetIndex].id) - if (!el) { - toggleScrollButton(scrollButton, false) - return - } - - const inUpperHalf = el.getBoundingClientRect().top < window.innerHeight / 2 - toggleScrollButton(scrollButton, inUpperHalf) - } - - // Use IntersectionObserver + scroll for reliable updates - observer = new IntersectionObserver( - () => updateButtonVisibility(), - { rootMargin: '-50% 0px -50% 0px', threshold: 0 } - ) - - scrollHandler = updateButtonVisibility - window.addEventListener('scroll', scrollHandler) - updateButtonVisibility() - - observer.observe(targetHeading) - return true - } - - // Initial setup with retries - setupScrollButton() - const timeouts = TOC_CONFIG.retryDelays.map(delay => - setTimeout(setupScrollButton, delay) - ) - - // Watch for content changes and re-setup button - const mainContent = document.querySelector(TOC_CONFIG.selectors.mainContent) - if (mainContent) { - mutationObserver = new MutationObserver(() => { - // Clear previous debounce timer - if (setupDebounceTimer) clearTimeout(setupDebounceTimer) - // Debounce re-setup to avoid excessive calls - setupDebounceTimer = setTimeout(setupScrollButton, 200) - }) - mutationObserver.observe(mainContent, { childList: true, subtree: true }) - } - - return () => { - timeouts.forEach(clearTimeout) - if (setupDebounceTimer) clearTimeout(setupDebounceTimer) - cleanupObservers() - mutationObserver?.disconnect() - } - }, []) - - // This component doesn't render anything - it manipulates the DOM - return null -} diff --git a/app/build/rpc/components/NodeAPIContent.tsx b/app/build/rpc/components/NodeAPIContent.tsx index 73516e5500a..7927a68d5f7 100644 --- a/app/build/rpc/components/NodeAPIContent.tsx +++ b/app/build/rpc/components/NodeAPIContent.tsx @@ -4,15 +4,15 @@ import axios from 'axios'; import { useEffect, useState, useCallback } from 'react'; import { INotification, MethodByPkg, OpenRPCSpec, Param } from '../lib/types'; -import { useDarkMode } from '../hooks/useDarkMode'; import NotificationModal from '../components/NotificationModal'; import Playground from '../components/Playground'; import RPCMethod from '../components/RPCMethod'; import ParamModal from '../components/ParamModal'; +import './rpc-api.css'; // Get base path for asset references -const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''; +const basePath = import.meta.env.BASE_URL?.replace(/\/$/, '') || ''; function extractAuth(methodDescription: string | undefined): string { if (!methodDescription) return ''; @@ -81,12 +81,10 @@ const versions = [ 'v0.25.3', 'v0.26.4', 'v0.28.4', + 'v0.31.3', ].reverse(); export default function RPCDocumentation() { - // Use shared dark mode hook - const isDark = useDarkMode(); - // State management const [spec, setSpec] = useState(null); const [isInitialLoading, setIsInitialLoading] = useState(true); @@ -141,11 +139,9 @@ export default function RPCDocumentation() { const targetVersion = versionParam && versions.includes(versionParam) ? versionParam : versions[0]; if (targetVersion !== selectedVersion) { - // eslint-disable-next-line react-hooks/set-state-in-effect setSelectedVersion(targetVersion); } fetchJsonData(targetVersion, true); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Scroll to hash with MutationObserver (replaces hardcoded setTimeout) @@ -206,13 +202,10 @@ export default function RPCDocumentation() { // Show loading state only on initial page load if (isInitialLoading) { return ( -
-
-
Loading API documentation...
-
+
+
+
Loading API documentation...
+
Version: {selectedVersion}
@@ -223,34 +216,16 @@ export default function RPCDocumentation() { // Show error state if (error) { return ( -
-

+
+

Error Loading Documentation

-

+

{error}

@@ -262,53 +237,40 @@ export default function RPCDocumentation() { if (!spec) return null; return ( - <> +
{/* Description and Controls */} -
+
{spec && ( -
- The Celestia Node API is the collection of RPC methods that can be used to interact with the services provided by Celestia Data Availability Nodes. Node API uses auth tokens to control access to this API.{' '} +
+ Showing the OpenRPC schema for{' '} - ({spec.info.version}) + celestia-node {spec.info.version} + .
)} {/* Search and Version Picker - Search fills space, Version floats right */} -
+
setSearchTerm(e.target.value)} - className="x:px-3 x:py-2 x:border x:border-gray-300 x:dark:border-gray-600 x:rounded-md x:text-sm x:text-gray-900 x:dark:text-gray-100 x:placeholder-gray-500 x:dark:placeholder-gray-400 x:shadow-sm" - style={{ - backgroundColor: isDark ? 'rgb(17 24 39)' : 'white', - flex: '1 1 0%', - minWidth: '0' - }} + className="rpc-api__search" /> -
- API version: +
+ API version: {isSwitchingVersion && ( -
+
)}
@@ -335,10 +287,9 @@ export default function RPCDocumentation() { {/* Methods by Package */}
{spec && @@ -366,38 +317,35 @@ export default function RPCDocumentation() { ); return ( -
-

+

{pkg == 'p2p' ? 'P2P' : pkg} - + > + # +

- -
+ +
{filteredMethods.map((method) => ( -
- -
+ pkg={pkg} + method={method} + activateSidebar={activateSidebar} + selectedVersion={selectedVersion} + setCurrentRequest={setCurrentRequest} + setPlaygroundOpen={setPlaygroundOpen} + /> ))}
-
+ ); })}
@@ -418,6 +366,6 @@ export default function RPCDocumentation() { notification={notification} setNotification={setNotification} /> - +
); } diff --git a/app/build/rpc/components/NotificationModal.tsx b/app/build/rpc/components/NotificationModal.tsx index 41baf44854c..37b599314b9 100644 --- a/app/build/rpc/components/NotificationModal.tsx +++ b/app/build/rpc/components/NotificationModal.tsx @@ -8,10 +8,10 @@ import { INotification } from '../lib/types'; import { useDarkMode } from '../hooks/useDarkMode'; export default function NotificationModal({ - notification, + notification = { message: '', success: true, active: false }, setNotification, }: { - notification: INotification; + notification?: INotification; setNotification: (notification: INotification) => void; }) { // Use shared dark mode hook @@ -120,4 +120,3 @@ export default function NotificationModal({
); } - diff --git a/app/build/rpc/components/ParamModal.tsx b/app/build/rpc/components/ParamModal.tsx index 8e05c13316b..3357848bdcb 100644 --- a/app/build/rpc/components/ParamModal.tsx +++ b/app/build/rpc/components/ParamModal.tsx @@ -17,66 +17,58 @@ export default function ParamModal({ if (!open) return null; return ( -
-
setOpen(false)} /> +
+
+ -
-
-
-
- -
-
-
- -
- {(() => { - const examples = currentParam.schema?.examples; - if (Array.isArray(examples) && examples.length > 0) { - return ( -

- Example Value: - - {JSON.stringify(examples[0], null, '\t')} - -

- ); - } - return null; - })()} + + + {(() => { + const examples = currentParam.schema?.examples; + if (Array.isArray(examples) && examples.length > 0) { + return ( +
+
Example value
+
+ + {JSON.stringify(examples[0], null, 2)} +
-
-
- -
-
+ ); + } + return ( +

+ No example value is included in this OpenRPC schema. +

+ ); + })()} + +
+
); } - diff --git a/app/build/rpc/components/Playground.tsx b/app/build/rpc/components/Playground.tsx index d6fead4fc38..1f8f0f7ad4b 100644 --- a/app/build/rpc/components/Playground.tsx +++ b/app/build/rpc/components/Playground.tsx @@ -4,8 +4,6 @@ import { Dialog, DialogPanel, DialogTitle, - Transition, - TransitionChild, } from '@headlessui/react'; import { CommandLineIcon } from '@heroicons/react/24/solid'; import { Editor } from '@monaco-editor/react'; @@ -208,55 +206,39 @@ const Playground = ({ // Use shared dark mode hook const isDark = useDarkMode(); + if (!playgroundOpen) return null; + return ( - - - -
- + +
+
-
-
- - -
+
-
- +
+
); }; export default Playground; - diff --git a/app/build/rpc/components/RPCMethod.tsx b/app/build/rpc/components/RPCMethod.tsx index 07dc841dbe8..6131a61d1df 100644 --- a/app/build/rpc/components/RPCMethod.tsx +++ b/app/build/rpc/components/RPCMethod.tsx @@ -16,7 +16,6 @@ const RPCMethod = ({ selectedVersion, setCurrentRequest, setPlaygroundOpen, - isDark = false, }: { pkg: string; method: Method; @@ -24,12 +23,15 @@ const RPCMethod = ({ selectedVersion: string; setCurrentRequest: (req: string) => void; setPlaygroundOpen: (open: boolean) => void; - isDark?: boolean; }) => { const [showRequest, setShowRequest] = useState(false); const [showResponse, setShowResponse] = useState(false); const [specCopied, setSpecCopied] = useState(false); const methodRef = useRef(null); + const params = method?.params ?? []; + const result = method?.result ?? { name: '', description: 'Null', schema: {} }; + + if (!method) return null; const handleCopyClick = () => { const hash = window.location.hash.substring(1); @@ -38,20 +40,20 @@ const RPCMethod = ({ }; return ( -
-
-
- {method.name}( - {method.params.map((param, i, { length }) => ( - +
+
+ {method.name}( + {params.map((param, i, { length }) => ( + {param.name}{' '} activateSidebar(param)} > {param.description} @@ -60,15 +62,15 @@ const RPCMethod = ({ ))} ) - {method.result.description !== 'Null' && ( + {result.description !== 'Null' && ( activateSidebar(method.result)} + className="rpc-method__result" + onClick={() => activateSidebar(result)} > - {method.result.description} + {result.description} )} - + perms: {method.auth}
@@ -76,22 +78,24 @@ const RPCMethod = ({ text={`${window.location.origin}${window.location.pathname}?version=${selectedVersion}#${pkg}.${method.name}`} onCopy={handleCopyClick} > -
{ window.location.hash = `${pkg}.${method.name}`; }} > -
+
-
+
{method.description}
-
+
@@ -118,11 +118,7 @@ const RPCMethod = ({ >
-
-
+
+ Request + {showRequest && ( -
+
{ + params: params.map((param) => { const examples = param.schema?.examples; return (Array.isArray(examples) && examples.length > 0) ? examples[0] @@ -187,27 +172,21 @@ const RPCMethod = ({
)} -
setShowResponse(!showResponse)} - className="x:flex x:items-center x:px-4 x:py-3 x:border-t x:border-gray-200 x:dark:border-gray-700 x:cursor-pointer x:transition-colors" - style={{ - backgroundColor: isDark ? (showResponse ? 'rgba(31, 41, 55, 0.5)' : 'rgb(31 41 55)') : (showResponse ? 'rgb(249 250 251)' : 'white') - }} + className="rpc-method__example-toggle" + aria-expanded={showResponse} > {showResponse ? ( - + ) : ( - + )} - Response -
+ Response + {showResponse && ( -
+
{ - if (method.result.description === 'Null') return []; - const examples = method.result.schema?.examples; + if (result.description === 'Null') return []; + const examples = result.schema?.examples; return (Array.isArray(examples) && examples.length > 0) ? examples[0] : []; @@ -235,7 +214,7 @@ const RPCMethod = ({
)}
-
+ ); }; export default RPCMethod; diff --git a/app/build/rpc/components/rpc-api.css b/app/build/rpc/components/rpc-api.css new file mode 100644 index 00000000000..639bc5e76e4 --- /dev/null +++ b/app/build/rpc/components/rpc-api.css @@ -0,0 +1,374 @@ +.rpc-api { + display: flex; + flex-direction: column; + gap: 2rem; + margin-top: 1.5rem; +} + +.rpc-api * { + box-sizing: border-box; +} + +.rpc-api__intro { + color: light-dark(#4b5563, #d1d5db); + font-size: 0.95rem; + line-height: 1.75; +} + +.rpc-api__intro a, +.rpc-api__link, +.rpc-method__type, +.rpc-method__result { + color: light-dark(#5b21b6, #a78bfa); + text-decoration: none; +} + +.rpc-api__intro a:hover, +.rpc-api__link:hover, +.rpc-method__type:hover, +.rpc-method__result:hover { + text-decoration: underline; +} + +.rpc-api__controls { + align-items: center; + display: flex; + gap: 0.75rem; +} + +.rpc-api__search, +.rpc-api__select { + background: light-dark(#ffffff, #111827); + border: 1px solid light-dark(#d1d5db, #374151); + border-radius: 0.5rem; + color: light-dark(#111827, #f9fafb); + font: inherit; + min-height: 2.5rem; +} + +.rpc-api__search { + flex: 1 1 auto; + min-width: 0; + padding: 0.5rem 0.75rem; +} + +.rpc-api__version { + align-items: center; + background: light-dark(#f4f4f5, #18181b); + border: 1px solid light-dark(#e4e4e7, #27272a); + border-radius: 0.625rem; + display: inline-flex; + flex: 0 0 auto; + gap: 0.5rem; + padding: 0.375rem 0.5rem 0.375rem 0.75rem; +} + +.rpc-api__version-label { + color: light-dark(#52525b, #d4d4d8); + font-size: 0.875rem; + white-space: nowrap; +} + +.rpc-api__select { + cursor: pointer; + padding: 0.35rem 2rem 0.35rem 0.65rem; +} + +.rpc-api__spinner { + animation: rpc-spin 0.7s linear infinite; + border: 2px solid light-dark(#d4d4d8, #52525b); + border-radius: 999px; + border-top-color: light-dark(#6d3df5, #a78bfa); + display: inline-block; + height: 1rem; + width: 1rem; +} + +.rpc-api__packages { + display: flex; + flex-direction: column; + gap: 2.25rem; + transition: opacity 0.2s ease-in-out; +} + +.rpc-package { + border-bottom: 1px solid light-dark(#e5e7eb, #27272a); + padding-bottom: 2rem; +} + +.rpc-package__title { + align-items: center; + border-bottom: 1px solid light-dark(#e5e7eb, #27272a); + color: light-dark(#18181b, #fafafa); + display: flex; + font-size: clamp(1.6rem, 3vw, 2.1rem); + font-weight: 650; + gap: 0.5rem; + letter-spacing: 0; + line-height: 1.2; + margin: 2.25rem 0 1.25rem; + padding-bottom: 0.5rem; +} + +.rpc-package__anchor { + color: light-dark(#a1a1aa, #71717a); + font-size: 1rem; + opacity: 0; + text-decoration: none; + transition: opacity 0.15s ease; +} + +.rpc-package__title:hover .rpc-package__anchor { + opacity: 1; +} + +.rpc-package__methods { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.rpc-method { + background: light-dark(#ffffff, #101014); + border: 1px solid light-dark(#e4e4e7, #27272a); + border-radius: 0.75rem; + padding: 1rem; + scroll-margin-top: 5rem; +} + +.rpc-method__header { + align-items: flex-start; + display: flex; + gap: 0.75rem; + justify-content: space-between; +} + +.rpc-method__signature { + color: light-dark(#52525b, #d4d4d8); + font-family: + var(--vocs-font-family-mono), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + monospace; + font-size: 0.9rem; + line-height: 1.65; + min-width: 0; + overflow-wrap: anywhere; +} + +.rpc-method__name { + color: light-dark(#111827, #f9fafb); + font-weight: 700; +} + +.rpc-method__param { + color: light-dark(#4b5563, #d1d5db); +} + +.rpc-method__type, +.rpc-method__result { + cursor: pointer; +} + +.rpc-method__auth { + align-items: center; + background: light-dark(#f3e8ff, rgb(88 28 135 / 0.35)); + border: 1px solid light-dark(#e9d5ff, rgb(168 85 247 / 0.35)); + border-radius: 999px; + color: light-dark(#5b21b6, #d8b4fe); + display: inline-flex; + font-family: var(--vocs-font-family-default), sans-serif; + font-size: 0.75rem; + font-weight: 650; + margin-left: 0.5rem; + padding: 0.15rem 0.5rem; + white-space: nowrap; +} + +.rpc-method__copy { + align-items: center; + background: transparent; + border: 0; + color: light-dark(#71717a, #a1a1aa); + cursor: pointer; + display: inline-flex; + flex: 0 0 auto; + justify-content: center; + padding: 0.25rem; +} + +.rpc-method__description { + color: light-dark(#52525b, #d4d4d8); + font-size: 0.925rem; + line-height: 1.7; + margin-top: 0.75rem; +} + +.rpc-method__actions { + display: flex; + gap: 0.5rem; + margin-top: 1rem; +} + +.rpc-button { + align-items: center; + background: light-dark(#ffffff, #18181b); + border: 1px solid light-dark(#d4d4d8, #3f3f46); + border-radius: 0.5rem; + color: light-dark(#3f3f46, #e4e4e7); + cursor: pointer; + display: inline-flex; + font: inherit; + font-size: 0.875rem; + font-weight: 600; + gap: 0.4rem; + justify-content: center; + min-height: 2.4rem; + padding: 0.5rem 0.85rem; + transition: + background-color 0.15s ease, + border-color 0.15s ease, + color 0.15s ease; +} + +.rpc-button:hover { + background: light-dark(#f8fafc, #27272a); + border-color: light-dark(#a1a1aa, #52525b); +} + +.rpc-button--primary { + flex: 1 1 auto; +} + +.rpc-button--secondary { + flex: 0 0 auto; +} + +.rpc-method__examples { + background: light-dark(#ffffff, #18181b); + border: 1px solid light-dark(#e4e4e7, #27272a); + border-radius: 0.75rem; + margin-top: 1rem; + overflow: hidden; +} + +.rpc-method__example-toggle { + align-items: center; + background: transparent; + border: 0; + border-top: 1px solid light-dark(#e4e4e7, #27272a); + color: light-dark(#18181b, #f4f4f5); + cursor: pointer; + display: flex; + font: inherit; + font-weight: 600; + gap: 0.4rem; + min-height: 2.65rem; + padding: 0.7rem 0.9rem; + text-align: left; + width: 100%; +} + +.rpc-method__example-toggle:first-child { + border-top: 0; +} + +.rpc-method__example-body { + background: light-dark(#f8fafc, #09090b); + border-top: 1px solid light-dark(#e4e4e7, #27272a); + padding: 0.9rem; +} + +.rpc-method__example-body pre { + margin: 0 !important; + white-space: pre-wrap !important; + word-break: break-word; +} + +.rpc-api__loading, +.rpc-api__error { + align-items: center; + border-radius: 0.75rem; + display: flex; + justify-content: center; + min-height: 18rem; + padding: 2rem; + text-align: center; +} + +.rpc-api__loading { + color: light-dark(#52525b, #d4d4d8); +} + +.rpc-api__error { + align-items: flex-start; + background: light-dark(#fef2f2, rgb(127 29 29 / 0.18)); + border: 1px solid light-dark(#fca5a5, rgb(248 113 113 / 0.35)); + color: light-dark(#991b1b, #fecaca); + flex-direction: column; + text-align: left; +} + +.rpc-param-modal { + align-items: center; + background: rgb(0 0 0 / 0.55); + display: flex; + inset: 0; + justify-content: center; + padding: 1rem; + position: fixed; + z-index: 10000; +} + +.rpc-param-modal__panel { + background: light-dark(#ffffff, #18181b); + border: 1px solid light-dark(#e4e4e7, #3f3f46); + border-radius: 0.75rem; + box-shadow: 0 20px 60px rgb(0 0 0 / 0.25); + color: light-dark(#18181b, #f4f4f5); + max-width: 42rem; + padding: 1.25rem; + position: relative; + width: min(100%, 42rem); +} + +.rpc-param-modal__close { + position: absolute; + right: 0.75rem; + top: 0.75rem; +} + +.rpc-param-modal__title { + font-size: 1rem; + font-weight: 650; + line-height: 1.5; + margin: 0 2.25rem 1rem 0; +} + +.rpc-param-modal__label { + color: light-dark(#52525b, #d4d4d8); + font-size: 0.875rem; + margin-bottom: 0.5rem; +} + +@media (max-width: 720px) { + .rpc-api__controls, + .rpc-api__version, + .rpc-method__actions, + .rpc-method__header { + align-items: stretch; + flex-direction: column; + } + + .rpc-api__version { + width: 100%; + } + + .rpc-api__select { + width: 100%; + } +} + +@keyframes rpc-spin { + to { + transform: rotate(360deg); + } +} diff --git a/app/build/rpc/hooks/useDarkMode.ts b/app/build/rpc/hooks/useDarkMode.ts index 59e2ed568ca..61f1f54ecbb 100644 --- a/app/build/rpc/hooks/useDarkMode.ts +++ b/app/build/rpc/hooks/useDarkMode.ts @@ -2,34 +2,37 @@ import { useState, useEffect } from 'react'; /** - * Custom hook to detect and track Nextra's dark mode state. - * - * Watches for changes to the 'dark' class on document.documentElement - * which is how Nextra manages theme switching. - * + * Tracks Vocs' active color theme. + * * @returns {boolean} true if dark mode is active, false otherwise */ export function useDarkMode(): boolean { + const readIsDark = () => { + if (typeof document === 'undefined') return false; + + const theme = document.documentElement.dataset.vocsTheme; + if (theme === 'dark') return true; + if (theme === 'light') return false; + + return window.matchMedia?.('(prefers-color-scheme: dark)').matches ?? false; + }; + const [isDark, setIsDark] = useState(() => { - if (typeof document !== 'undefined') { - return document.documentElement.classList.contains('dark'); - } - return false; + return readIsDark(); }); - + useEffect(() => { const observer = new MutationObserver(() => { - setIsDark(document.documentElement.classList.contains('dark')); + setIsDark(readIsDark()); }); - + observer.observe(document.documentElement, { attributes: true, - attributeFilter: ['class'] + attributeFilter: ['data-vocs-theme', 'style'] }); - + return () => observer.disconnect(); }, []); - + return isDark; } - diff --git a/app/build/rpc/lib/helper.ts b/app/build/rpc/lib/helper.ts index 43adafb57a7..e8c9d4462fa 100644 --- a/app/build/rpc/lib/helper.ts +++ b/app/build/rpc/lib/helper.ts @@ -1,7 +1,6 @@ import { Method, Param } from './types'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function classNames(...classes: any[]) { +export function classNames(...classes: unknown[]) { return classes.filter(Boolean).join(' '); } @@ -40,4 +39,3 @@ export const getExampleResponse = (method: Method): string => { 2 ); }; - diff --git a/app/build/rpc/node-api/page.mdx b/app/build/rpc/node-api/page.mdx index a0cbf5d21bb..90b3cf36fb6 100644 --- a/app/build/rpc/node-api/page.mdx +++ b/app/build/rpc/node-api/page.mdx @@ -1,28 +1,16 @@ -import NodeAPIContent from "../components/NodeAPIContent"; -import { DynamicRPCTOC } from "../components/DynamicRPCTOC"; +--- +showAskAi: false +--- -{/* Hide Nextra's copy button - doesn't work well with dynamic content on this page */} - +import NodeAPIContent from "../components/NodeAPIContent"; # Node API -
- -## Celestia Node API - -The Celestia Node API is the collection of RPC methods that can be used to interact with the services provided by Celestia Data Availability Nodes. Node API uses auth tokens to control access to this API. - -Celestia node RPC reference for blob, blobstream, da, das, fraud, header, node, p2p, share, and state packages. Includes methods like blob.Get, blob.GetAll, blob.GetProof, blob.Submit, share.GetShare, share.GetRange, share.GetNamespaceData, share.GetEDS, share.SharesAvailable, state.SubmitPayForBlob, state.Balance, state.Transfer, header.NetworkHead, header.GetByHeight, header.Subscribe, p2p.Peers, p2p.Connect, p2p.Info, da.Submit, da.Validate, da.GetProofs, das.SamplingStats, node.AuthNew, node.Info, blobstream.GetDataRootTupleInclusionProof, fraud.Subscribe. +The Celestia Node API is the collection of RPC methods that can be used to +interact with the services provided by Celestia Data Availability Nodes. Node +API uses auth tokens to control access to this API. -
+Use this reference to browse the OpenRPC schema for blob, blobstream, DA, DAS, +fraud, header, node, P2P, share, and state packages. - diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index e5da0a3c200..00000000000 --- a/app/layout.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { Footer, Layout, Navbar } from "nextra-theme-docs"; -import { Banner, Head } from "nextra/components"; -import { getPageMap } from "nextra/page-map"; -import Image from "next/image"; -import "nextra-theme-docs/style.css"; -import "katex/dist/katex.min.css"; -import { FontStyles } from "@/components/FontStyles"; - -const SITE_ORIGIN = "https://docs.celestia.org"; -const SITE_DESCRIPTION = - "Learn, build, and operate on Celestia - the modular data availability network."; - -// Use BASE env var (same as next.config.mjs) and ensure it's available client-side -const basePath = - process.env.NEXT_PUBLIC_BASE_PATH || - process.env.BASE?.replace(/\/$/, "") || - ""; -const withBasePath = (path: string) => `${basePath}${path}`; - -// Theme configuration -const THEME_CONFIG = { - projectLink: "https://github.com/celestiaorg/docs", - chatLink: "https://discord.com/invite/YsnTPcSfWQ", - docsRepositoryBase: "https://github.com/celestiaorg/docs/blob/main", - footerText: "Celestia Documentation", - primaryHue: 200, - primarySaturation: 100, -}; -export const metadata = { - metadataBase: new URL(SITE_ORIGIN), - title: { - default: "Celestia Documentation", - template: "%s - Celestia Documentation", - }, - description: SITE_DESCRIPTION, - openGraph: { - type: "website", - siteName: "Celestia Documentation", - title: "Celestia Documentation", - description: SITE_DESCRIPTION, - images: [ - { - url: "/Celestia-og.png", - width: 1200, - height: 630, - alt: "Celestia Documentation", - }, - ], - }, - twitter: { - card: "summary_large_image", - site: "@CelestiaOrg", - title: "Celestia Documentation", - description: SITE_DESCRIPTION, - images: ["/Celestia-og.png"], - }, - robots: { - index: true, - follow: true, - googleBot: { - index: true, - follow: true, - }, - }, -}; - -const organizationJsonLd = { - "@context": "https://schema.org", - "@type": "Organization", - name: "Celestia", - url: "https://celestia.org", - logo: `${SITE_ORIGIN}/logo-light.svg`, - sameAs: [ - "https://github.com/celestiaorg", - "https://x.com/CelestiaOrg", - "https://discord.com/invite/YsnTPcSfWQ", - ], -}; - -const websiteJsonLd = { - "@context": "https://schema.org", - "@type": "WebSite", - name: "Celestia Documentation", - url: SITE_ORIGIN, - description: SITE_DESCRIPTION, -}; - -const documentationJsonLd = { - "@context": "https://schema.org", - "@type": "TechArticle", - headline: "Celestia Documentation", - description: SITE_DESCRIPTION, - url: SITE_ORIGIN, - publisher: { - "@type": "Organization", - name: "Celestia", - url: "https://celestia.org", - }, - about: [ - "Celestia", - "data availability", - "modular blockchain", - "blobspace", - "node operation", - ], -}; - -const softwareApplicationJsonLd = { - "@context": "https://schema.org", - "@type": "SoftwareApplication", - name: "Celestia Node API", - applicationCategory: "DeveloperApplication", - operatingSystem: ["Linux", "macOS"], - url: `${SITE_ORIGIN}/build/rpc/node-api/`, - softwareHelp: `${SITE_ORIGIN}/operate/maintenance/troubleshooting/`, - isAccessibleForFree: true, - publisher: { - "@type": "Organization", - name: "Celestia", - url: "https://celestia.org", - }, -}; - -const faqJsonLd = { - "@context": "https://schema.org", - "@type": "FAQPage", - mainEntity: [ - { - "@type": "Question", - name: "Where can I troubleshoot Celestia node issues?", - acceptedAnswer: { - "@type": "Answer", - text: "Use the Celestia troubleshooting documentation for common node, sync, network, and operational issues.", - url: `${SITE_ORIGIN}/operate/maintenance/troubleshooting/`, - }, - }, - { - "@type": "Question", - name: "Where can I find the Celestia Node API reference?", - acceptedAnswer: { - "@type": "Answer", - text: "Use the Celestia Node API reference for RPC methods and the OpenRPC specification for machine-readable API details.", - url: `${SITE_ORIGIN}/build/rpc/node-api/`, - }, - }, - { - "@type": "Question", - name: "Where can I learn how to run a Celestia light node?", - acceptedAnswer: { - "@type": "Answer", - text: "Start with the light node quickstart guide, then use the troubleshooting page if setup, sync, or connectivity issues appear.", - url: `${SITE_ORIGIN}/operate/data-availability/light-node/quickstart/`, - }, - }, - ], -}; - -const jsonLd = [ - organizationJsonLd, - websiteJsonLd, - documentationJsonLd, - softwareApplicationJsonLd, - faqJsonLd, -]; - -const banner = ( - Welcome to our new docs! 🎉 -); -const navbar = ( - - } - projectLink={THEME_CONFIG.projectLink} - chatLink={THEME_CONFIG.chatLink} - /> -); -const footer =
{THEME_CONFIG.footerText}
; - -export default async function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - - - - - - - - - - - - - {jsonLd.map((data, index) => ( -