From 066f1288f100728e197a371d6101d80f4826f310 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 29 Mar 2026 00:28:47 +0100 Subject: [PATCH] feat: trim integration pages from 75 to 25 + migrate to MDX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add dedicatedPage flag to integration registry (25 true, 50 false) - Delete hardcoded nextjs/react/vue/wordpress route pages (wrong metadata) - Hub page routes non-dedicated integrations to /integrations/script-tag - Add 301 redirects for 50 removed slugs → /integrations/script-tag - Migrate guide content from TSX to MDX (content/integrations/*.mdx) - Add gray-matter, next-mdx-remote, remark-gfm dependencies - Add content loader (lib/integration-content.ts) matching ciphera-website pattern - Add prebuild script for integration guide index generation - Sitemap reduced from 83 to 35 URLs with real lastmod dates - Remove seoDescription from registry (now in MDX frontmatter) --- .gitignore | 3 + app/integrations/[slug]/page.tsx | 54 +- app/integrations/nextjs/page.tsx | 128 - app/integrations/page.tsx | 4 +- app/integrations/react/page.tsx | 118 - app/integrations/script-tag/page.tsx | 147 ++ app/integrations/vue/page.tsx | 112 - app/integrations/wordpress/page.tsx | 80 - app/sitemap.ts | 43 +- content/integrations/angular.mdx | 39 + content/integrations/astro.mdx | 48 + content/integrations/django.mdx | 42 + content/integrations/drupal.mdx | 45 + content/integrations/eleventy.mdx | 38 + content/integrations/express.mdx | 61 + content/integrations/flask.mdx | 40 + content/integrations/framer.mdx | 25 + content/integrations/gatsby.mdx | 52 + content/integrations/ghost.mdx | 27 + content/integrations/gtm.mdx | 39 + content/integrations/hugo.mdx | 44 + content/integrations/laravel.mdx | 41 + content/integrations/nextjs.mdx | 68 + content/integrations/nuxt.mdx | 49 + content/integrations/rails.mdx | 42 + content/integrations/react.mdx | 56 + content/integrations/remix.mdx | 50 + content/integrations/script-tag.mdx | 45 + content/integrations/shopify.mdx | 32 + content/integrations/squarespace.mdx | 27 + content/integrations/svelte.mdx | 53 + content/integrations/vue.mdx | 39 + content/integrations/webflow.mdx | 27 + content/integrations/wix.mdx | 27 + content/integrations/wordpress.mdx | 27 + lib/integration-content.ts | 63 + lib/integration-guides.tsx | 3606 -------------------------- lib/integrations.tsx | 229 +- next.config.ts | 19 + package-lock.json | 1010 +++++++- package.json | 5 + scripts/generate-integrations.ts | 44 + 42 files changed, 2515 insertions(+), 4233 deletions(-) delete mode 100644 app/integrations/nextjs/page.tsx delete mode 100644 app/integrations/react/page.tsx create mode 100644 app/integrations/script-tag/page.tsx delete mode 100644 app/integrations/vue/page.tsx delete mode 100644 app/integrations/wordpress/page.tsx create mode 100644 content/integrations/angular.mdx create mode 100644 content/integrations/astro.mdx create mode 100644 content/integrations/django.mdx create mode 100644 content/integrations/drupal.mdx create mode 100644 content/integrations/eleventy.mdx create mode 100644 content/integrations/express.mdx create mode 100644 content/integrations/flask.mdx create mode 100644 content/integrations/framer.mdx create mode 100644 content/integrations/gatsby.mdx create mode 100644 content/integrations/ghost.mdx create mode 100644 content/integrations/gtm.mdx create mode 100644 content/integrations/hugo.mdx create mode 100644 content/integrations/laravel.mdx create mode 100644 content/integrations/nextjs.mdx create mode 100644 content/integrations/nuxt.mdx create mode 100644 content/integrations/rails.mdx create mode 100644 content/integrations/react.mdx create mode 100644 content/integrations/remix.mdx create mode 100644 content/integrations/script-tag.mdx create mode 100644 content/integrations/shopify.mdx create mode 100644 content/integrations/squarespace.mdx create mode 100644 content/integrations/svelte.mdx create mode 100644 content/integrations/vue.mdx create mode 100644 content/integrations/webflow.mdx create mode 100644 content/integrations/wix.mdx create mode 100644 content/integrations/wordpress.mdx create mode 100644 lib/integration-content.ts delete mode 100644 lib/integration-guides.tsx create mode 100644 scripts/generate-integrations.ts diff --git a/.gitignore b/.gitignore index dc822ba..691a4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +# auto-generated +/lib/integration-guides.gen.ts + # dependencies /node_modules /.pnp diff --git a/app/integrations/[slug]/page.tsx b/app/integrations/[slug]/page.tsx index f9ce901..089b038 100644 --- a/app/integrations/[slug]/page.tsx +++ b/app/integrations/[slug]/page.tsx @@ -1,46 +1,56 @@ /** * @file Dynamic route for individual integration guide pages. * - * Handles all 50 integration routes via [slug]. + * Renders MDX content from content/integrations/*.mdx via next-mdx-remote. * Exports generateStaticParams for static generation and * generateMetadata for per-page SEO (title, description, OG, JSON-LD). */ import type { Metadata } from 'next' import { notFound } from 'next/navigation' +import { MDXRemote } from 'next-mdx-remote/rsc' +import remarkGfm from 'remark-gfm' +import { CodeBlock } from '@ciphera-net/ui' import { integrations, getIntegration } from '@/lib/integrations' -import { getGuideContent } from '@/lib/integration-guides' +import { getIntegrationGuide } from '@/lib/integration-content' import { IntegrationGuide } from '@/components/IntegrationGuide' -// * ─── Static Params ─────────────────────────────────────────────── -export function generateStaticParams() { - return integrations.map((i) => ({ slug: i.id })) +// * ─── MDX Components ──────────────────────────────────────────── +const mdxComponents = { + CodeBlock, } -// * ─── SEO Metadata ──────────────────────────────────────────────── +// * ─── Static Params ───────────────────────────────────────────── +export function generateStaticParams() { + return integrations + .filter((i) => i.dedicatedPage) + .map((i) => ({ slug: i.id })) +} + +// * ─── SEO Metadata ────────────────────────────────────────────── interface PageProps { params: Promise<{ slug: string }> } export async function generateMetadata({ params }: PageProps): Promise { const { slug } = await params - const integration = getIntegration(slug) - if (!integration) return {} + const guide = getIntegrationGuide(slug) + if (!guide) return {} - const title = `How to Add Pulse Analytics to ${integration.name} | Pulse by Ciphera` - const description = integration.seoDescription - const url = `https://pulse.ciphera.net/integrations/${integration.id}` + const title = `How to Add Pulse Analytics to ${guide.title} | Pulse by Ciphera` + const description = guide.description + const url = `https://pulse.ciphera.net/integrations/${guide.slug}` return { title, description, keywords: [ - `${integration.name} analytics`, - `${integration.name} Pulse`, + `${guide.title} analytics`, + `${guide.title} Pulse`, 'privacy-first analytics', 'website analytics', 'Ciphera Pulse', - integration.name, + guide.title, ], alternates: { canonical: url }, openGraph: { @@ -58,21 +68,19 @@ export async function generateMetadata({ params }: PageProps): Promise } } -// * ─── Page Component ────────────────────────────────────────────── +// * ─── Page Component ──────────────────────────────────────────── export default async function IntegrationPage({ params }: PageProps) { const { slug } = await params const integration = getIntegration(slug) - if (!integration) return notFound() - - const content = getGuideContent(slug) - if (!content) return notFound() + const guide = getIntegrationGuide(slug) + if (!integration || !guide) return notFound() // * HowTo JSON-LD for rich search snippets const jsonLd = { '@context': 'https://schema.org', '@type': 'HowTo', name: `How to Add Pulse Analytics to ${integration.name}`, - description: integration.seoDescription, + description: guide.description, step: [ { '@type': 'HowToStep', @@ -104,7 +112,11 @@ export default async function IntegrationPage({ params }: PageProps) { dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> - {content} + ) diff --git a/app/integrations/nextjs/page.tsx b/app/integrations/nextjs/page.tsx deleted file mode 100644 index 96ced7e..0000000 --- a/app/integrations/nextjs/page.tsx +++ /dev/null @@ -1,128 +0,0 @@ -'use client' - -import Link from 'next/link' -import { ArrowLeftIcon } from '@ciphera-net/ui' - -export default function NextJsIntegrationPage() { - return ( -
- {/* * --- ATMOSPHERE (Background) --- */} -
-
-
-
- -
- - - Back to Integrations - - -
-
- - - -
-

- Next.js Integration -

-
- -
-

- The best way to add Pulse to your Next.js application is using the built-in next/script component. -

- -
- -

Using App Router (Recommended)

-

- Add the script to your root layout file (usually app/layout.tsx or app/layout.js). -

- -
-
- app/layout.tsx -
-
-
-{`import Script from 'next/script'
-
-export default function RootLayout({
-  children,
-}: {
-  children: React.ReactNode
-}) {
-  return (
-    
-      
-        
-    
-    My React App
-  
-  
-    
- -`} -
-
-
- -

Method 2: Programmatic Injection

-

- If you need to load the script dynamically (e.g., only in production), you can use a useEffect hook in your main App component. -

- -
-
- src/App.tsx -
-
-
-{`import { useEffect } from 'react'
-
-function App() {
-  useEffect(() => {
-    // Only load in production
-    if (process.env.NODE_ENV === 'production') {
-      const script = document.createElement('script')
-      script.defer = true
-      script.setAttribute('data-domain', 'your-site.com')
-      script.src = 'https://pulse.ciphera.net/script.js'
-      document.head.appendChild(script)
-    }
-  }, [])
-
-  return (
-    
-

Hello World

-
- ) -}`} -
-
-
-
-
-
- ) -} diff --git a/app/integrations/script-tag/page.tsx b/app/integrations/script-tag/page.tsx new file mode 100644 index 0000000..8200b6c --- /dev/null +++ b/app/integrations/script-tag/page.tsx @@ -0,0 +1,147 @@ +import type { Metadata } from 'next' +import Link from 'next/link' +import { ArrowLeftIcon } from '@ciphera-net/ui' +import { CodeBlock } from '@ciphera-net/ui' + +export const metadata: Metadata = { + title: 'Add Pulse Analytics to Any Website | Pulse by Ciphera', + description: 'Add privacy-first analytics to any website with a single script tag. Works with any platform, CMS, or framework.', + alternates: { canonical: 'https://pulse.ciphera.net/integrations/script-tag' }, + openGraph: { + title: 'Add Pulse Analytics to Any Website | Pulse by Ciphera', + description: 'Add privacy-first analytics to any website with a single script tag.', + url: 'https://pulse.ciphera.net/integrations/script-tag', + siteName: 'Pulse by Ciphera', + type: 'article', + }, +} + +export default function ScriptTagPage() { + const jsonLd = { + '@context': 'https://schema.org', + '@type': 'HowTo', + name: 'How to Add Pulse Analytics to Any Website', + description: 'Add privacy-first analytics to any website with a single script tag.', + step: [ + { + '@type': 'HowToStep', + name: 'Copy the script tag', + text: 'Copy the Pulse tracking script with your domain.', + }, + { + '@type': 'HowToStep', + name: 'Paste into your HTML head', + text: 'Add the script tag inside the section of your website.', + }, + { + '@type': 'HowToStep', + name: 'Deploy and verify', + text: 'Deploy your site and check the Pulse dashboard for incoming data.', + }, + ], + tool: { + '@type': 'HowToTool', + name: 'Pulse by Ciphera', + url: 'https://pulse.ciphera.net', + }, + } + + return ( + <> + +`} + +

Configuration

+
    +
  • data-domain — your site's domain as shown in your Pulse dashboard (e.g. example.com), without https://
  • +
  • defer — loads the script without blocking page rendering
  • +
+ +

Where to paste the script

+

+ Most platforms have a “Custom Code”, “Code Injection”, or “Header Scripts” + section in their settings. Look for one of these: +

+
    +
  • Squarespace: Settings → Developer Tools → Code Injection → Header
  • +
  • Wix: Settings → Custom Code → Head
  • +
  • Webflow: Project Settings → Custom Code → Head Code
  • +
  • Ghost: Settings → Code Injection → Site Header
  • +
  • Any HTML site: Paste directly into your <head> tag
  • +
+ +

Verify installation

+

+ After deploying, visit your site and check the Pulse dashboard. You should + see your first page view within a few seconds. +

+ +
+

Optional: Frustration Tracking

+

+ Detect rage clicks and dead clicks by adding the frustration tracking + add-on after the core script: +

+ {``} +

+ No extra configuration needed. Add data-no-rage or{' '} + data-no-dead to disable individual signals. +

+
+
+ + + ) +} diff --git a/app/integrations/vue/page.tsx b/app/integrations/vue/page.tsx deleted file mode 100644 index 013f6b9..0000000 --- a/app/integrations/vue/page.tsx +++ /dev/null @@ -1,112 +0,0 @@ -'use client' - -import Link from 'next/link' -import { ArrowLeftIcon } from '@ciphera-net/ui' - -export default function VueIntegrationPage() { - return ( -
- {/* * --- ATMOSPHERE (Background) --- */} -
-
-
-
- -
- - - Back to Integrations - - -
-
- - - - -
-

- Vue.js Integration -

-
- -
-

- Integrating Pulse with Vue.js is straightforward. You can add the script to your index.html file. -

- -
- -

Method 1: index.html (Recommended)

-

- Add the script tag to the <head> section of your index.html file. This works for both Vue 2 and Vue 3 projects created with Vue CLI or Vite. -

- -
-
- index.html -
-
-
-{`
-
-  
-    
-    
-    
-    
-    
-    
-    My Vue App
-  
-  
-    
- - -`} -
-
-
- -

Method 2: Nuxt.js

-

- For Nuxt.js applications, you should add the script to your nuxt.config.js or nuxt.config.ts file. -

- -
-
- nuxt.config.ts -
-
-
-{`export default defineNuxtConfig({
-  app: {
-    head: {
-      script: [
-        {
-          src: 'https://pulse.ciphera.net/script.js',
-          defer: true,
-          'data-domain': 'your-site.com'
-        }
-      ]
-    }
-  }
-})`}
-              
-
-
-
-
-
- ) -} diff --git a/app/integrations/wordpress/page.tsx b/app/integrations/wordpress/page.tsx deleted file mode 100644 index 98f9f6f..0000000 --- a/app/integrations/wordpress/page.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client' - -import Link from 'next/link' -import { ArrowLeftIcon } from '@ciphera-net/ui' - -export default function WordPressIntegrationPage() { - return ( -
- {/* * --- ATMOSPHERE (Background) --- */} -
-
-
-
- -
- - - Back to Integrations - - -
-
- - - -
-

- WordPress Integration -

-
- -
-

- You can add Pulse to your WordPress site without installing any heavy plugins, or by using a simple code snippet plugin. -

- -
- -

Method 1: Using a Plugin (Easiest)

-
    -
  1. Install a plugin like "Insert Headers and Footers" (WPCode).
  2. -
  3. Go to the plugin settings and find the "Scripts in Header" section.
  4. -
  5. Paste the following code snippet:
  6. -
- -
-
- Header Script -
-
-
-{``}
-              
-
-
- -

Method 2: Edit Theme Files (Advanced)

-

- If you are comfortable editing your theme files, you can add the script directly to your header.php file. -

-
    -
  1. Go to Appearance > Theme File Editor.
  2. -
  3. Select header.php from the right sidebar.
  4. -
  5. Paste the script tag just before the closing </head> tag.
  6. -
-
-
-
- ) -} diff --git a/app/sitemap.ts b/app/sitemap.ts index d70f475..c6b0bf9 100644 --- a/app/sitemap.ts +++ b/app/sitemap.ts @@ -1,9 +1,13 @@ import type { MetadataRoute } from 'next' import { integrations } from '@/lib/integrations' +import { getIntegrationGuides } from '@/lib/integration-content' const BASE_URL = 'https://pulse.ciphera.net' export default function sitemap(): MetadataRoute.Sitemap { + const guides = getIntegrationGuides() + const guidesBySlug = new Map(guides.map((g) => [g.slug, g])) + const publicRoutes = [ { url: '', priority: 1.0, changeFrequency: 'weekly' as const }, { url: '/about', priority: 0.8, changeFrequency: 'monthly' as const }, @@ -13,20 +17,33 @@ export default function sitemap(): MetadataRoute.Sitemap { { url: '/changelog', priority: 0.6, changeFrequency: 'weekly' as const }, { url: '/installation', priority: 0.8, changeFrequency: 'monthly' as const }, { url: '/integrations', priority: 0.8, changeFrequency: 'monthly' as const }, + { url: '/integrations/script-tag', priority: 0.6, changeFrequency: 'monthly' as const }, ] - const integrationRoutes = integrations.map((i) => ({ - url: `/integrations/${i.id}`, - priority: 0.7, - changeFrequency: 'monthly' as const, - })) + const integrationRoutes = integrations + .filter((i) => i.dedicatedPage) + .map((i) => { + const guide = guidesBySlug.get(i.id) + return { + url: `/integrations/${i.id}`, + priority: 0.7, + changeFrequency: 'monthly' as const, + lastModified: guide?.date ? new Date(guide.date) : new Date('2026-03-28'), + } + }) - const allRoutes = [...publicRoutes, ...integrationRoutes] - - return allRoutes.map((route) => ({ - url: `${BASE_URL}${route.url}`, - lastModified: new Date(), - changeFrequency: route.changeFrequency, - priority: route.priority, - })) + return [ + ...publicRoutes.map((route) => ({ + url: `${BASE_URL}${route.url}`, + lastModified: new Date('2026-03-28'), + changeFrequency: route.changeFrequency, + priority: route.priority, + })), + ...integrationRoutes.map((route) => ({ + url: `${BASE_URL}${route.url}`, + lastModified: route.lastModified, + changeFrequency: route.changeFrequency, + priority: route.priority, + })), + ] } diff --git a/content/integrations/angular.mdx b/content/integrations/angular.mdx new file mode 100644 index 0000000..6531696 --- /dev/null +++ b/content/integrations/angular.mdx @@ -0,0 +1,39 @@ +--- +title: "Angular" +description: "Add Pulse analytics to your Angular application. Simple index.html setup for all Angular versions." +category: "framework" +brandColor: "#0F0F11" +officialUrl: "https://angular.dev" +relatedIds: ["react", "vue"] +date: "2026-03-28" +--- + +Add the script to your `src/index.html` — the single entry point for all Angular applications. + +--- + +## Add the Pulse script to index.html + +Place the Pulse script inside the `` tag of your Angular app's `src/index.html`. + +{` + + + + + + + + My Angular App + + + + + +`} + +For more details, see the [Angular docs](https://angular.dev/guide/components). diff --git a/content/integrations/astro.mdx b/content/integrations/astro.mdx new file mode 100644 index 0000000..974815f --- /dev/null +++ b/content/integrations/astro.mdx @@ -0,0 +1,48 @@ +--- +title: "Astro" +description: "Integrate Pulse analytics with Astro. Add the script to your base layout for all pages." +category: "framework" +brandColor: "#BC52EE" +officialUrl: "https://docs.astro.build" +relatedIds: ["svelte", "hugo", "eleventy"] +date: "2026-03-28" +--- + +Add the Pulse script to your base layout so it's included on every page of your Astro site. + +--- + +## Add to your base layout + +Place the Pulse script in the `` of your shared layout component. + +{`--- +interface Props { + title: string +} + +const { title } = Astro.props +--- + + + + + + + + + + {title} + + + + +`} + +If you're using Astro's View Transitions, the script will persist across navigations by default since it's in the ``. + +For more details, see the [Astro scripts docs](https://docs.astro.build/en/guides/client-side-scripts/). diff --git a/content/integrations/django.mdx b/content/integrations/django.mdx new file mode 100644 index 0000000..063008e --- /dev/null +++ b/content/integrations/django.mdx @@ -0,0 +1,42 @@ +--- +title: "Django" +description: "Add Pulse analytics to your Django app. Template-based integration for all Django versions." +category: "backend" +brandColor: "#092E20" +officialUrl: "https://docs.djangoproject.com" +relatedIds: ["flask", "laravel", "htmx"] +date: "2026-03-28" +--- + +Add the Pulse script to your base template with a debug guard. + +--- + +## Add to your base template + +Use Django's template tags to only load the script when `DEBUG` is `False`. + +{` + + + + + + {% if not debug %} + + {% endif %} + + {% block title %}My Django App{% endblock %} + + + {% block content %}{% endblock %} + +`} + +Make sure to pass `debug` to the template context via `settings.DEBUG`, or use a context processor to make it available globally. + +For more details, see the [Django template docs](https://docs.djangoproject.com/en/stable/ref/templates/builtins/). diff --git a/content/integrations/drupal.mdx b/content/integrations/drupal.mdx new file mode 100644 index 0000000..5661548 --- /dev/null +++ b/content/integrations/drupal.mdx @@ -0,0 +1,45 @@ +--- +title: "Drupal" +description: "Add Pulse analytics to your Drupal site using a module or theme template." +category: "cms" +brandColor: "#0678BE" +officialUrl: "https://www.drupal.org/docs" +relatedIds: ["wordpress", "joomla"] +date: "2026-03-28" +--- + +Add the Pulse script via a contributed module or by editing your theme's Twig template. + +--- + +## Method 1: Using Asset Injector module + +Install the [Asset Injector](https://www.drupal.org/project/asset_injector) module and create a new JS injector with the Pulse script. Set it to load on all pages in the header region. + +## Method 2: Edit html.html.twig + +Add the script directly to your theme's `html.html.twig` template in the head area. + +{` + + + + {{ head_title|safe_join(' | ') }} + + + + + + + {{ page_top }} + {{ page }} + {{ page_bottom }} + + +`} + +For more details, see the [Drupal theming docs](https://www.drupal.org/docs/theming-drupal). diff --git a/content/integrations/eleventy.mdx b/content/integrations/eleventy.mdx new file mode 100644 index 0000000..2ad4e5d --- /dev/null +++ b/content/integrations/eleventy.mdx @@ -0,0 +1,38 @@ +--- +title: "Eleventy" +description: "Add Pulse analytics to your Eleventy (11ty) site. Template-based integration." +category: "ssg" +brandColor: "#222222" +officialUrl: "https://www.11ty.dev/docs" +relatedIds: ["hugo", "jekyll", "astro"] +date: "2026-03-28" +--- + +Add the Pulse script to your base Nunjucks or Liquid layout. + +--- + +## Add to your base layout + +Place the Pulse script inside the `` of your shared base template. + +{` + + + + + + + + {{ title }} + + + {{ content | safe }} + +`} + +For more details, see the [Eleventy layouts docs](https://www.11ty.dev/docs/layouts/). diff --git a/content/integrations/express.mdx b/content/integrations/express.mdx new file mode 100644 index 0000000..5e7d9ed --- /dev/null +++ b/content/integrations/express.mdx @@ -0,0 +1,61 @@ +--- +title: "Express" +description: "Serve Pulse analytics from your Express.js app. Middleware or template-based setup." +category: "backend" +brandColor: "#000000" +officialUrl: "https://expressjs.com" +relatedIds: ["flask", "nextjs", "react"] +date: "2026-03-28" +--- + +Add the Pulse script to your template engine's layout (EJS, Pug, Handlebars) or serve it via static HTML. + +--- + +## Method 1: EJS template + +If you use EJS as your template engine, add the script to your layout with a production guard. + +{` + + + + + + <% if (process.env.NODE_ENV === 'production') { %> + + <% } %> + + <%= title %> + + + <%- body %> + +`} + +## Method 2: Static HTML + +If you serve static HTML files via Express, add the script directly. + +{` + + + + + + + + My Express App + + +

Hello World

+ +`}
diff --git a/content/integrations/flask.mdx b/content/integrations/flask.mdx new file mode 100644 index 0000000..140872a --- /dev/null +++ b/content/integrations/flask.mdx @@ -0,0 +1,40 @@ +--- +title: "Flask" +description: "Add Pulse analytics to your Flask app. Jinja2 template integration." +category: "backend" +brandColor: "#3BABC3" +officialUrl: "https://flask.palletsprojects.com" +relatedIds: ["django", "htmx", "express"] +date: "2026-03-28" +--- + +Add the Pulse script to your Jinja2 base template with a debug guard. + +--- + +## Add to your base template + +Use Jinja2's conditional to only load the script when `DEBUG` is off. + +{` + + + + + + {% if not config.DEBUG %} + + {% endif %} + + {% block title %}My Flask App{% endblock %} + + + {% block content %}{% endblock %} + +`} + +For more details, see the [Flask template docs](https://flask.palletsprojects.com/en/stable/patterns/templateinheritance/). diff --git a/content/integrations/framer.mdx b/content/integrations/framer.mdx new file mode 100644 index 0000000..aec9208 --- /dev/null +++ b/content/integrations/framer.mdx @@ -0,0 +1,25 @@ +--- +title: "Framer" +description: "Add Pulse analytics to your Framer site via custom code settings." +category: "platform" +brandColor: "#0055FF" +officialUrl: "https://www.framer.com/help" +relatedIds: ["webflow", "squarespace", "wix"] +date: "2026-03-28" +--- + +Add the Pulse script to your Framer project's custom code settings. + +--- + +## Add via Project Settings + +Go to **Project Settings -> General -> Custom Code -> Head** and paste the Pulse script. + +{``} + +For more details, see the [Framer custom code docs](https://www.framer.com/help/articles/custom-code/). diff --git a/content/integrations/gatsby.mdx b/content/integrations/gatsby.mdx new file mode 100644 index 0000000..2c83a7f --- /dev/null +++ b/content/integrations/gatsby.mdx @@ -0,0 +1,52 @@ +--- +title: "Gatsby" +description: "Add Pulse analytics to your Gatsby site using gatsby-ssr or the Gatsby Head API." +category: "ssg" +brandColor: "#663399" +officialUrl: "https://www.gatsbyjs.com/docs" +relatedIds: ["react", "nextjs", "hugo"] +date: "2026-03-28" +--- + +Use the Gatsby SSR API or the Gatsby Head API to add Pulse to your site. + +--- + +## Method 1: gatsby-ssr.js + +Use the `onRenderBody` hook to inject the Pulse script into every page's ``. + +{`import React from "react" + +export const onRenderBody = ({ setHeadComponents }) => { + setHeadComponents([ + `} + +Alternatively, you can add the script directly to your theme's `default.hbs` template file. + +For more details, see the [Ghost themes docs](https://ghost.org/docs/themes/). diff --git a/content/integrations/gtm.mdx b/content/integrations/gtm.mdx new file mode 100644 index 0000000..e3206c7 --- /dev/null +++ b/content/integrations/gtm.mdx @@ -0,0 +1,39 @@ +--- +title: "Google Tag Manager" +description: "Add Pulse analytics via Google Tag Manager. Works with any site using GTM." +category: "platform" +brandColor: "#246FDB" +officialUrl: "https://tagmanager.google.com" +relatedIds: ["wordpress", "shopify", "webflow"] +date: "2026-03-28" +--- + +Add Pulse via Google Tag Manager — works with any site that already has GTM installed. + +--- + +## Create a Custom HTML tag + +Follow these steps to add Pulse through GTM: + +1. Go to **Tags -> New -> Custom HTML** +2. Paste the snippet below +3. Set the trigger to **All Pages** +4. Publish your container + +{``} + +That's it. Pulse auto-detects the domain from the page, so no extra configuration is needed. + +
+Advanced: override domain or configure options + +If your site is registered under a different domain than the page hostname, or you need custom options (API endpoint, storage mode, etc.), use `pulseConfig`: + +{` +`} +
+ +For more details, see the [GTM Custom HTML tag docs](https://support.google.com/tagmanager/answer/6103696). diff --git a/content/integrations/hugo.mdx b/content/integrations/hugo.mdx new file mode 100644 index 0000000..8d0e4a5 --- /dev/null +++ b/content/integrations/hugo.mdx @@ -0,0 +1,44 @@ +--- +title: "Hugo" +description: "Add Pulse analytics to your Hugo site via a partial or base template." +category: "ssg" +brandColor: "#FF4088" +officialUrl: "https://gohugo.io/documentation" +relatedIds: ["jekyll", "eleventy", "astro"] +date: "2026-03-28" +--- + +Add the Pulse script via a Hugo partial or directly in your base template. + +--- + +## Method 1: Create a partial + +Create an analytics partial with a production guard using Hugo's `.Site.IsServer` flag. + +{`{{ if not .Site.IsServer }} + +{{ end }}`} + +## Method 2: Include the partial in your base layout + +Add the partial to your `baseof.html` layout. + +{` + + + + + {{ partial "analytics.html" . }} + {{ .Title }} + + + {{ block "main" . }}{{ end }} + +`} + +For more details, see the [Hugo partials docs](https://gohugo.io/templates/partials/). diff --git a/content/integrations/laravel.mdx b/content/integrations/laravel.mdx new file mode 100644 index 0000000..9364a2a --- /dev/null +++ b/content/integrations/laravel.mdx @@ -0,0 +1,41 @@ +--- +title: "Laravel" +description: "Add Pulse analytics to your Laravel app. Blade template integration for all Laravel versions." +category: "backend" +brandColor: "#FF2D20" +officialUrl: "https://laravel.com/docs" +relatedIds: ["django", "rails", "wordpress"] +date: "2026-03-28" +--- + +Add the Pulse script to your Blade layout template with a production guard. + +--- + +## Add to your Blade layout + +Use Laravel's `@production` directive to only load the script in production. + +{` + + + + + + @production + + @endproduction + + @yield('title') + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + @yield('content') + +`} + +For more details, see the [Laravel @production docs](https://laravel.com/docs/blade#the-production-directive). diff --git a/content/integrations/nextjs.mdx b/content/integrations/nextjs.mdx new file mode 100644 index 0000000..0f8db65 --- /dev/null +++ b/content/integrations/nextjs.mdx @@ -0,0 +1,68 @@ +--- +title: "Next.js" +description: "Step-by-step guide to adding Pulse privacy-first analytics to your Next.js app with next/script. Covers App Router and Pages Router." +category: "framework" +brandColor: "#000000" +officialUrl: "https://nextjs.org/docs" +relatedIds: ["react", "vercel", "nuxt"] +date: "2026-03-28" +--- + +The best way to add Pulse to your Next.js application is using the built-in `next/script` component. + +--- + +## Method 1: App Router + +Add the Pulse script to your root layout so it loads on every page. + +{`import Script from 'next/script' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + <% end %> + + <%= yield(:title) || "My Rails App" %> + <%= csrf_meta_tags %> + <%= stylesheet_link_tag "application" %> + + + <%= yield %> + +`} + +For more details, see the [Rails layout docs](https://guides.rubyonrails.org/layouts_and_rendering.html). diff --git a/content/integrations/react.mdx b/content/integrations/react.mdx new file mode 100644 index 0000000..fd8c566 --- /dev/null +++ b/content/integrations/react.mdx @@ -0,0 +1,56 @@ +--- +title: "React" +description: "Integrate Pulse analytics with any React SPA — Create React App, Vite, or custom setups. Two easy methods." +category: "framework" +brandColor: "#61DAFB" +officialUrl: "https://react.dev" +relatedIds: ["nextjs", "remix", "gatsby", "preact"] +date: "2026-03-28" +--- + +For standard React SPAs, add the script to your `index.html`. + +--- + +## Method 1: index.html (Recommended) + +The simplest approach is to add the Pulse script directly to your HTML entry point. + +{` + + + + + + + + My React App + + +
+ +`}
+ +## Method 2: Programmatic injection via useEffect + +If you prefer to inject the script programmatically (e.g. only in production), use a `useEffect` hook. + +{`import { useEffect } from 'react' + +function App() { + useEffect(() => { + if (process.env.NODE_ENV === 'production') { + const script = document.createElement('script') + script.defer = true + script.setAttribute('data-domain', 'your-site.com') + script.src = 'https://pulse.ciphera.net/script.js' + document.head.appendChild(script) + } + }, []) + + return

Hello World

+}`}
diff --git a/content/integrations/remix.mdx b/content/integrations/remix.mdx new file mode 100644 index 0000000..9a5f56c --- /dev/null +++ b/content/integrations/remix.mdx @@ -0,0 +1,50 @@ +--- +title: "Remix" +description: "Add Pulse analytics to your Remix application via the root route. Simple script tag in app/root.tsx." +category: "framework" +brandColor: "#000000" +officialUrl: "https://remix.run/docs" +relatedIds: ["react", "nextjs"] +date: "2026-03-28" +--- + +Add the Pulse script to your `app/root.tsx` so it's included on every route. + +--- + +## Add script to app/root.tsx + +The root route is the top-level layout in Remix. Add the Pulse script inside the `` section. + +{`import { + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from '@remix-run/react' + +export default function App() { + return ( + + + + + + + +`} + +## Configuration + +- `data-domain` — your site's domain as shown in your Pulse dashboard (e.g. `example.com`), without `https://` +- `defer` — loads the script without blocking page rendering + +## Where to paste the script + +Most platforms have a "Custom Code", "Code Injection", or "Header Scripts" section in their settings. Look for one of these: + +- **Squarespace:** Settings -> Developer Tools -> Code Injection -> Header +- **Wix:** Settings -> Custom Code -> Head +- **Webflow:** Project Settings -> Custom Code -> Head Code +- **Ghost:** Settings -> Code Injection -> Site Header +- **Any HTML site:** Paste directly into your `` tag + +## Verify installation + +After deploying, visit your site and check the Pulse dashboard. You should see your first page view within a few seconds. diff --git a/content/integrations/shopify.mdx b/content/integrations/shopify.mdx new file mode 100644 index 0000000..1e0defd --- /dev/null +++ b/content/integrations/shopify.mdx @@ -0,0 +1,32 @@ +--- +title: "Shopify" +description: "Add Pulse privacy-first analytics to your Shopify store via the theme editor." +category: "ecommerce" +brandColor: "#7AB55C" +officialUrl: "https://shopify.dev/docs" +relatedIds: ["woocommerce", "bigcommerce", "prestashop"] +date: "2026-03-28" +--- + +Add the Pulse script via the Shopify theme editor — no app needed. + +--- + +## Method 1: Edit theme code + +Go to **Online Store -> Themes -> Edit code** and open `layout/theme.liquid`. Add the Pulse script before the closing `` tag. + +{` +`} + +## Method 2: Shopify Plus — Customer Events + +If you're on Shopify Plus, you can add the Pulse script via **Settings -> Customer Events -> Custom Pixels**. + +Use your custom domain or `.myshopify.com` domain as the `data-domain` value. + +For more details, see the [Shopify theme docs](https://shopify.dev/docs/storefronts/themes/architecture/layouts). diff --git a/content/integrations/squarespace.mdx b/content/integrations/squarespace.mdx new file mode 100644 index 0000000..1229a86 --- /dev/null +++ b/content/integrations/squarespace.mdx @@ -0,0 +1,27 @@ +--- +title: "Squarespace" +description: "Add Pulse analytics to your Squarespace site via the Code Injection panel." +category: "platform" +brandColor: "#000000" +officialUrl: "https://support.squarespace.com" +relatedIds: ["webflow", "wix", "carrd"] +date: "2026-03-28" +--- + +Use Squarespace's Code Injection feature to add the Pulse script. + +--- + +## Add via Code Injection + +Go to **Settings -> Developer Tools -> Code Injection -> Header** and paste the Pulse script. + +{``} + +**Note:** Code Injection requires a Business or Commerce plan. + +For more details, see the [Squarespace code injection docs](https://support.squarespace.com/hc/en-us/articles/205815928). diff --git a/content/integrations/svelte.mdx b/content/integrations/svelte.mdx new file mode 100644 index 0000000..8f64611 --- /dev/null +++ b/content/integrations/svelte.mdx @@ -0,0 +1,53 @@ +--- +title: "Svelte" +description: "Add Pulse analytics to Svelte or SvelteKit. Simple setup for both Vite-based Svelte and SvelteKit applications." +category: "framework" +brandColor: "#FF3E00" +officialUrl: "https://svelte.dev" +relatedIds: ["astro", "vue"] +date: "2026-03-28" +--- + +Add the script to your `index.html` for Vite-based Svelte, or use `` in SvelteKit. + +--- + +## Method 1: Svelte (Vite) + +For standard Svelte projects using Vite, add the Pulse script to your `index.html`. + +{` + + + + + + + + My Svelte App + + +
+ + +`}
+ +## Method 2: SvelteKit + +In SvelteKit, use `` in your root layout to add the script to every page. + +{` + + + +`} + +Alternatively, you can add the script directly to `src/app.html` in your SvelteKit project. diff --git a/content/integrations/vue.mdx b/content/integrations/vue.mdx new file mode 100644 index 0000000..2d3df51 --- /dev/null +++ b/content/integrations/vue.mdx @@ -0,0 +1,39 @@ +--- +title: "Vue.js" +description: "Add Pulse privacy-first analytics to your Vue.js app. Works with Vue 2, Vue 3, Vue CLI, and Vite." +category: "framework" +brandColor: "#4FC08D" +officialUrl: "https://vuejs.org" +relatedIds: ["nuxt", "vitepress"] +date: "2026-03-28" +--- + +Add the script to your `index.html` — works for both Vue CLI and Vite-based projects. + +--- + +## Add the Pulse script to index.html + +Both Vue CLI and Vite use an `index.html` as the entry point. Simply add the Pulse script inside the `` tag. + +{` + + + + + + + + My Vue App + + +
+ + +`}
+ +Looking for Nuxt? Check the dedicated [Nuxt guide](/integrations/nuxt). diff --git a/content/integrations/webflow.mdx b/content/integrations/webflow.mdx new file mode 100644 index 0000000..91239bf --- /dev/null +++ b/content/integrations/webflow.mdx @@ -0,0 +1,27 @@ +--- +title: "Webflow" +description: "Add Pulse analytics to your Webflow site via project custom code settings." +category: "platform" +brandColor: "#146EF5" +officialUrl: "https://university.webflow.com" +relatedIds: ["squarespace", "wix", "framer"] +date: "2026-03-28" +--- + +Paste the Pulse script into your Webflow project's custom code settings. + +--- + +## Add via Project Settings + +Go to **Project Settings -> Custom Code -> Head Code** and paste the Pulse script. + +{``} + +**Note:** Custom code requires a paid Webflow site plan. The script won't appear in the Designer preview — publish your site to see it in action. + +For more details, see the [Webflow custom code docs](https://university.webflow.com/lesson/custom-code-in-the-head-and-body-tag). diff --git a/content/integrations/wix.mdx b/content/integrations/wix.mdx new file mode 100644 index 0000000..1ab7977 --- /dev/null +++ b/content/integrations/wix.mdx @@ -0,0 +1,27 @@ +--- +title: "Wix" +description: "Add Pulse analytics to your Wix site via Custom Code settings." +category: "platform" +brandColor: "#0C6EFC" +officialUrl: "https://support.wix.com" +relatedIds: ["webflow", "squarespace", "framer"] +date: "2026-03-28" +--- + +Use Wix's Custom Code settings to add the Pulse script. + +--- + +## Add via Custom Code + +Go to **Settings -> Custom Code -> Add Custom Code**. Set the placement to **Head** and apply it to **All pages**. + +{``} + +**Note:** Custom Code requires a Wix Premium plan. + +For more details, see the [Wix custom code docs](https://support.wix.com/en/article/embedding-custom-code-on-your-site). diff --git a/content/integrations/wordpress.mdx b/content/integrations/wordpress.mdx new file mode 100644 index 0000000..1223536 --- /dev/null +++ b/content/integrations/wordpress.mdx @@ -0,0 +1,27 @@ +--- +title: "WordPress" +description: "Add Pulse analytics to your WordPress site via a plugin or theme header code." +category: "cms" +brandColor: "#21759B" +officialUrl: "https://wordpress.org/documentation" +relatedIds: ["ghost", "drupal", "woocommerce"] +date: "2026-03-28" +--- + +Add the Pulse script via a plugin or by editing your theme's header file directly. + +--- + +## Method 1: Using a plugin (Recommended) + +The easiest way is to use the [WPCode (Insert Headers and Footers)](https://wordpress.org/plugins/insert-headers-and-footers/) plugin. Install it, then go to **Code Snippets -> Header & Footer** and paste the Pulse script into the Header section. + +{``} + +## Method 2: Edit header.php directly + +Go to **Appearance -> Theme File Editor** and edit `header.php`. Add the Pulse script before the closing `` tag. diff --git a/lib/integration-content.ts b/lib/integration-content.ts new file mode 100644 index 0000000..12940fe --- /dev/null +++ b/lib/integration-content.ts @@ -0,0 +1,63 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +const CONTENT_DIR = path.join(process.cwd(), 'content', 'integrations') + +export interface IntegrationGuideMeta { + slug: string + title: string + description: string + category: string + brandColor: string + officialUrl: string + relatedIds: string[] + date: string +} + +export interface IntegrationGuideArticle extends IntegrationGuideMeta { + content: string +} + +export function getIntegrationGuides(): IntegrationGuideMeta[] { + if (!fs.existsSync(CONTENT_DIR)) return [] + + const files = fs.readdirSync(CONTENT_DIR).filter((f) => f.endsWith('.mdx')) + + return files.map((filename) => { + const slug = filename.replace(/\.mdx$/, '') + const raw = fs.readFileSync(path.join(CONTENT_DIR, filename), 'utf-8') + const { data } = matter(raw) + + return { + slug, + title: data.title, + description: data.description, + category: data.category, + brandColor: data.brandColor, + officialUrl: data.officialUrl, + relatedIds: data.relatedIds || [], + date: data.date, + } + }) +} + +export function getIntegrationGuide(slug: string): IntegrationGuideArticle | null { + const filePath = path.join(CONTENT_DIR, `${slug}.mdx`) + if (!fs.existsSync(filePath)) return null + + const raw = fs.readFileSync(filePath, 'utf-8') + const { data, content } = matter(raw) + + return { + slug, + title: data.title, + description: data.description, + category: data.category, + brandColor: data.brandColor, + officialUrl: data.officialUrl, + relatedIds: data.relatedIds || [], + date: data.date, + content, + } +} diff --git a/lib/integration-guides.tsx b/lib/integration-guides.tsx deleted file mode 100644 index d72993d..0000000 --- a/lib/integration-guides.tsx +++ /dev/null @@ -1,3606 +0,0 @@ -/** - * @file Integration guide content — full JSX guide for every supported platform. - * - * Each guide is keyed by the integration slug and rendered on the - * `/integrations/[slug]` page. - * - * * 75 guides across 7 categories. - */ - -import { type ReactNode } from 'react' -import { CodeBlock } from '@ciphera-net/ui' - -// * ─── Guide registry ───────────────────────────────────────────────────────── - -const guides: Record = { - /* ──────────────────────────────────────────────────────────────────────────── - * 1. Next.js - * ──────────────────────────────────────────────────────────────────────────── */ - 'nextjs': ( - <> -

- The best way to add Pulse to your Next.js application is using the - built-in next/script component. -

- -
- -

Method 1: App Router

-

- Add the Pulse script to your root layout so it loads on every page. -

- {`import Script from 'next/script' - -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { - return ( - - - - - My React App - - -
- -`}
- -

Method 2: Programmatic injection via useEffect

-

- If you prefer to inject the script programmatically (e.g. only in - production), use a useEffect hook. -

- {`import { useEffect } from 'react' - -function App() { - useEffect(() => { - if (process.env.NODE_ENV === 'production') { - const script = document.createElement('script') - script.defer = true - script.setAttribute('data-domain', 'your-site.com') - script.src = 'https://pulse.ciphera.net/script.js' - document.head.appendChild(script) - } - }, []) - - return

Hello World

-}`}
- -

- Related Integrations:{' '} - Next.js,{' '} - Remix,{' '} - Gatsby,{' '} - Preact -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 3. Vue.js - * ──────────────────────────────────────────────────────────────────────────── */ - 'vue': ( - <> -

- Add the script to your index.html — works for both Vue CLI - and Vite-based projects. -

- -
- -

Add the Pulse script to index.html

-

- Both Vue CLI and Vite use an index.html as the entry point. - Simply add the Pulse script inside the <head> tag. -

- {` - - - - - - - - My Vue App - - -
- - -`}
- -

- Looking for Nuxt? Check the dedicated{' '} - Nuxt guide. -

- -

- Related Integrations:{' '} - Nuxt,{' '} - VitePress -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 4. Angular - * ──────────────────────────────────────────────────────────────────────────── */ - 'angular': ( - <> -

- Add the script to your src/index.html — the single entry - point for all Angular applications. -

- -
- -

Add the Pulse script to index.html

-

- Place the Pulse script inside the <head> tag of your - Angular app's src/index.html. -

- {` - - - - - - - - My Angular App - - - - - -`} - -

- For more details, see the{' '} - - Angular docs - . -

- -

- Related Integrations:{' '} - React,{' '} - Vue.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 5. Svelte - * ──────────────────────────────────────────────────────────────────────────── */ - 'svelte': ( - <> -

- Add the script to your index.html for Vite-based Svelte, or - use <svelte:head> in SvelteKit. -

- -
- -

Method 1: Svelte (Vite)

-

- For standard Svelte projects using Vite, add the Pulse script to your{' '} - index.html. -

- {` - - - - - - - - My Svelte App - - -
- - -`}
- -

Method 2: SvelteKit

-

- In SvelteKit, use <svelte:head> in your root layout - to add the script to every page. -

- {` - - - -`} - -

- Alternatively, you can add the script directly to{' '} - src/app.html in your SvelteKit project. -

- -

- Related Integrations:{' '} - Astro,{' '} - Vue.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 6. Nuxt - * ──────────────────────────────────────────────────────────────────────────── */ - 'nuxt': ( - <> -

- Configure Pulse analytics in your nuxt.config for a - framework-native setup. -

- -
- -

Method 1: Nuxt 3

-

- Add the Pulse script via the app.head option in your Nuxt 3 - config. -

- {`export default defineNuxtConfig({ - app: { - head: { - script: [ - { - defer: true, - 'data-domain': 'your-site.com', - src: 'https://pulse.ciphera.net/script.js', - }, - ], - }, - }, -})`} - -

Method 2: Nuxt 2

-

- In Nuxt 2, use the head property in your config. -

- {`export default { - head: { - script: [ - { - defer: true, - 'data-domain': 'your-site.com', - src: 'https://pulse.ciphera.net/script.js', - }, - ], - }, -}`} - -

- For more details, see the{' '} - - Nuxt head config docs - . -

- -

- Related Integrations:{' '} - Vue.js,{' '} - Next.js,{' '} - VitePress -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 7. Remix - * ──────────────────────────────────────────────────────────────────────────── */ - 'remix': ( - <> -

- Add the Pulse script to your app/root.tsx so it's - included on every route. -

- -
- -

Add script to app/root.tsx

-

- The root route is the top-level layout in Remix. Add the Pulse script - inside the <head> section. -

- {`import { - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, -} from '@remix-run/react' - -export default function App() { - return ( - - - - - - - - - {title} - - - - -`} - -

- If you're using Astro's View Transitions, the script will - persist across navigations by default since it's in the{' '} - <head>. -

- -

- For more details, see the{' '} - - Astro scripts docs - . -

- -

- Related Integrations:{' '} - Svelte,{' '} - Hugo,{' '} - Eleventy -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 9. Solid.js - * ──────────────────────────────────────────────────────────────────────────── */ - 'solidjs': ( - <> -

- Add the Pulse script to your index.html like any Vite-based - project. -

- -
- -

Add the Pulse script to index.html

-

- Solid.js uses Vite under the hood, so simply add the Pulse script to - your HTML entry file. -

- {` - - - - - - - - My Solid App - - -
- - -`}
- -

- Related Integrations:{' '} - React,{' '} - Qwik,{' '} - Preact -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 10. Qwik - * ──────────────────────────────────────────────────────────────────────────── */ - 'qwik': ( - <> -

- Add the Pulse script to your root entry component so it loads on every - page. -

- -
- -

Add to src/root.tsx

-

- In Qwik, add the Pulse script to the <head> section - of your root component. -

- {`import { component$ } from '@builder.io/qwik' -import { QwikCityProvider, RouterOutlet } from '@builder.io/qwik-city' - -export default component$(() => { - return ( - - - - - - - My Preact App - - -
- - -`}
- -

- Related Integrations:{' '} - React,{' '} - Solid.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 12. HTMX - * ──────────────────────────────────────────────────────────────────────────── */ - 'htmx': ( - <> -

- Since HTMX is used with server-rendered HTML, add the Pulse script to - your server's base template. -

- -
- -

Add to your base HTML template

-

- HTMX works with any backend — Django, Flask, Laravel, Rails, Express, - and more. Add the Pulse script to whichever base template your server - renders. -

- {` - - - - - - - - - My HTMX App - - - - -`} - -

- See the backend-specific guides for template syntax details:{' '} - Django,{' '} - Flask,{' '} - Laravel,{' '} - Rails,{' '} - Express. -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 13. Ember.js - * ──────────────────────────────────────────────────────────────────────────── */ - 'ember': ( - <> -

- Add the Pulse script to your app/index.html. -

- -
- -

Add the Pulse script to index.html

-

- Ember uses app/index.html as its entry point. Add the - script inside the <head> tag. -

- {` - - - - - - - - My Ember App - - {{content-for "head"}} - - - - {{content-for "body"}} - - - -`} - -

- For more details, see the{' '} - - Ember docs - . -

- -

- Related Integrations:{' '} - React,{' '} - Angular -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 14. Laravel - * ──────────────────────────────────────────────────────────────────────────── */ - 'laravel': ( - <> -

- Add the Pulse script to your Blade layout template with a production - guard. -

- -
- -

Add to your Blade layout

-

- Use Laravel's @production directive to only load the - script in production. -

- {` - - - - - - @production - - @endproduction - - @yield('title') - @vite(['resources/css/app.css', 'resources/js/app.js']) - - - @yield('content') - -`} - -

- For more details, see the{' '} - - Laravel @production docs - . -

- -

- Related Integrations:{' '} - Django,{' '} - Rails,{' '} - WordPress -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 15. Django - * ──────────────────────────────────────────────────────────────────────────── */ - 'django': ( - <> -

- Add the Pulse script to your base template with a debug guard. -

- -
- -

Add to your base template

-

- Use Django's template tags to only load the script when{' '} - DEBUG is False. -

- {` - - - - - - {% if not debug %} - - {% endif %} - - {% block title %}My Django App{% endblock %} - - - {% block content %}{% endblock %} - -`} - -

- Make sure to pass debug to the template context via{' '} - settings.DEBUG, or use a context processor to make it - available globally. -

- -

- For more details, see the{' '} - - Django template docs - . -

- -

- Related Integrations:{' '} - Flask,{' '} - Laravel,{' '} - HTMX -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 16. Ruby on Rails - * ──────────────────────────────────────────────────────────────────────────── */ - 'rails': ( - <> -

- Add the Pulse script to your application layout with a production - environment guard. -

- -
- -

Add to your application layout

-

- Use an if guard to only load the script in production. -

- {` - - - - - - <% if Rails.env.production? %> - - <% end %> - - <%= yield(:title) || "My Rails App" %> - <%= csrf_meta_tags %> - <%= stylesheet_link_tag "application" %> - - - <%= yield %> - -`} - -

- For more details, see the{' '} - - Rails layout docs - . -

- -

- Related Integrations:{' '} - Laravel,{' '} - Django,{' '} - Jekyll -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 17. Flask - * ──────────────────────────────────────────────────────────────────────────── */ - 'flask': ( - <> -

- Add the Pulse script to your Jinja2 base template with a debug guard. -

- -
- -

Add to your base template

-

- Use Jinja2's conditional to only load the script when{' '} - DEBUG is off. -

- {` - - - - - - {% if not config.DEBUG %} - - {% endif %} - - {% block title %}My Flask App{% endblock %} - - - {% block content %}{% endblock %} - -`} - -

- For more details, see the{' '} - - Flask template docs - . -

- -

- Related Integrations:{' '} - Django,{' '} - HTMX,{' '} - Express -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 18. Express - * ──────────────────────────────────────────────────────────────────────────── */ - 'express': ( - <> -

- Add the Pulse script to your template engine's layout (EJS, Pug, - Handlebars) or serve it via static HTML. -

- -
- -

Method 1: EJS template

-

- If you use EJS as your template engine, add the script to your layout - with a production guard. -

- {` - - - - - - <% if (process.env.NODE_ENV === 'production') { %> - - <% } %> - - <%= title %> - - - <%- body %> - -`} - -

Method 2: Static HTML

-

- If you serve static HTML files via Express, add the script directly. -

- {` - - - - - - - - My Express App - - -

Hello World

- -`}
- -

- Related Integrations:{' '} - Flask,{' '} - Next.js,{' '} - React -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 19. Gatsby - * ──────────────────────────────────────────────────────────────────────────── */ - 'gatsby': ( - <> -

- Use the Gatsby SSR API or the Gatsby Head API to add Pulse to your site. -

- -
- -

Method 1: gatsby-ssr.js

-

- Use the onRenderBody hook to inject the Pulse script into - every page's <head>. -

- {`import React from "react" - -export const onRenderBody = ({ setHeadComponents }) => { - setHeadComponents([ - -{{ end }}`} - -

Method 2: Include the partial in your base layout

-

- Add the partial to your baseof.html layout. -

- {` - - - - - {{ partial "analytics.html" . }} - {{ .Title }} - - - {{ block "main" . }}{{ end }} - -`} - -

- For more details, see the{' '} - - Hugo partials docs - . -

- -

- Related Integrations:{' '} - Jekyll,{' '} - Eleventy,{' '} - Astro -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 21. Eleventy - * ──────────────────────────────────────────────────────────────────────────── */ - 'eleventy': ( - <> -

- Add the Pulse script to your base Nunjucks or Liquid layout. -

- -
- -

Add to your base layout

-

- Place the Pulse script inside the <head> of your - shared base template. -

- {` - - - - - - - - {{ title }} - - - {{ content | safe }} - -`} - -

- For more details, see the{' '} - - Eleventy layouts docs - . -

- -

- Related Integrations:{' '} - Hugo,{' '} - Jekyll,{' '} - Astro -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 22. Jekyll - * ──────────────────────────────────────────────────────────────────────────── */ - 'jekyll': ( - <> -

- Add the Pulse script to your default layout or an _includes{' '} - partial. -

- -
- -

Method 1: Create an analytics include

-

- Create a reusable include with a production environment guard. -

- {`{% if jekyll.environment == "production" %} - -{% endif %}`} - -

Method 2: Include in your default layout

-

- Reference the include in your default layout's{' '} - <head>. -

- {` - - - - - {% include analytics.html %} - {{ page.title }} - - - {{ content }} - -`} - -

- For more details, see the{' '} - - Jekyll includes docs - . -

- -

- Related Integrations:{' '} - Hugo,{' '} - Eleventy,{' '} - GitHub Pages -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 23. Docusaurus - * ──────────────────────────────────────────────────────────────────────────── */ - 'docusaurus': ( - <> -

- Add the Pulse script via the scripts array in your - Docusaurus config. -

- -
- -

Configure in docusaurus.config.js

-

- Docusaurus supports a scripts config option that injects - script tags into every page. -

- {`module.exports = { - scripts: [ - { - src: 'https://pulse.ciphera.net/script.js', - defer: true, - 'data-domain': 'your-site.com', - }, - ], - // ... rest of config -}`} - -

- For more details, see the{' '} - - Docusaurus scripts config docs - . -

- -

- Related Integrations:{' '} - VitePress,{' '} - MkDocs,{' '} - Gatsby -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 24. VitePress - * ──────────────────────────────────────────────────────────────────────────── */ - 'vitepress': ( - <> -

- Add the Pulse script via VitePress's head config - option. -

- -
- -

Configure in .vitepress/config.ts

-

- VitePress lets you inject tags into the <head> of - every page via the head array. -

- {`import { defineConfig } from 'vitepress' - -export default defineConfig({ - head: [ - [ - 'script', - { - defer: '', - 'data-domain': 'your-site.com', - src: 'https://pulse.ciphera.net/script.js', - }, - ], - ], -})`} - -

- For more details, see the{' '} - - VitePress head config docs - . -

- -

- Related Integrations:{' '} - Docusaurus,{' '} - Vue.js,{' '} - Nuxt -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 25. Hexo - * ──────────────────────────────────────────────────────────────────────────── */ - 'hexo': ( - <> -

- Add the Pulse script to your Hexo theme's layout file. -

- -
- -

Edit your theme's layout

-

- Open the layout file for your active theme and add the Pulse script - inside the <head>. -

- {` - - - - - - - - <%= config.title %> - <%- css('css/style') %> - - - <%- body %> - <%- js('js/script') %> - -`} - -

- Alternatively, you can use Hexo's after_render filter - to inject the script programmatically. -

- -

- For more details, see the{' '} - - Hexo templates docs - . -

- -

- Related Integrations:{' '} - Hugo,{' '} - Jekyll,{' '} - Eleventy -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 26. MkDocs - * ──────────────────────────────────────────────────────────────────────────── */ - 'mkdocs': ( - <> -

- Add the Pulse script to your MkDocs site using a custom template - override. -

- -
- -

Step 1: Create a custom template override

-

- To include the data-domain attribute, create a template - override file. -

- {`{% extends "base.html" %} - -{% block extrahead %} - -{% endblock %}`} - -

Step 2: Configure mkdocs.yml

-

- Set the custom_dir to your overrides folder in your - MkDocs configuration. -

- {`theme: - name: material - custom_dir: overrides`} - -

- For more details, see the{' '} - - MkDocs customization docs - . -

- -

- Related Integrations:{' '} - Docusaurus,{' '} - VitePress,{' '} - Django -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 27. WordPress - * ──────────────────────────────────────────────────────────────────────────── */ - 'wordpress': ( - <> -

- Add the Pulse script via a plugin or by editing your theme's header - file directly. -

- -
- -

Method 1: Using a plugin (Recommended)

-

- The easiest way is to use the{' '} - - WPCode (Insert Headers and Footers) - {' '} - plugin. Install it, then go to Code Snippets → Header & Footer{' '} - and paste the Pulse script into the Header section. -

- {``} - -

Method 2: Edit header.php directly

-

- Go to Appearance → Theme File Editor and edit{' '} - header.php. Add the Pulse script before the closing{' '} - </head> tag. -

- -

- Related Integrations:{' '} - Ghost,{' '} - Drupal,{' '} - WooCommerce -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 28. Ghost - * ──────────────────────────────────────────────────────────────────────────── */ - 'ghost': ( - <> -

- Use Ghost's built-in Code Injection feature to add the Pulse - script — no theme editing required. -

- -
- -

Add via Code Injection

-

- Go to Settings → Code injection → Site Header{' '} - and paste the Pulse script. -

- {``} - -

- Alternatively, you can add the script directly to your theme's{' '} - default.hbs template file. -

- -

- For more details, see the{' '} - - Ghost themes docs - . -

- -

- Related Integrations:{' '} - WordPress,{' '} - Blogger -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 29. Drupal - * ──────────────────────────────────────────────────────────────────────────── */ - 'drupal': ( - <> -

- Add the Pulse script via a contributed module or by editing your - theme's Twig template. -

- -
- -

Method 1: Using Asset Injector module

-

- Install the{' '} - - Asset Injector - {' '} - module and create a new JS injector with the Pulse script. Set it to - load on all pages in the header region. -

- -

Method 2: Edit html.html.twig

-

- Add the script directly to your theme's{' '} - html.html.twig template in the head area. -

- {` - - - - {{ head_title|safe_join(' | ') }} - - - - - - - {{ page_top }} - {{ page }} - {{ page_bottom }} - - -`} - -

- For more details, see the{' '} - - Drupal theming docs - . -

- -

- Related Integrations:{' '} - WordPress,{' '} - Joomla -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 30. Joomla - * ──────────────────────────────────────────────────────────────────────────── */ - 'joomla': ( - <> -

- Add the Pulse script via your Joomla template or a custom HTML module. -

- -
- -

Method 1: Edit template index.php

-

- Go to Extensions → Templates → Your Template{' '} - and edit index.php. Add the Pulse script before the - closing </head> tag. -

- {` - - - - - - - - - - -`} - -

Method 2: Custom HTML module

-

- Create a “Custom HTML” module and assign it to the head - position of your template. Paste the Pulse script as the content. -

- -

- For more details, see the{' '} - - Joomla template docs - . -

- -

- Related Integrations:{' '} - WordPress,{' '} - Drupal -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 31. Strapi - * ──────────────────────────────────────────────────────────────────────────── */ - 'strapi': ( - <> -

- Strapi is a headless CMS — add Pulse to the frontend{' '} - that consumes your Strapi API, not to Strapi itself. -

- -
- -

Where to add the script

-

- Since Strapi is an API-only backend, the analytics script belongs in - your frontend application. Follow the guide for whichever framework - you're using to render your Strapi content: -

- - -

- Related Integrations:{' '} - Contentful,{' '} - Sanity,{' '} - Next.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 32. Sanity - * ──────────────────────────────────────────────────────────────────────────── */ - 'sanity': ( - <> -

- Sanity is a headless CMS — add Pulse to the frontend{' '} - that renders your Sanity content, not to Sanity Studio. -

- -
- -

Where to add the script

-

- Since Sanity is a headless content platform, the analytics script - belongs in your frontend application. Follow the guide for whichever - framework you're using: -

- - -

- Related Integrations:{' '} - Strapi,{' '} - Contentful,{' '} - Next.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 33. Contentful - * ──────────────────────────────────────────────────────────────────────────── */ - 'contentful': ( - <> -

- Contentful is a headless CMS — add Pulse to the{' '} - frontend that displays your Contentful content. -

- -
- -

Where to add the script

-

- Since Contentful is an API-first content platform, the analytics script - belongs in your frontend application. Follow the guide for your - framework: -

- - -

- Related Integrations:{' '} - Strapi,{' '} - Sanity,{' '} - Next.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 34. Payload CMS - * ──────────────────────────────────────────────────────────────────────────── */ - 'payload': ( - <> -

- Payload CMS is a headless CMS — add Pulse to the{' '} - frontend application that renders your content. -

- -
- -

Where to add the script

-

- Since Payload is a headless CMS, the analytics script belongs in your - frontend. Payload is most commonly used with Next.js, so the{' '} - Next.js guide{' '} - is the best starting point. -

- - -

- Related Integrations:{' '} - Strapi,{' '} - Contentful,{' '} - Next.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 35. Shopify - * ──────────────────────────────────────────────────────────────────────────── */ - 'shopify': ( - <> -

- Add the Pulse script via the Shopify theme editor — no app needed. -

- -
- -

Method 1: Edit theme code

-

- Go to Online Store → Themes → Edit code and - open layout/theme.liquid. Add the Pulse script before the - closing </head> tag. -

- {` -`} - -

Method 2: Shopify Plus — Customer Events

-

- If you're on Shopify Plus, you can add the Pulse script via{' '} - Settings → Customer Events → Custom Pixels. -

- -

- Use your custom domain or .myshopify.com domain as the{' '} - data-domain value. -

- -

- For more details, see the{' '} - - Shopify theme docs - . -

- -

- Related Integrations:{' '} - WooCommerce,{' '} - BigCommerce,{' '} - PrestaShop -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 36. WooCommerce - * ──────────────────────────────────────────────────────────────────────────── */ - 'woocommerce': ( - <> -

- WooCommerce runs on WordPress. Use WordPress methods to add the Pulse - script. -

- -
- -

Method 1: Using a plugin (Recommended)

-

- Install the{' '} - - WPCode plugin - {' '} - and paste the Pulse script into the Header section. -

- {``} - -

Method 2: Edit header.php

-

- Go to Appearance → Theme File Editor and add the - Pulse script to your theme's header.php before{' '} - </head>. -

- -

- Use the same domain you track your main WordPress site with. -

- -

- For the full WordPress setup, see the{' '} - WordPress guide. -

- -

- Related Integrations:{' '} - Shopify,{' '} - WordPress,{' '} - BigCommerce -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 37. BigCommerce - * ──────────────────────────────────────────────────────────────────────────── */ - 'bigcommerce': ( - <> -

- Add the Pulse script via BigCommerce's Script Manager. -

- -
- -

Add via Script Manager

-

- Go to Storefront → Script Manager → Create a Script{' '} - and configure it as follows: -

-
    -
  • Placement: Head
  • -
  • Location: All Pages
  • -
  • Script type: Script tag
  • -
- {``} - -

- For more details, see the{' '} - - BigCommerce Script Manager docs - . -

- -

- Related Integrations:{' '} - Shopify,{' '} - WooCommerce,{' '} - PrestaShop -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 38. PrestaShop - * ──────────────────────────────────────────────────────────────────────────── */ - 'prestashop': ( - <> -

- Add the Pulse script to your PrestaShop theme template. -

- -
- -

Edit your theme's head template

-

- Open the head partial for your active theme and add the Pulse script. -

- {`{block name='head_seo'} - {$page.meta.title} - -{/block} - - - -{block name='head_stylesheets'} - {include file='_partials/stylesheets.tpl'} -{/block}`} - -

- For more details, see the{' '} - - PrestaShop theme docs - . -

- -

- Related Integrations:{' '} - Shopify,{' '} - WooCommerce,{' '} - BigCommerce -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 39. Webflow - * ──────────────────────────────────────────────────────────────────────────── */ - 'webflow': ( - <> -

- Paste the Pulse script into your Webflow project's custom code - settings. -

- -
- -

Add via Project Settings

-

- Go to Project Settings → Custom Code → Head Code{' '} - and paste the Pulse script. -

- {``} - -

- Note: Custom code requires a paid Webflow site plan. The - script won't appear in the Designer preview — publish your site to - see it in action. -

- -

- For more details, see the{' '} - - Webflow custom code docs - . -

- -

- Related Integrations:{' '} - Squarespace,{' '} - Wix,{' '} - Framer -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 40. Squarespace - * ──────────────────────────────────────────────────────────────────────────── */ - 'squarespace': ( - <> -

- Use Squarespace's Code Injection feature to add the Pulse script. -

- -
- -

Add via Code Injection

-

- Go to Settings → Developer Tools → Code Injection → Header{' '} - and paste the Pulse script. -

- {``} - -

- Note: Code Injection requires a Business or Commerce - plan. -

- -

- For more details, see the{' '} - - Squarespace code injection docs - . -

- -

- Related Integrations:{' '} - Webflow,{' '} - Wix,{' '} - Carrd -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 41. Wix - * ──────────────────────────────────────────────────────────────────────────── */ - 'wix': ( - <> -

- Use Wix's Custom Code settings to add the Pulse script. -

- -
- -

Add via Custom Code

-

- Go to Settings → Custom Code → Add Custom Code. - Set the placement to Head and apply it to{' '} - All pages. -

- {``} - -

- Note: Custom Code requires a Wix Premium plan. -

- -

- For more details, see the{' '} - - Wix custom code docs - . -

- -

- Related Integrations:{' '} - Webflow,{' '} - Squarespace,{' '} - Framer -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 42. Framer - * ──────────────────────────────────────────────────────────────────────────── */ - 'framer': ( - <> -

- Add the Pulse script to your Framer project's custom code settings. -

- -
- -

Add via Project Settings

-

- Go to Project Settings → General → Custom Code → Head{' '} - and paste the Pulse script. -

- {``} - -

- For more details, see the{' '} - - Framer custom code docs - . -

- -

- Related Integrations:{' '} - Webflow,{' '} - Squarespace,{' '} - Wix -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 43. Carrd - * ──────────────────────────────────────────────────────────────────────────── */ - 'carrd': ( - <> -

- Add the Pulse script to your Carrd site's head code section. -

- -
- -

Add via Settings

-

- Open your site's Settings panel and navigate to the{' '} - Head section. Paste the Pulse script there. -

- {``} - -

- Note: Custom code requires a Carrd Pro plan. -

- -

- For more details, see the{' '} - - Carrd head settings docs - . -

- -

- Related Integrations:{' '} - Framer,{' '} - Webflow -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 44. Blogger - * ──────────────────────────────────────────────────────────────────────────── */ - 'blogger': ( - <> -

- Add the Pulse script via Blogger's Theme HTML editor. -

- -
- -

Edit your theme HTML

-

- Go to Theme → Edit HTML and paste the Pulse script - before the closing </head> tag. -

- {` -`} - -

- For more details, see the{' '} - - Blogger HTML editing docs - . -

- -

- Related Integrations:{' '} - WordPress,{' '} - Ghost -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 45. Google Tag Manager - * ──────────────────────────────────────────────────────────────────────────── */ - 'gtm': ( - <> -

- Add Pulse via Google Tag Manager — works with any site that already has - GTM installed. -

- -
- -

Create a Custom HTML tag

-

Follow these steps to add Pulse through GTM:

-
    -
  1. Go to Tags → New → Custom HTML
  2. -
  3. Paste the snippet below
  4. -
  5. Set the trigger to All Pages
  6. -
  7. Publish your container
  8. -
- {``} - -

- That's it. Pulse auto-detects the domain from the page, so no extra - configuration is needed. -

- -
- - Advanced: override domain or configure options - -

- If your site is registered under a different domain than the page - hostname, or you need custom options (API endpoint, storage mode, etc.), - use pulseConfig: -

- {` -`} -
- -

- For more details, see the{' '} - - GTM Custom HTML tag docs - . -

- -

- Related Integrations:{' '} - WordPress,{' '} - Shopify,{' '} - Webflow -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 46. Notion - * ──────────────────────────────────────────────────────────────────────────── */ - 'notion': ( - <> -

- Notion itself doesn't support custom scripts, but tools like{' '} - Super.so,{' '} - Potion, and{' '} - Feather{' '} - let you turn Notion pages into websites with custom code support. -

- -
- -

Super.so

-

- Go to Site Settings → Code → Head and paste - the Pulse script. -

- {``} - -

Potion

-

- Go to Project Settings → Custom Code → Head{' '} - and paste the Pulse script. -

- -

- For more details, see the{' '} - - Super.so docs - . -

- -

- Related Integrations:{' '} - Webflow,{' '} - Framer,{' '} - Carrd -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 47. Cloudflare Pages - * ──────────────────────────────────────────────────────────────────────────── */ - 'cloudflare-pages': ( - <> -

- Add Pulse to your project's HTML or follow your framework's - guide. -

- -
- -

Method 1: Framework-based setup (Recommended)

-

- If you're deploying a framework (Next.js, Astro, Nuxt, etc.) to - Cloudflare Pages, follow that framework's integration guide: -

- - -

Method 2: Static HTML

-

- For static HTML sites, add the Pulse script directly to your{' '} - index.html. -

- {` - - - - - - - - My Site - - -

Hello World

- -`}
- -

- For more details, see the{' '} - - Cloudflare Pages docs - . -

- -

- Related Integrations:{' '} - Netlify,{' '} - Vercel,{' '} - GitHub Pages -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 48. Netlify - * ──────────────────────────────────────────────────────────────────────────── */ - 'netlify': ( - <> -

- Add Pulse via your framework's setup or Netlify's snippet - injection feature. -

- -
- -

Method 1: Framework guide (Recommended)

-

- The best approach is to follow your framework's integration guide. - Netlify deploys framework projects, so the script setup happens in your - source code: -

- - -

Method 2: Snippet injection (via Netlify UI)

-

- Go to Site settings → Build & deploy → Post processing → Snippet injection{' '} - and add the Pulse script to the <head> of your - pages. -

- {``} - -

- For more details, see the{' '} - - Netlify snippet injection docs - . -

- -

- Related Integrations:{' '} - Cloudflare Pages,{' '} - Vercel,{' '} - GitHub Pages -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 49. Vercel - * ──────────────────────────────────────────────────────────────────────────── */ - 'vercel': ( - <> -

- Add Pulse via your framework's setup. Vercel deploys framework - projects, so the script is added in your source code. -

- -
- -

Follow your framework's guide

-

- Vercel is a deployment platform — it doesn't have a built-in - mechanism for injecting scripts. Add the Pulse script following - your framework's integration guide: -

- - -

- Note: Vercel's Edge Middleware cannot inject - scripts by design. Use the framework-level approach instead. -

- -

- For more details, see the{' '} - - Vercel frameworks docs - . -

- -

- Related Integrations:{' '} - Netlify,{' '} - Cloudflare Pages,{' '} - Next.js -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 50. GitHub Pages - * ──────────────────────────────────────────────────────────────────────────── */ - 'github-pages': ( - <> -

- Add the Pulse script to your static site's HTML template on GitHub - Pages. -

- -
- -

Method 1: Jekyll (default for GitHub Pages)

-

- Create an analytics include with a production guard and reference it in - your default layout. -

- {`{% if jekyll.environment == "production" %} - -{% endif %}`} - -

- Then include it in your layout's <head>: -

- {` - - - - - {% include analytics.html %} - {{ page.title }} - - - {{ content }} - -`} - -

Method 2: Plain HTML

-

- For simple static sites, add the Pulse script directly to your{' '} - index.html. -

- {` - - - - - - - - My Site - - -

Hello World

- -`}
- -

Method 3: Hugo on GitHub Pages

-

- If you're using Hugo with GitHub Pages, follow the{' '} - Hugo guide. -

- -

- For more details, see the{' '} - - GitHub Pages docs - . -

- -

- Related Integrations:{' '} - Jekyll,{' '} - Hugo,{' '} - Netlify -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 51. Craft CMS - * ──────────────────────────────────────────────────────────────────────────── */ - 'craftcms': ( - <> -

- Add Pulse to your Craft CMS site by editing your Twig layout template. -

- -
- -

Add the Pulse Script to Your Layout

-

- Edit your main Twig layout template at{' '} - templates/_layout.twig and add the Pulse script inside the{' '} - <head> section. -

- {` - - - - - - - - {{ siteName }} - - - {% block content %}{% endblock %} - -`} - -

- For more details, see the{' '} - - Craft CMS docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 52. Statamic - * ──────────────────────────────────────────────────────────────────────────── */ - 'statamic': ( - <> -

- Statamic is a Laravel-based CMS. Add Pulse to your Antlers or Blade - layout. -

- -
- -

Add the Pulse Script to Your Layout

-

- Edit your Antlers layout at{' '} - resources/views/layout.antlers.html and add the Pulse - script inside the <head> section. -

- {` - - - - - - - - {{ title }} - - - {{ template_content }} - -`} - -

- For more details, see the{' '} - - Statamic views docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 53. TYPO3 - * ──────────────────────────────────────────────────────────────────────────── */ - 'typo3': ( - <> -

- Add Pulse to your TYPO3 site via TypoScript setup. -

- -
- -

Add the Pulse Script via TypoScript

-

- Add the following to your setup.typoscript file to inject - the Pulse script into the page header. -

- {`page.headerData.999 = TEXT -page.headerData.999.value = `} - -

- For more details, see the{' '} - - TypoScript reference - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 54. Kirby - * ──────────────────────────────────────────────────────────────────────────── */ - 'kirby': ( - <> -

- Add Pulse to your Kirby site via a PHP snippet. -

- -
- -

Add the Pulse Script to Your Header Snippet

-

- Edit site/snippets/header.php or{' '} - site/templates/default.php and add the Pulse script before - the closing </head> tag. -

- {` - - - - - - - - <?= $page->title() ?> -`} - -

- For more details, see the{' '} - - Kirby snippets docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 55. Grav - * ──────────────────────────────────────────────────────────────────────────── */ - 'grav': ( - <> -

- Add Pulse to your Grav site via Twig templates. -

- -
- -

Add the Pulse Script to Your Base Template

-

- Edit your theme's base template at{' '} - templates/partials/base.html.twig and add the Pulse script - inside the head block. -

- {` - - - {% block head %} - - - - - - {{ page.title }} - {% endblock %} - - - {% block content %}{% endblock %} - -`} - -

- For more details, see the{' '} - - Grav Twig docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 56. Umbraco - * ──────────────────────────────────────────────────────────────────────────── */ - 'umbraco': ( - <> -

- Add Pulse to your Umbraco site via a Razor layout view. -

- -
- -

Add the Pulse Script to Your Layout

-

- Edit Views/Shared/_Layout.cshtml and add the Pulse script - before the closing </head> tag. Use an environment - tag guard to only load in production. -

- {` - - - - - - - - - - @ViewData["Title"] - - - @RenderBody() - -`} - -

- For more details, see the{' '} - - Umbraco templates docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 57. Storyblok - * ──────────────────────────────────────────────────────────────────────────── */ - 'storyblok': ( - <> -

- Storyblok is a headless CMS — add Pulse to the frontend that renders - your Storyblok content. -

- -
- -

Add Pulse to Your Frontend

-

- Since Storyblok is a headless CMS, the analytics script goes in your - frontend framework. Follow the guide for your framework: -

- - -

- For more details, see the{' '} - - Storyblok docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 58. Prismic - * ──────────────────────────────────────────────────────────────────────────── */ - 'prismic': ( - <> -

- Prismic is a headless CMS — add Pulse to the frontend that displays - your Prismic content. -

- -
- -

Add Pulse to Your Frontend

-

- Since Prismic is a headless CMS, the analytics script goes in your - frontend framework. Follow the guide for your framework: -

- - -

- For more details, see the{' '} - - Prismic docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 59. Shopware - * ──────────────────────────────────────────────────────────────────────────── */ - 'shopware': ( - <> -

- Add Pulse to your Shopware 6 store via theme template. -

- -
- -

Add the Pulse Script to Your Theme

-

- Edit{' '} - Resources/views/storefront/layout/meta.html.twig in your - theme and add the Pulse script in the base_header block. -

- {`{% sw_extends '@Storefront/storefront/layout/meta.html.twig' %} - -{% block base_header %} - {{ parent() }} - - -{% endblock %}`} - -

- For more details, see the{' '} - - Shopware themes docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 60. Magento / Adobe Commerce - * ──────────────────────────────────────────────────────────────────────────── */ - 'magento': ( - <> -

- Add Pulse to your Magento / Adobe Commerce store via layout XML. -

- -
- -

Method 1: Layout XML

-

- Add the Pulse script to your theme's layout XML at{' '} - app/design/frontend/YOUR_THEME/default/Magento_Theme/layout/default_head_blocks.xml. -

- {` - - `} - -

- For more details, see the{' '} - - Magento layout docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 61. Bubble - * ──────────────────────────────────────────────────────────────────────────── */ - 'bubble': ( - <> -

- Add Pulse to your Bubble app via the SEO / Meta tags section. -

- -
- -

Add the Pulse Script via Settings

-

- Go to Settings → SEO / Metatags → Script/meta tags in - header and paste the Pulse script. -

- {``} - -

- For more details, see the{' '} - - Bubble settings docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 62. Discourse - * ──────────────────────────────────────────────────────────────────────────── */ - 'discourse': ( - <> -

- Add Pulse to your Discourse forum via admin customization. -

- -
- -

Add the Pulse Script via Theme Customization

-

- Go to Admin → Customize → Themes → Edit - CSS/HTML and add the Pulse script in the{' '} - </head> section. -

- {``} - -

- For more details, see the{' '} - - Discourse themes guide - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 63. HubSpot - * ──────────────────────────────────────────────────────────────────────────── */ - 'hubspot': ( - <> -

- Add Pulse to your HubSpot-hosted pages via site settings. -

- -
- -

Add the Pulse Script via Site Settings

-

- Go to Settings → Website → Pages → Site Header - HTML and paste the Pulse script. -

- {``} - -

- Works for HubSpot CMS Free, Starter, Pro, and Enterprise. -

- -

- For more details, see the{' '} - - HubSpot header code docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 64. Substack - * ──────────────────────────────────────────────────────────────────────────── */ - 'substack': ( - <> -

- Substack supports custom domains. Add Pulse tracking for your custom - domain. -

- -
- -

Track Your Substack via a Custom Domain

-

- Substack doesn't allow custom scripts directly. You can track your - Substack via your custom domain by configuring Pulse to track the custom - domain. -

-
    -
  1. Set up your custom domain in Substack.
  2. -
  3. Add your custom domain in the Pulse dashboard.
  4. -
  5. Pulse will automatically track page views on your custom domain.
  6. -
- -

- For more details, see the{' '} - - Substack custom domain docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 65. Linktree - * ──────────────────────────────────────────────────────────────────────────── */ - 'linktree': ( - <> -

- Add Pulse to your Linktree page via custom code (Business plan - required). -

- -
- -

Add the Pulse Script via Custom Meta Tags

-

- Go to Settings → SEO → Custom Meta Tags and - add the Pulse script to the head. -

- {``} - -

- Note: Requires Linktree Business or Enterprise plan. -

- -

- For more details, see{' '} - - Linktree Business - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 66. Weebly - * ──────────────────────────────────────────────────────────────────────────── */ - 'weebly': ( - <> -

- Add Pulse to your Weebly site via the header code settings. -

- -
- -

Add the Pulse Script via SEO Settings

-

- Go to Settings → SEO → Header Code and paste - the Pulse script. -

- {``} - -

- For more details, see the{' '} - - Weebly SEO docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 67. GitBook - * ──────────────────────────────────────────────────────────────────────────── */ - 'gitbook': ( - <> -

- Add Pulse to your GitBook documentation site. -

- -
- -

Add the Pulse Script via Custom Scripts

-

- GitBook supports custom header integrations. Go to{' '} - Space settings → Integrations → Custom - scripts and add the Pulse script. -

- {``} - -

- For more details, see the{' '} - - GitBook customization docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 68. Gridsome - * ──────────────────────────────────────────────────────────────────────────── */ - 'gridsome': ( - <> -

- Add Pulse to your Gridsome site via the HTML template or a plugin. -

- -
- -

Method 1: HTML Template

-

- Edit src/index.html and add the Pulse script to the head - section. -

- {` - - - \${head} - - - - - \${app} - -`} - -

Method 2: Server Configuration

-

- Use gridsome.server.js to inject the script - programmatically. -

- {`module.exports = function (api) { - api.afterBuild(({ queue }) => { - // Script injection logic - }) -}`} - -

- For more details, see the{' '} - - Gridsome head management docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 69. Read the Docs - * ──────────────────────────────────────────────────────────────────────────── */ - 'readthedocs': ( - <> -

- Add Pulse to your Read the Docs documentation. -

- -
- -

Add the Pulse Script via Sphinx Configuration

-

- Create a custom template override. In your Sphinx{' '} - conf.py, add the Pulse script as a JavaScript file. -

- {`html_js_files = [ - ('https://pulse.ciphera.net/script.js', {'defer': 'defer', 'data-domain': 'your-site.com'}), -]`} - -

- For more details, see the{' '} - - Read the Docs customization - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 70. Sphinx - * ──────────────────────────────────────────────────────────────────────────── */ - 'sphinx': ( - <> -

- Add Pulse to your Sphinx documentation via conf.py. -

- -
- -

Add the Pulse Script via Configuration

-

- In your conf.py, add the Pulse script using{' '} - html_js_files. -

- {`html_js_files = [ - ('https://pulse.ciphera.net/script.js', {'defer': 'defer', 'data-domain': 'your-site.com'}), -]`} - -

- For more details, see the{' '} - - Sphinx html_js_files docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 71. ReadMe - * ──────────────────────────────────────────────────────────────────────────── */ - 'readme': ( - <> -

- Add Pulse to your ReadMe API documentation portal. -

- -
- -

Add the Pulse Script via Custom JavaScript

-

- Go to Project Settings → Custom JavaScript and - paste the Pulse script. -

- {``} - -

- For more details, see the{' '} - - ReadMe custom JavaScript docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 72. Flutter Web - * ──────────────────────────────────────────────────────────────────────────── */ - 'flutter': ( - <> -

- Add Pulse to your Flutter web application via{' '} - web/index.html. -

- -
- -

Add the Pulse Script to Your HTML Template

-

- Edit web/index.html in your Flutter project and add the - Pulse script to the <head> section. -

- {` - - - - - - - - My Flutter App - - - - -`} - -

- Note: This only applies to Flutter Web. For Flutter - mobile apps, Pulse tracks web views only. -

- -

- For more details, see the{' '} - - Flutter Web docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 73. Render - * ──────────────────────────────────────────────────────────────────────────── */ - 'render': ( - <> -

- Add Pulse via your framework setup. Render deploys framework projects. -

- -
- -

Follow Your Framework's Guide

-

- The analytics script goes in your source code, not in Render's - dashboard. Follow your framework's integration guide: -

- - -

- For more details, see the{' '} - - Render deployment docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 74. Firebase Hosting - * ──────────────────────────────────────────────────────────────────────────── */ - 'firebase': ( - <> -

- Add Pulse to sites deployed on Firebase Hosting. -

- -
- -

Add the Pulse Script to Your Source Code

-

- Follow your framework's integration guide. The analytics script - goes in your source code. Firebase Hosting serves static files, so add - the script to your index.html or framework layout. -

- {` - - - - - - - - My App - - -
- -`}
- -

- For more details, see the{' '} - - Firebase Hosting docs - . -

- - ), - - /* ──────────────────────────────────────────────────────────────────────────── - * 75. AMP - * ──────────────────────────────────────────────────────────────────────────── */ - 'amp': ( - <> -

- Add Pulse to your AMP pages using the amp-analytics{' '} - component. -

- -
- -

Add Pulse via amp-analytics

-

- AMP pages have restrictions on JavaScript. The{' '} - amp-analytics component is the standard way to add - analytics. Add the following to your AMP page. -

- {` - -`} - -

- Note: AMP pages have restrictions on JavaScript. The{' '} - amp-analytics component is the standard way to add - analytics. -

- -

- For more details, see the{' '} - - amp-analytics docs - . -

- - ), -} - -// * ─── Public API ───────────────────────────────────────────────────────────── - -export function getGuideContent(slug: string): ReactNode | undefined { - return guides[slug] -} diff --git a/lib/integrations.tsx b/lib/integrations.tsx index 0c6c8fc..79b1345 100644 --- a/lib/integrations.tsx +++ b/lib/integrations.tsx @@ -34,10 +34,10 @@ export interface Integration { icon: ReactNode /** URL to official documentation / website */ officialUrl: string - /** SEO meta description for this integration's guide page */ - seoDescription: string /** Related integration IDs for cross-linking */ relatedIds: string[] + /** Whether this integration has a dedicated guide page */ + dedicatedPage: boolean } // * ─── Category labels (for UI grouping) ────────────────────────────────────── @@ -79,9 +79,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://nextjs.org/docs', - seoDescription: - 'Step-by-step guide to adding Pulse privacy-first analytics to your Next.js app with next/script. Covers App Router and Pages Router.', relatedIds: ['react', 'vercel', 'nuxt'], + dedicatedPage: true, }, { id: 'react', @@ -95,9 +94,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://react.dev', - seoDescription: - 'Integrate Pulse analytics with any React SPA — Create React App, Vite, or custom setups. Two easy methods.', relatedIds: ['nextjs', 'remix', 'gatsby', 'preact'], + dedicatedPage: true, }, { id: 'vue', @@ -111,9 +109,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://vuejs.org', - seoDescription: - 'Add Pulse privacy-first analytics to your Vue.js app. Works with Vue 2, Vue 3, Vue CLI, and Vite.', relatedIds: ['nuxt', 'vitepress'], + dedicatedPage: true, }, { id: 'angular', @@ -128,9 +125,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://angular.dev', - seoDescription: - 'Add Pulse analytics to your Angular application. Simple index.html setup for all Angular versions.', relatedIds: ['react', 'vue'], + dedicatedPage: true, }, { id: 'svelte', @@ -144,9 +140,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://svelte.dev', - seoDescription: - 'Add Pulse analytics to Svelte or SvelteKit. Simple setup for both Vite-based Svelte and SvelteKit applications.', relatedIds: ['astro', 'vue'], + dedicatedPage: true, }, { id: 'nuxt', @@ -160,9 +155,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://nuxt.com/docs', - seoDescription: - 'Configure Pulse analytics in Nuxt 2 or Nuxt 3 via nuxt.config. Simple, framework-native setup.', relatedIds: ['vue', 'nextjs', 'vitepress'], + dedicatedPage: true, }, { id: 'remix', @@ -177,9 +171,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://remix.run/docs', - seoDescription: - 'Add Pulse analytics to your Remix application via the root route. Simple script tag in app/root.tsx.', relatedIds: ['react', 'nextjs'], + dedicatedPage: true, }, { id: 'astro', @@ -193,9 +186,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.astro.build', - seoDescription: - 'Integrate Pulse analytics with Astro. Add the script to your base layout for all pages.', relatedIds: ['svelte', 'hugo', 'eleventy'], + dedicatedPage: true, }, { id: 'solidjs', @@ -209,9 +201,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.solidjs.com/docs', - seoDescription: - 'Add Pulse analytics to your Solid.js application. Simple index.html script tag setup.', relatedIds: ['react', 'qwik', 'preact'], + dedicatedPage: false, }, { id: 'qwik', @@ -225,9 +216,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://qwik.dev/docs', - seoDescription: - 'Integrate Pulse analytics with Qwik. Add the script to your root entry file.', relatedIds: ['react', 'solidjs', 'astro'], + dedicatedPage: false, }, { id: 'preact', @@ -241,9 +231,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://preactjs.com/guide', - seoDescription: - 'Add Pulse analytics to your Preact application. Same simple setup as any Vite or HTML project.', relatedIds: ['react', 'solidjs'], + dedicatedPage: false, }, { id: 'htmx', @@ -257,9 +246,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://htmx.org/docs', - seoDescription: - 'Add Pulse analytics to your HTMX-powered site. Works with any backend serving HTML.', relatedIds: ['django', 'flask', 'laravel', 'rails'], + dedicatedPage: false, }, { id: 'ember', @@ -273,9 +261,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://guides.emberjs.com', - seoDescription: - 'Add Pulse analytics to your Ember.js app. Simple index.html script tag setup.', relatedIds: ['react', 'angular'], + dedicatedPage: false, }, // * ─── Backend Frameworks ─────────────────────────────────────────────────── @@ -291,9 +278,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://laravel.com/docs', - seoDescription: - 'Add Pulse analytics to your Laravel app. Blade template integration for all Laravel versions.', relatedIds: ['django', 'rails', 'wordpress'], + dedicatedPage: true, }, { id: 'django', @@ -307,9 +293,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.djangoproject.com', - seoDescription: - 'Add Pulse analytics to your Django app. Template-based integration for all Django versions.', relatedIds: ['flask', 'laravel', 'htmx'], + dedicatedPage: true, }, { id: 'rails', @@ -323,9 +308,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://guides.rubyonrails.org', - seoDescription: - 'Add Pulse analytics to your Ruby on Rails app. ERB layout integration.', relatedIds: ['laravel', 'django', 'jekyll'], + dedicatedPage: true, }, { id: 'flask', @@ -339,9 +323,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://flask.palletsprojects.com', - seoDescription: - 'Add Pulse analytics to your Flask app. Jinja2 template integration.', relatedIds: ['django', 'htmx', 'express'], + dedicatedPage: true, }, { id: 'express', @@ -356,9 +339,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://expressjs.com', - seoDescription: - 'Serve Pulse analytics from your Express.js app. Middleware or template-based setup.', relatedIds: ['flask', 'nextjs', 'react'], + dedicatedPage: true, }, // * ─── Static Sites & Documentation ───────────────────────────────────────── @@ -374,9 +356,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.gatsbyjs.com/docs', - seoDescription: - 'Add Pulse analytics to your Gatsby site using gatsby-ssr or the Gatsby Head API.', relatedIds: ['react', 'nextjs', 'hugo'], + dedicatedPage: true, }, { id: 'hugo', @@ -390,9 +371,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://gohugo.io/documentation', - seoDescription: - 'Add Pulse analytics to your Hugo site via a partial or base template.', relatedIds: ['jekyll', 'eleventy', 'astro'], + dedicatedPage: true, }, { id: 'eleventy', @@ -407,9 +387,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.11ty.dev/docs', - seoDescription: - 'Add Pulse analytics to your Eleventy (11ty) site. Template-based integration.', relatedIds: ['hugo', 'jekyll', 'astro'], + dedicatedPage: true, }, { id: 'jekyll', @@ -423,9 +402,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://jekyllrb.com/docs', - seoDescription: - 'Add Pulse analytics to your Jekyll site. Liquid template integration for GitHub Pages and beyond.', relatedIds: ['hugo', 'eleventy', 'github-pages'], + dedicatedPage: false, }, { id: 'docusaurus', @@ -439,9 +417,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docusaurus.io/docs', - seoDescription: - 'Add Pulse analytics to your Docusaurus documentation site via docusaurus.config.js.', relatedIds: ['vitepress', 'mkdocs', 'gatsby'], + dedicatedPage: false, }, { id: 'vitepress', @@ -455,9 +432,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://vitepress.dev', - seoDescription: - 'Add Pulse analytics to your VitePress documentation site via config.', relatedIds: ['docusaurus', 'vue', 'nuxt'], + dedicatedPage: false, }, { id: 'hexo', @@ -471,9 +447,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://hexo.io/docs', - seoDescription: - 'Add Pulse analytics to your Hexo blog or documentation site.', relatedIds: ['hugo', 'jekyll', 'eleventy'], + dedicatedPage: false, }, { id: 'mkdocs', @@ -487,9 +462,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.mkdocs.org', - seoDescription: - 'Add Pulse analytics to your MkDocs documentation site.', relatedIds: ['docusaurus', 'vitepress', 'django'], + dedicatedPage: false, }, // * ─── CMS & Blogging ────────────────────────────────────────────────────── @@ -505,9 +479,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://wordpress.org/documentation', - seoDescription: - 'Add Pulse analytics to your WordPress site via a plugin or theme header code.', relatedIds: ['ghost', 'drupal', 'woocommerce'], + dedicatedPage: true, }, { id: 'ghost', @@ -522,9 +495,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://ghost.org/docs', - seoDescription: - 'Add Pulse analytics to your Ghost blog via Code Injection settings.', relatedIds: ['wordpress', 'blogger'], + dedicatedPage: true, }, { id: 'drupal', @@ -538,9 +510,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.drupal.org/docs', - seoDescription: - 'Add Pulse analytics to your Drupal site using a module or theme template.', relatedIds: ['wordpress', 'joomla'], + dedicatedPage: true, }, { id: 'joomla', @@ -554,9 +525,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.joomla.org', - seoDescription: - 'Add Pulse analytics to your Joomla site via template or extension.', relatedIds: ['wordpress', 'drupal'], + dedicatedPage: false, }, { id: 'strapi', @@ -570,9 +540,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.strapi.io', - seoDescription: - 'Add Pulse analytics to your Strapi-powered frontend.', relatedIds: ['contentful', 'sanity', 'nextjs'], + dedicatedPage: false, }, { id: 'sanity', @@ -587,9 +556,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.sanity.io/docs', - seoDescription: - 'Add Pulse analytics to your Sanity-powered frontend application.', relatedIds: ['strapi', 'contentful', 'nextjs'], + dedicatedPage: false, }, { id: 'contentful', @@ -603,9 +571,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.contentful.com/developers/docs', - seoDescription: - 'Add Pulse analytics to your Contentful-powered frontend.', relatedIds: ['strapi', 'sanity', 'nextjs'], + dedicatedPage: false, }, { id: 'payload', @@ -620,9 +587,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://payloadcms.com/docs', - seoDescription: - 'Add Pulse analytics to your Payload CMS frontend application.', relatedIds: ['strapi', 'contentful', 'nextjs'], + dedicatedPage: false, }, // * ─── eCommerce ──────────────────────────────────────────────────────────── @@ -638,9 +604,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://shopify.dev/docs', - seoDescription: - 'Add Pulse privacy-first analytics to your Shopify store via the theme editor.', relatedIds: ['woocommerce', 'bigcommerce', 'prestashop'], + dedicatedPage: true, }, { id: 'woocommerce', @@ -654,9 +619,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://woocommerce.com/documentation', - seoDescription: - 'Add Pulse analytics to your WooCommerce store. WordPress-based setup.', relatedIds: ['shopify', 'wordpress', 'bigcommerce'], + dedicatedPage: false, }, { id: 'bigcommerce', @@ -671,9 +635,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://developer.bigcommerce.com/docs', - seoDescription: - 'Add Pulse analytics to your BigCommerce store via Script Manager.', relatedIds: ['shopify', 'woocommerce', 'prestashop'], + dedicatedPage: false, }, { id: 'prestashop', @@ -687,9 +650,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://devdocs.prestashop-project.org', - seoDescription: - 'Add Pulse analytics to your PrestaShop store via theme template.', relatedIds: ['shopify', 'woocommerce', 'bigcommerce'], + dedicatedPage: false, }, // * ─── Platforms & Tools ──────────────────────────────────────────────────── @@ -705,9 +667,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://university.webflow.com', - seoDescription: - 'Add Pulse analytics to your Webflow site via project custom code settings.', relatedIds: ['squarespace', 'wix', 'framer'], + dedicatedPage: true, }, { id: 'squarespace', @@ -722,9 +683,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://support.squarespace.com', - seoDescription: - 'Add Pulse analytics to your Squarespace site via the Code Injection panel.', relatedIds: ['webflow', 'wix', 'carrd'], + dedicatedPage: true, }, { id: 'wix', @@ -738,9 +698,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://support.wix.com', - seoDescription: - 'Add Pulse analytics to your Wix site via Custom Code settings.', relatedIds: ['webflow', 'squarespace', 'framer'], + dedicatedPage: true, }, { id: 'framer', @@ -754,9 +713,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.framer.com/help', - seoDescription: - 'Add Pulse analytics to your Framer site via custom code settings.', relatedIds: ['webflow', 'squarespace', 'wix'], + dedicatedPage: true, }, { id: 'carrd', @@ -770,9 +728,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://carrd.co/docs', - seoDescription: - 'Add Pulse analytics to your Carrd one-page site via head code.', relatedIds: ['framer', 'webflow'], + dedicatedPage: false, }, { id: 'blogger', @@ -786,9 +743,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://support.google.com/blogger', - seoDescription: - 'Add Pulse analytics to your Blogger blog via theme HTML editor.', relatedIds: ['wordpress', 'ghost'], + dedicatedPage: false, }, { id: 'gtm', @@ -802,9 +758,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://tagmanager.google.com', - seoDescription: - 'Add Pulse analytics via Google Tag Manager. Works with any site using GTM.', relatedIds: ['wordpress', 'shopify', 'webflow'], + dedicatedPage: true, }, { id: 'notion', @@ -819,9 +774,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.notion.so', - seoDescription: - 'Add Pulse analytics to Notion-powered websites using Super.so, Potion, or similar tools.', relatedIds: ['webflow', 'framer', 'carrd'], + dedicatedPage: false, }, // * ─── Hosting & Deployment ───────────────────────────────────────────────── @@ -837,9 +791,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://developers.cloudflare.com/pages', - seoDescription: - 'Deploy with Pulse analytics on Cloudflare Pages. Works with any framework.', relatedIds: ['netlify', 'vercel', 'github-pages'], + dedicatedPage: false, }, { id: 'netlify', @@ -853,9 +806,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.netlify.com', - seoDescription: - 'Add Pulse analytics to sites deployed on Netlify. Snippet injection and build-based methods.', relatedIds: ['cloudflare-pages', 'vercel', 'github-pages'], + dedicatedPage: false, }, { id: 'vercel', @@ -870,9 +822,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://vercel.com/docs', - seoDescription: - 'Add Pulse analytics to sites deployed on Vercel. Works with any framework.', relatedIds: ['netlify', 'cloudflare-pages', 'nextjs'], + dedicatedPage: false, }, { id: 'github-pages', @@ -887,9 +838,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.github.com/en/pages', - seoDescription: - 'Add Pulse analytics to your GitHub Pages site. Works with Jekyll, Hugo, or plain HTML.', relatedIds: ['jekyll', 'hugo', 'netlify'], + dedicatedPage: false, }, // * ─── CMS & Blogging (continued) ────────────────────────────────────────── @@ -905,9 +855,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://craftcms.com/docs', - seoDescription: - 'Add Pulse analytics to your Craft CMS site via Twig templates. Simple installation.', relatedIds: ['wordpress', 'statamic', 'drupal'], + dedicatedPage: false, }, { id: 'statamic', @@ -921,9 +870,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://statamic.dev/docs', - seoDescription: - 'Add Pulse analytics to your Statamic site. Antlers template integration.', relatedIds: ['craftcms', 'laravel', 'wordpress'], + dedicatedPage: false, }, { id: 'typo3', @@ -937,9 +885,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.typo3.org', - seoDescription: - 'Add Pulse analytics to your TYPO3 CMS site. TypoScript and Fluid template integration.', relatedIds: ['wordpress', 'drupal', 'joomla'], + dedicatedPage: false, }, { id: 'kirby', @@ -954,9 +901,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://getkirby.com/docs', - seoDescription: - 'Add Pulse analytics to your Kirby CMS site. Simple PHP snippet integration.', relatedIds: ['craftcms', 'statamic', 'grav'], + dedicatedPage: false, }, { id: 'grav', @@ -971,9 +917,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://learn.getgrav.org', - seoDescription: - 'Add Pulse analytics to your Grav flat-file CMS. Twig template integration.', relatedIds: ['kirby', 'craftcms', 'hugo'], + dedicatedPage: false, }, { id: 'umbraco', @@ -987,9 +932,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.umbraco.com', - seoDescription: - 'Add Pulse analytics to your Umbraco CMS site. Razor view integration for .NET.', relatedIds: ['wordpress', 'drupal', 'typo3'], + dedicatedPage: false, }, { id: 'storyblok', @@ -1003,9 +947,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.storyblok.com/docs', - seoDescription: - 'Add Pulse analytics to your Storyblok-powered frontend application.', relatedIds: ['contentful', 'prismic', 'nextjs'], + dedicatedPage: false, }, { id: 'prismic', @@ -1019,9 +962,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://prismic.io/docs', - seoDescription: - 'Add Pulse analytics to your Prismic-powered frontend application.', relatedIds: ['contentful', 'storyblok', 'nextjs'], + dedicatedPage: false, }, // * ─── eCommerce (continued) ─────────────────────────────────────────────── @@ -1037,9 +979,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://developer.shopware.com/docs', - seoDescription: - 'Add Pulse analytics to your Shopware 6 store via theme template.', relatedIds: ['shopify', 'woocommerce', 'magento'], + dedicatedPage: false, }, { id: 'magento', @@ -1053,9 +994,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://developer.adobe.com/commerce', - seoDescription: - 'Add Pulse analytics to your Magento or Adobe Commerce store via layout XML.', relatedIds: ['shopify', 'woocommerce', 'shopware'], + dedicatedPage: false, }, // * ─── Platforms & Tools (continued) ─────────────────────────────────────── @@ -1072,9 +1012,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://manual.bubble.io', - seoDescription: - 'Add Pulse analytics to your Bubble no-code app via the SEO/Meta tags section.', relatedIds: ['webflow', 'framer', 'wix'], + dedicatedPage: false, }, { id: 'discourse', @@ -1089,9 +1028,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://meta.discourse.org/docs', - seoDescription: - 'Add Pulse analytics to your Discourse forum via admin customization.', relatedIds: ['wordpress', 'ghost'], + dedicatedPage: false, }, { id: 'hubspot', @@ -1105,9 +1043,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://knowledge.hubspot.com', - seoDescription: - 'Add Pulse analytics to HubSpot landing pages and website via Settings.', relatedIds: ['wordpress', 'webflow'], + dedicatedPage: false, }, { id: 'substack', @@ -1121,9 +1058,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://substack.com', - seoDescription: - 'Add Pulse analytics to your Substack publication using custom domain settings.', relatedIds: ['ghost', 'blogger', 'wordpress'], + dedicatedPage: false, }, { id: 'linktree', @@ -1137,9 +1073,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://linktr.ee', - seoDescription: - 'Add Pulse analytics to your Linktree link-in-bio page.', relatedIds: ['carrd', 'framer', 'webflow'], + dedicatedPage: false, }, { id: 'weebly', @@ -1153,9 +1088,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.weebly.com', - seoDescription: - 'Add Pulse analytics to your Weebly website via the header code settings.', relatedIds: ['squarespace', 'wix', 'webflow'], + dedicatedPage: false, }, // * ─── Static Sites & Documentation (continued) ─────────────────────────── @@ -1171,9 +1105,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.gitbook.com', - seoDescription: - 'Add Pulse analytics to your GitBook-hosted documentation.', relatedIds: ['docusaurus', 'readme', 'readthedocs'], + dedicatedPage: false, }, { id: 'gridsome', @@ -1187,9 +1120,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://gridsome.org/docs', - seoDescription: - 'Add Pulse analytics to your Gridsome Vue-based static site.', relatedIds: ['gatsby', 'vue', 'nuxt'], + dedicatedPage: false, }, { id: 'readthedocs', @@ -1204,9 +1136,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.readthedocs.io', - seoDescription: - 'Add Pulse analytics to your Read the Docs documentation site.', relatedIds: ['sphinx', 'mkdocs', 'docusaurus'], + dedicatedPage: false, }, { id: 'sphinx', @@ -1221,9 +1152,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://www.sphinx-doc.org', - seoDescription: - 'Add Pulse analytics to your Sphinx-generated documentation.', relatedIds: ['readthedocs', 'mkdocs', 'docusaurus'], + dedicatedPage: false, }, { id: 'readme', @@ -1237,9 +1167,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.readme.com', - seoDescription: - 'Add Pulse analytics to your ReadMe API documentation portal.', relatedIds: ['gitbook', 'docusaurus', 'readthedocs'], + dedicatedPage: false, }, // * ─── JavaScript Frameworks (continued) ─────────────────────────────────── @@ -1255,9 +1184,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.flutter.dev', - seoDescription: - 'Add Pulse analytics to your Flutter web app via web/index.html.', relatedIds: ['react', 'angular', 'preact'], + dedicatedPage: false, }, // * ─── Hosting & Deployment (continued) ──────────────────────────────────── @@ -1274,9 +1202,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://docs.render.com', - seoDescription: - 'Add Pulse analytics to sites deployed on Render. Works with any framework.', relatedIds: ['netlify', 'vercel', 'cloudflare-pages'], + dedicatedPage: false, }, { id: 'firebase', @@ -1290,9 +1217,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://firebase.google.com/docs/hosting', - seoDescription: - 'Add Pulse analytics to sites deployed on Firebase Hosting.', relatedIds: ['netlify', 'vercel', 'render'], + dedicatedPage: false, }, // * ─── Platforms & Tools (continued) ─────────────────────────────────────── @@ -1308,9 +1234,8 @@ export const integrations: Integration[] = [ ), officialUrl: 'https://amp.dev/documentation', - seoDescription: - 'Add Pulse analytics to Google AMP pages using amp-analytics.', relatedIds: ['gtm', 'wordpress', 'webflow'], + dedicatedPage: false, }, ] diff --git a/next.config.ts b/next.config.ts index 1070da7..cb46ba9 100644 --- a/next.config.ts +++ b/next.config.ts @@ -76,12 +76,31 @@ const nextConfig: NextConfig = { ] }, async redirects() { + const removedIntegrations = [ + 'solidjs', 'qwik', 'preact', 'htmx', 'ember', + 'jekyll', 'docusaurus', 'vitepress', 'hexo', 'mkdocs', + 'joomla', 'strapi', 'sanity', 'contentful', 'payload', + 'craftcms', 'statamic', 'typo3', 'kirby', 'grav', 'umbraco', + 'storyblok', 'prismic', 'shopware', 'magento', + 'woocommerce', 'bigcommerce', 'prestashop', + 'blogger', 'substack', 'linktree', 'weebly', 'gitbook', + 'gridsome', 'readthedocs', 'sphinx', 'readme', + 'bubble', 'discourse', 'hubspot', 'notion', + 'cloudflare-pages', 'netlify', 'vercel', 'github-pages', + 'firebase', 'render', 'flutter', 'amp', 'carrd', + ] + return [ { source: '/dashboard', destination: '/', permanent: false, }, + ...removedIntegrations.map((slug) => ({ + source: `/integrations/${slug}`, + destination: '/integrations/script-tag', + permanent: true, + })), ] }, async rewrites() { diff --git a/package-lock.json b/package-lock.json index 5436332..11d2877 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,18 +33,21 @@ "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "framer-motion": "^12.23.26", + "gray-matter": "^4.0.3", "html-to-image": "^1.11.13", "iso-3166-2": "^1.0.0", "jspdf": "^4.0.0", "jspdf-autotable": "^5.0.7", "lucide-react": "^0.577.0", "next": "^16.1.1", + "next-mdx-remote": "^6.0.0", "radix-ui": "^1.4.3", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", "react-use-measure": "^2.1.7", "recharts": "^2.15.0", + "remark-gfm": "^4.0.1", "sonner": "^2.0.7", "svg-dotted-map": "^2.0.1", "swr": "^2.3.3", @@ -3119,6 +3122,78 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mdx-js/mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz", + "integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@mdx-js/mdx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@mdx-js/mdx/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@mdx-js/react": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=16", + "react": ">=16" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -6085,6 +6160,12 @@ "@types/unist": "*" } }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "license": "MIT" + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -7191,7 +7272,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -7491,6 +7571,15 @@ "dev": true, "license": "MIT" }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "license": "MIT", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -8040,6 +8129,16 @@ "node": ">=0.8" } }, + "node_modules/collapse-white-space": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-2.1.0.tgz", + "integrity": "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -9184,6 +9283,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esast-util-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", + "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esast-util-from-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", + "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "acorn": "^8.0.0", + "esast-util-from-estree": "^2.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esbuild": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", @@ -9652,6 +9783,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", @@ -9688,6 +9832,44 @@ "node": ">=4.0" } }, + "node_modules/estree-util-attach-comments": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz", + "integrity": "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz", + "integrity": "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "estree-walker": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-build-jsx/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/estree-util-is-identifier-name": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", @@ -9698,6 +9880,58 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/estree-util-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-util-scope/-/estree-util-scope-1.0.0.tgz", + "integrity": "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "devlop": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", + "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "astring": "^1.8.0", + "source-map": "^0.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/estree-util-to-js/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/estree-util-visit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", + "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", @@ -9735,6 +9969,18 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10245,6 +10491,43 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -10333,6 +10616,34 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-estree": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz", + "integrity": "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -10775,6 +11086,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -11365,6 +11685,15 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -11528,6 +11857,28 @@ "sourcemap-codec": "^1.4.8" } }, + "node_modules/markdown-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-2.0.0.tgz", + "integrity": "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -11537,6 +11888,34 @@ "node": ">= 0.4" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", @@ -11561,6 +11940,124 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz", + "integrity": "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx-expression": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", @@ -11775,6 +12272,229 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-expression": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz", + "integrity": "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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" + } + }, + "node_modules/micromark-extension-mdx-jsx": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz", + "integrity": "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdx-md": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz", + "integrity": "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz", + "integrity": "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-mdxjs-esm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz", + "integrity": "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/micromark-factory-destination": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", @@ -11818,6 +12538,33 @@ "micromark-util-types": "^2.0.0" } }, + "node_modules/micromark-factory-mdx-expression": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz", + "integrity": "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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" + } + }, "node_modules/micromark-factory-space": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", @@ -12019,6 +12766,31 @@ ], "license": "MIT" }, + "node_modules/micromark-util-events-to-acorn": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz", + "integrity": "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "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" + } + }, "node_modules/micromark-util-html-tag-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", @@ -12320,6 +13092,28 @@ } } }, + "node_modules/next-mdx-remote": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-6.0.0.tgz", + "integrity": "sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ==", + "license": "MPL-2.0", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@mdx-js/mdx": "^3.0.1", + "@mdx-js/react": "^3.0.1", + "unist-util-remove": "^4.0.0", + "unist-util-visit": "^5.1.0", + "vfile": "^6.0.1", + "vfile-matter": "^5.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=7" + }, + "peerDependencies": { + "react": ">=16" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -13406,6 +14200,73 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, + "node_modules/recma-build-jsx": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz", + "integrity": "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-build-jsx": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-jsx": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz", + "integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/recma-parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-parse/-/recma-parse-1.0.0.tgz", + "integrity": "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "esast-util-from-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/recma-stringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/recma-stringify/-/recma-stringify-1.0.0.tgz", + "integrity": "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-util-to-js": "^2.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -13522,6 +14383,53 @@ "regjsparser": "bin/parser" } }, + "node_modules/rehype-recma": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/rehype-recma/-/rehype-recma-1.0.0.tgz", + "integrity": "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "hast-util-to-estree": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "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" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-mdx": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz", + "integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==", + "license": "MIT", + "dependencies": { + "mdast-util-mdx": "^3.0.0", + "micromark-extension-mdxjs": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -13555,6 +14463,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -13771,6 +14694,19 @@ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "license": "MIT", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -14068,6 +15004,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/ssf": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", @@ -14271,6 +15213,15 @@ "node": ">=4" } }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -15052,6 +16003,34 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-position-from-estree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", + "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", + "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", @@ -15271,6 +16250,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-matter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-5.0.1.tgz", + "integrity": "sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw==", + "license": "MIT", + "dependencies": { + "vfile": "^6.0.0", + "yaml": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -16174,6 +17167,21 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 6fa2eb7..c3ce286 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "private": true, "scripts": { "dev": "next dev", + "generate:integrations": "npx tsx scripts/generate-integrations.ts", + "prebuild": "npm run generate:integrations", "build": "next build --webpack", "start": "next start", "lint": "next lint", @@ -37,18 +39,21 @@ "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "framer-motion": "^12.23.26", + "gray-matter": "^4.0.3", "html-to-image": "^1.11.13", "iso-3166-2": "^1.0.0", "jspdf": "^4.0.0", "jspdf-autotable": "^5.0.7", "lucide-react": "^0.577.0", "next": "^16.1.1", + "next-mdx-remote": "^6.0.0", "radix-ui": "^1.4.3", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", "react-use-measure": "^2.1.7", "recharts": "^2.15.0", + "remark-gfm": "^4.0.1", "sonner": "^2.0.7", "svg-dotted-map": "^2.0.1", "swr": "^2.3.3", diff --git a/scripts/generate-integrations.ts b/scripts/generate-integrations.ts new file mode 100644 index 0000000..3e256a1 --- /dev/null +++ b/scripts/generate-integrations.ts @@ -0,0 +1,44 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +const CONTENT_DIR = path.join(process.cwd(), 'content', 'integrations') +const OUTPUT_PATH = path.join(process.cwd(), 'lib', 'integration-guides.gen.ts') + +const files = fs.existsSync(CONTENT_DIR) + ? fs.readdirSync(CONTENT_DIR).filter((f) => f.endsWith('.mdx')) + : [] + +const guides: { slug: string; title: string; description: string; category: string; date: string }[] = [] + +for (const filename of files) { + const slug = filename.replace(/\.mdx$/, '') + const raw = fs.readFileSync(path.join(CONTENT_DIR, filename), 'utf-8') + const { data } = matter(raw) + guides.push({ + slug, + title: data.title as string, + description: data.description as string, + category: data.category as string, + date: data.date as string, + }) +} + +guides.sort((a, b) => a.title.localeCompare(b.title)) + +const output = `// Auto-generated from content/integrations/*.mdx — do not edit manually +// Run: npm run generate:integrations + +export interface IntegrationGuideSummary { + slug: string + title: string + description: string + category: string + date: string +} + +export const integrationGuides: IntegrationGuideSummary[] = ${JSON.stringify(guides, null, 2)} +` + +fs.writeFileSync(OUTPUT_PATH, output, 'utf-8') +console.log(`Generated ${guides.length} integration guides → lib/integration-guides.gen.ts`)