diff --git a/docs/.env.example b/docs/.env.example new file mode 100644 index 00000000..01693726 --- /dev/null +++ b/docs/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_POSTHOG_API_KEY= +NEXT_PUBLIC_POSTHOG_HOST= \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index b6f494e5..18ddff9a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,6 +34,14 @@ A `source.config.ts` config file has been included, you can customise different Read the [Introduction](https://fumadocs.dev/docs/mdx) for further details. +## Setup Telemetry + +We use PostHog for telemetry to improve the clarity and structure of our documentation. Start by copying the `.env.example` and adding in your PostHog API key and host. + +```bash +cp .env.example .env +``` + ## Learn More To learn more about Next.js and Fumadocs, take a look at the following diff --git a/docs/package.json b/docs/package.json index c6c083c9..d94c168e 100644 --- a/docs/package.json +++ b/docs/package.json @@ -16,6 +16,7 @@ "mermaid": "^11.8.1", "next": "15.3.3", "next-themes": "^0.4.6", + "posthog-js": "^1.276.0", "react": "^19.1.0", "react-dom": "^19.1.0", "remark": "^15.0.1", diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 21945e6b..21932097 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + posthog-js: + specifier: ^1.276.0 + version: 1.276.0 react: specifier: ^19.1.0 version: 19.1.0 @@ -489,6 +492,9 @@ packages: resolution: {integrity: sha512-6yB0117ZjsgNevZw3LP+bkrZa9mU/POPVaXgzMPOBbBc35w2P3R+1vMMhEfC06kYCpd5bf0jodBaTkYQW5TVeQ==} engines: {node: '>= 20.0.0'} + '@posthog/core@1.3.0': + resolution: {integrity: sha512-hxLL8kZNHH098geedcxCz8y6xojkNYbmJEW+1vFXsmPcExyCXIUUJ/34X6xa9GcprKxd0Wsx3vfJQLQX4iVPhw==} + '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1221,6 +1227,9 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + core-js@3.46.0: + resolution: {integrity: sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -1492,6 +1501,9 @@ packages: picomatch: optional: true + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + fumadocs-core@15.5.1: resolution: {integrity: sha512-5eJPJw+BFWFdgrtWPQ9aAZAhhsyuZAwth8OjBd9R77sXoIoae4Y4lJZMq3BeSpJZcuIAOVbSCS+pJhsBAoXJ8g==} peerDependencies: @@ -2012,6 +2024,20 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + posthog-js@1.276.0: + resolution: {integrity: sha512-FYZE1037LrAoKKeUU0pUL7u8WwNK2BVeg5TFApwquVPUdj9h7u5Z077A313hPN19Ar+7Y+VHxqYqdHc4VNsVgw==} + peerDependencies: + '@rrweb/types': 2.0.0-alpha.17 + rrweb-snapshot: 2.0.0-alpha.17 + peerDependenciesMeta: + '@rrweb/types': + optional: true + rrweb-snapshot: + optional: true + + preact@10.27.2: + resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -2317,6 +2343,9 @@ packages: vscode-uri@3.0.8: resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -2642,6 +2671,8 @@ snapshots: '@orama/orama@3.1.7': {} + '@posthog/core@1.3.0': {} + '@radix-ui/number@1.1.1': {} '@radix-ui/primitive@1.1.2': {} @@ -3378,6 +3409,8 @@ snapshots: confbox@0.2.2: {} + core-js@3.46.0: {} + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -3702,6 +3735,8 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fflate@0.4.8: {} + fumadocs-core@15.5.1(@types/react@19.1.8)(next@15.3.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@formatjs/intl-localematcher': 0.6.1 @@ -4566,6 +4601,16 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + posthog-js@1.276.0: + dependencies: + '@posthog/core': 1.3.0 + core-js: 3.46.0 + fflate: 0.4.8 + preact: 10.27.2 + web-vitals: 4.2.4 + + preact@10.27.2: {} + prettier@3.6.2: {} property-information@7.1.0: {} @@ -4934,6 +4979,8 @@ snapshots: vscode-uri@3.0.8: {} + web-vitals@4.2.4: {} + yallist@5.0.0: {} zod@3.25.76: {} diff --git a/docs/src/app/(home)/[[...slug]]/page.tsx b/docs/src/app/(home)/[[...slug]]/page.tsx index a9a36b4f..20b84e3e 100644 --- a/docs/src/app/(home)/[[...slug]]/page.tsx +++ b/docs/src/app/(home)/[[...slug]]/page.tsx @@ -18,6 +18,7 @@ import { ChevronDown, CodeXml, ExternalLink } from 'lucide-react'; import type { Metadata } from 'next'; import Link from 'next/link'; import { notFound, redirect } from 'next/navigation'; +import { PageFeedback } from '@/components/page-feedback'; export default async function Page(props: { params: Promise<{ slug?: string[] }>; @@ -270,6 +271,7 @@ export default async function Page(props: { a: createRelativeLink(source, page), })} /> + ); diff --git a/docs/src/app/api/posthog/[...path]/route.ts b/docs/src/app/api/posthog/[...path]/route.ts new file mode 100644 index 00000000..24e2e751 --- /dev/null +++ b/docs/src/app/api/posthog/[...path]/route.ts @@ -0,0 +1,75 @@ +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ path: string[] }> } +) { + const { path } = await params; + const url = new URL(request.url); + + const targetUrl = `${process.env.NEXT_PUBLIC_POSTHOG_HOST}/${path.join('/')}${url.search}`; + + try { + const response = await fetch(targetUrl, { + method: 'GET', + headers: { + 'Content-Type': request.headers.get('Content-Type') || 'application/json', + }, + }); + + // Handle 204 No Content responses + if (response.status === 204) { + return new NextResponse(null, { status: 204 }); + } + + const data = await response.arrayBuffer(); + return new NextResponse(data, { + status: response.status, + headers: { + 'Content-Type': response.headers.get('Content-Type') || 'application/json', + }, + }); + } catch (error) { + console.error('PostHog proxy error:', error); + return new NextResponse('Error proxying request', { status: 500 }); + } +} + +export async function POST( + request: NextRequest, + { params }: { params: Promise<{ path: string[] }> } +) { + const { path } = await params; + const url = new URL(request.url); + + const targetUrl = `${process.env.NEXT_PUBLIC_POSTHOG_HOST}/${path.join('/')}${url.search}`; + + try { + const body = await request.arrayBuffer(); + const contentType = request.headers.get('Content-Type') || 'application/x-www-form-urlencoded'; + + const response = await fetch(targetUrl, { + method: 'POST', + headers: { + 'Content-Type': contentType, + }, + body, + }); + + // Handle 204 No Content responses + if (response.status === 204) { + return new NextResponse(null, { status: 204 }); + } + + const data = await response.arrayBuffer(); + return new NextResponse(data, { + status: response.status, + headers: { + 'Content-Type': response.headers.get('Content-Type') || 'application/json', + }, + }); + } catch (error) { + console.error('PostHog proxy error:', error); + return new NextResponse('Error proxying request', { status: 500 }); + } +} diff --git a/docs/src/app/layout.tsx b/docs/src/app/layout.tsx index 2fdc4fea..e821184e 100644 --- a/docs/src/app/layout.tsx +++ b/docs/src/app/layout.tsx @@ -2,6 +2,11 @@ import './global.css'; import { RootProvider } from 'fumadocs-ui/provider'; import { Inter } from 'next/font/google'; import type { ReactNode } from 'react'; +import { PHProvider, PostHogPageView } from '@/providers/posthog-provider'; +import { AnalyticsTracker } from '@/components/analytics-tracker'; +import { CookieConsent } from '@/components/cookie-consent'; +import { Footer } from '@/components/footer'; +import { Suspense } from 'react'; const inter = Inter({ subsets: ['latin'], @@ -14,9 +19,17 @@ export default function Layout({ children }: { children: ReactNode }) { - - {children} - + + + + + + + {children} + +