Adds gtag.js to the production head so Search Console can cross-reference
GA traffic. Same gating as Umami — production builds only, never injected
on dev. Three head entries:
1. Umami (kept) — self-hosted, DNT-aware, GDPR-light
2. Google's gtag loader (async)
3. Inline init: window.dataLayer + gtag('config', 'G-V8QWB45G6X')
Both analytics run side-by-side; pick one or both in the GA dashboard.
A GDPR consent banner is not added — configure IP anonymisation +
consent mode v2 in the GA property settings if you intend to serve EU
visitors without explicit cookie acceptance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
413 lines
13 KiB
JavaScript
413 lines
13 KiB
JavaScript
// @ts-check
|
||
import { defineConfig } from 'astro/config';
|
||
import starlight from '@astrojs/starlight';
|
||
import sitemap from '@astrojs/sitemap';
|
||
import node from '@astrojs/node';
|
||
import AstroPWA from '@vite-pwa/astro';
|
||
|
||
export default defineConfig({
|
||
site: 'https://nibiru-framework.com',
|
||
output: 'server',
|
||
adapter: node({ mode: 'standalone' }),
|
||
// Disable Astro's dev-mode floating toolbar — it's the small dark bar with
|
||
// component-inspect controls. Useful for Astro debugging, not needed for
|
||
// reviewing the design.
|
||
devToolbar: { enabled: false },
|
||
redirects: {
|
||
'/': '/en/',
|
||
// Bing/Yandex/IndexNow probe /sitemap.xml directly. Astro's sitemap
|
||
// integration emits sitemap-index.xml + sitemap-0.xml; add a 301 so
|
||
// either path resolves. Both are also referenced in robots.txt.
|
||
'/sitemap.xml': '/sitemap-index.xml',
|
||
},
|
||
integrations: [
|
||
AstroPWA({
|
||
registerType: 'autoUpdate',
|
||
includeAssets: [
|
||
'favicon.svg',
|
||
'img/nibiru-logo.png',
|
||
'img/lifecycle.svg',
|
||
],
|
||
manifest: {
|
||
name: 'Nibiru — Modular MMVC PHP Framework',
|
||
short_name: 'Nibiru',
|
||
description:
|
||
'A modular MMVC PHP framework for builders who ship. First-class AI, the Atelier × Cosmos design system, on-brand docs.',
|
||
start_url: '/en/',
|
||
scope: '/',
|
||
display: 'standalone',
|
||
orientation: 'portrait-primary',
|
||
background_color: '#fdf6df',
|
||
theme_color: '#7c7bb8',
|
||
lang: 'en',
|
||
categories: ['developer-tools', 'productivity', 'education'],
|
||
icons: [
|
||
{ src: '/img/pwa-192.png', sizes: '192x192', type: 'image/png' },
|
||
{ src: '/img/pwa-512.png', sizes: '512x512', type: 'image/png' },
|
||
{
|
||
src: '/img/pwa-512-maskable.png',
|
||
sizes: '512x512',
|
||
type: 'image/png',
|
||
purpose: 'maskable',
|
||
},
|
||
],
|
||
shortcuts: [
|
||
{
|
||
name: 'Quick Start',
|
||
short_name: 'Start',
|
||
url: '/en/start/quick-start/',
|
||
description: 'Build your first Nibiru page in five minutes.',
|
||
},
|
||
{
|
||
name: 'AI module',
|
||
short_name: 'AI',
|
||
url: '/en/ai/module/overview/',
|
||
description: 'First-class AI in your Nibiru app.',
|
||
},
|
||
{
|
||
name: 'Why Nibiru',
|
||
short_name: 'Why',
|
||
url: '/en/why-nibiru/',
|
||
description: 'Differentiators with code references.',
|
||
},
|
||
],
|
||
},
|
||
workbox: {
|
||
navigateFallback: '/en/',
|
||
// SSR /api/* endpoints — never cache, always go to network.
|
||
navigateFallbackDenylist: [/^\/api\//],
|
||
// Don't precache the heavy hero illustrations — they're served
|
||
// from network and runtime-cached after first view.
|
||
globPatterns: [
|
||
'**/*.{html,css,js,svg,woff2,json,xml}',
|
||
'img/pwa-*.png',
|
||
'img/apple-touch-icon.png',
|
||
'img/nibiru-*.png',
|
||
'img/lotus-divider.png',
|
||
'img/showcase-*.png',
|
||
'img/hero-backdrop.png',
|
||
],
|
||
maximumFileSizeToCacheInBytes: 4 * 1024 * 1024,
|
||
runtimeCaching: [
|
||
{
|
||
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
||
handler: 'StaleWhileRevalidate',
|
||
options: { cacheName: 'google-fonts-stylesheets' },
|
||
},
|
||
{
|
||
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
||
handler: 'CacheFirst',
|
||
options: {
|
||
cacheName: 'google-fonts-webfonts',
|
||
expiration: { maxEntries: 16, maxAgeSeconds: 60 * 60 * 24 * 365 },
|
||
},
|
||
},
|
||
{
|
||
urlPattern: /\/img\/.*\.(png|svg|jpg|jpeg|webp)$/i,
|
||
handler: 'CacheFirst',
|
||
options: {
|
||
cacheName: 'nibiru-images',
|
||
expiration: { maxEntries: 60, maxAgeSeconds: 60 * 60 * 24 * 30 },
|
||
},
|
||
},
|
||
],
|
||
},
|
||
devOptions: {
|
||
enabled: false, // PWA runs only in production builds
|
||
navigateFallback: '/en/',
|
||
},
|
||
}),
|
||
sitemap({
|
||
i18n: {
|
||
defaultLocale: 'en',
|
||
locales: { en: 'en', de: 'de', ja: 'ja', es: 'es', fr: 'fr' },
|
||
},
|
||
filter: (page) => !page.includes('/api/'),
|
||
}),
|
||
starlight({
|
||
title: 'Nibiru',
|
||
description:
|
||
'A modular MMVC PHP framework for rapid prototyping. Born among the old gods, built for modern builders.',
|
||
// Analytics — production-only.
|
||
// 1. Umami (self-hosted) — cookie-free, honours Do-Not-Track,
|
||
// no PII captured, no GDPR banner required.
|
||
// 2. Google Analytics 4 (gtag.js) — added for Search Console
|
||
// cross-linking + audience reach. Subject to GDPR; configure
|
||
// consent / IP-anonymisation in the GA property settings.
|
||
head: import.meta.env.PROD
|
||
? [
|
||
{
|
||
tag: 'script',
|
||
attrs: {
|
||
defer: true,
|
||
src: 'https://analytics.neuronetz.ai/script.js',
|
||
'data-website-id': '5f2143d2-775b-4269-afcd-2639381add47',
|
||
'data-do-not-track': 'true',
|
||
'data-exclude-search': 'true',
|
||
},
|
||
},
|
||
{
|
||
tag: 'script',
|
||
attrs: {
|
||
async: true,
|
||
src: 'https://www.googletagmanager.com/gtag/js?id=G-V8QWB45G6X',
|
||
},
|
||
},
|
||
{
|
||
tag: 'script',
|
||
content:
|
||
"window.dataLayer = window.dataLayer || [];\n" +
|
||
"function gtag(){dataLayer.push(arguments);}\n" +
|
||
"gtag('js', new Date());\n" +
|
||
"gtag('config', 'G-V8QWB45G6X');",
|
||
},
|
||
]
|
||
: [],
|
||
logo: {
|
||
src: './public/img/nibiru-logo.png',
|
||
replacesTitle: true,
|
||
alt: 'Nibiru — Create, Invent, Impress',
|
||
},
|
||
favicon: '/favicon.svg',
|
||
editLink: { baseUrl: 'https://github.com/alllinux/Nibiru/edit/master/docs/' },
|
||
lastUpdated: true,
|
||
defaultLocale: 'en',
|
||
locales: {
|
||
en: { label: 'English', lang: 'en' },
|
||
de: { label: 'Deutsch', lang: 'de' },
|
||
ja: { label: '日本語', lang: 'ja' },
|
||
es: { label: 'Español', lang: 'es' },
|
||
fr: { label: 'Français', lang: 'fr' },
|
||
},
|
||
customCss: [
|
||
// 1. Brand tokens (--nibiru-*). Mirrored from /design-system/ by
|
||
// scripts/sync-design-system.mjs on every dev/build.
|
||
'./src/styles/design-system/tokens.css',
|
||
// 2. Docs-system tokens — verbatim from the design handoff. Defines
|
||
// --space, --space-2/3, --plum, --star-soft, --nebula-cyan/rose/green,
|
||
// --note-fg, --r-md, --fs-base, etc. Must come BEFORE components.css
|
||
// and nibiru.css since both consume these vars.
|
||
'./src/styles/docs-system/tokens.css',
|
||
// 3. Docs-system components — verbatim from the handoff. Provides the
|
||
// .topnav / .sidebar / .toc / .prose / .code-block / .callout-* /
|
||
// .doc-table / .tab-* / .api-* / .card / .search-modal / .fab styles.
|
||
'./src/styles/docs-system/components.css',
|
||
// 4. Fonts (Starlight needs --sl-font set before its own theme runs)
|
||
'./src/styles/fonts.css',
|
||
// 5. Site-specific styles + Starlight overrides (splash sections,
|
||
// legacy aliases, oracle launcher, atelier-button shims).
|
||
'./src/styles/nibiru.css',
|
||
// 6. Starlight × handoff bridge — translates Starlight's actual class
|
||
// names (.top-level, a[aria-current='page'], starlight-toc,
|
||
// .sl-markdown-content, .pagination-links, .starlight-aside-*)
|
||
// into the handoff's visual rules. Loaded LAST so it can win the
|
||
// cascade over both Starlight's defaults and earlier styles.
|
||
'./src/styles/starlight-docs-bridge.css',
|
||
],
|
||
components: {
|
||
Hero: './src/components/GalaxyHero.astro',
|
||
Header: './src/components/BrandHeader.astro',
|
||
// Article header — emits .breadcrumbs / <h1><em> / .doc-lede / .doc-meta
|
||
PageTitle: './src/components/PageTitle.astro',
|
||
// Article footer — Pagination + .help-strip ("Was this page helpful?")
|
||
Footer: './src/components/Footer.astro',
|
||
// Lock to dark — Cosmos is dark-only by design.
|
||
ThemeSelect: './src/components/ThemeSelectStub.astro',
|
||
},
|
||
expressiveCode: {
|
||
themes: ['github-light', 'github-dark'],
|
||
shiki: {
|
||
langAlias: {
|
||
smarty: 'html',
|
||
tpl: 'html',
|
||
},
|
||
},
|
||
styleOverrides: {
|
||
borderRadius: '0.75rem',
|
||
frames: {
|
||
shadowColor: 'rgba(124, 123, 184, 0.28)',
|
||
},
|
||
},
|
||
},
|
||
social: [
|
||
{
|
||
icon: 'github',
|
||
label: 'GitHub',
|
||
href: 'https://github.com/alllinux/Nibiru',
|
||
},
|
||
],
|
||
sidebar: [
|
||
{
|
||
label: 'Get started',
|
||
translations: {
|
||
de: 'Erste Schritte',
|
||
ja: 'はじめに',
|
||
es: 'Primeros pasos',
|
||
fr: 'Premiers pas',
|
||
},
|
||
items: [
|
||
{
|
||
label: 'What is Nibiru?',
|
||
slug: 'start/what-is-nibiru',
|
||
translations: {
|
||
de: 'Was ist Nibiru?',
|
||
ja: 'Nibiru とは?',
|
||
es: '¿Qué es Nibiru?',
|
||
fr: 'Qu\'est-ce que Nibiru ?',
|
||
},
|
||
},
|
||
{
|
||
label: 'Why Nibiru, not Laravel',
|
||
slug: 'why-nibiru',
|
||
translations: {
|
||
de: 'Warum Nibiru, nicht Laravel',
|
||
ja: 'なぜ Nibiru なのか',
|
||
es: '¿Por qué Nibiru y no Laravel?',
|
||
fr: 'Pourquoi Nibiru, pas Laravel',
|
||
},
|
||
},
|
||
{
|
||
label: 'Installation',
|
||
slug: 'start/installation',
|
||
translations: {
|
||
de: 'Installation',
|
||
ja: 'インストール',
|
||
es: 'Instalación',
|
||
fr: 'Installation',
|
||
},
|
||
},
|
||
{
|
||
label: 'Quick Start',
|
||
slug: 'start/quick-start',
|
||
translations: {
|
||
de: 'Schnellstart',
|
||
ja: 'クイックスタート',
|
||
es: 'Inicio rápido',
|
||
fr: 'Démarrage rapide',
|
||
},
|
||
},
|
||
{
|
||
label: 'Project Structure',
|
||
slug: 'start/structure',
|
||
translations: {
|
||
de: 'Projektstruktur',
|
||
ja: 'プロジェクト構成',
|
||
es: 'Estructura del proyecto',
|
||
fr: 'Structure du projet',
|
||
},
|
||
},
|
||
{
|
||
label: 'Run It Locally',
|
||
slug: 'start/local-testing',
|
||
translations: {
|
||
de: 'Lokal ausführen',
|
||
ja: 'ローカル実行',
|
||
es: 'Ejecución local',
|
||
fr: 'Lancer en local',
|
||
},
|
||
},
|
||
{
|
||
label: 'Deployment',
|
||
slug: 'start/deployment',
|
||
translations: {
|
||
de: 'Deployment',
|
||
ja: 'デプロイメント',
|
||
es: 'Despliegue',
|
||
fr: 'Déploiement',
|
||
},
|
||
},
|
||
],
|
||
},
|
||
{
|
||
label: 'The framework',
|
||
translations: {
|
||
de: 'Das Framework',
|
||
ja: 'フレームワーク',
|
||
es: 'El framework',
|
||
fr: 'Le framework',
|
||
},
|
||
items: [
|
||
{ label: 'Architecture (MMVC)', slug: 'core/architecture' },
|
||
{ label: 'Bootstrap & Dispatcher', slug: 'core/dispatcher' },
|
||
{ label: 'Routing', slug: 'core/routing' },
|
||
{ label: 'Controllers', slug: 'core/controllers' },
|
||
{ label: 'Views & Smarty', slug: 'core/views' },
|
||
{ label: 'Models', slug: 'core/models' },
|
||
{ label: 'Modules', slug: 'core/modules' },
|
||
{ label: 'Forms', slug: 'core/forms' },
|
||
{ label: 'Database & Migrations', slug: 'core/database' },
|
||
{ label: 'Auth', slug: 'core/auth' },
|
||
{ label: 'Config & Settings', slug: 'core/config' },
|
||
{ label: 'Pagination', slug: 'core/pagination' },
|
||
{ label: 'Registry', slug: 'core/registry' },
|
||
],
|
||
},
|
||
{
|
||
label: 'CLI',
|
||
translations: {
|
||
de: 'CLI',
|
||
ja: 'CLI',
|
||
es: 'CLI',
|
||
fr: 'CLI',
|
||
},
|
||
items: [
|
||
{ label: 'Overview', slug: 'cli/overview' },
|
||
{ label: 'Modules & Controllers', slug: 'cli/scaffolding' },
|
||
{ label: 'Migrations', slug: 'cli/migrations' },
|
||
{ label: 'CMS Pages', slug: 'cli/cms' },
|
||
],
|
||
},
|
||
{
|
||
label: 'Design System',
|
||
translations: {
|
||
de: 'Design-System',
|
||
ja: 'デザインシステム',
|
||
es: 'Sistema de diseño',
|
||
fr: 'Système de design',
|
||
},
|
||
items: [
|
||
{ label: 'Overview', slug: 'design/overview' },
|
||
{ label: 'Palette', slug: 'design/palette' },
|
||
{ label: 'Typography', slug: 'design/typography' },
|
||
{ label: 'Components', slug: 'design/components' },
|
||
{ label: 'Motion', slug: 'design/motion' },
|
||
],
|
||
},
|
||
{
|
||
label: 'In production',
|
||
translations: {
|
||
de: 'Im Einsatz',
|
||
ja: '実例',
|
||
es: 'En producción',
|
||
fr: 'En production',
|
||
},
|
||
items: [
|
||
{ label: 'Real-World Projects', slug: 'showcase/projects' },
|
||
{ label: 'Patterns from Production', slug: 'showcase/patterns' },
|
||
],
|
||
},
|
||
{
|
||
label: 'AI in Nibiru',
|
||
translations: {
|
||
de: 'KI in Nibiru',
|
||
ja: 'Nibiru の AI',
|
||
es: 'IA en Nibiru',
|
||
fr: "L'IA dans Nibiru",
|
||
},
|
||
items: [
|
||
{ label: 'The AI module', slug: 'ai/module/overview' },
|
||
{ label: 'Chat plugin', slug: 'ai/module/chat' },
|
||
{ label: 'Embed plugin', slug: 'ai/module/embed' },
|
||
{ label: 'RAG plugin', slug: 'ai/module/rag' },
|
||
{ label: 'Agent plugin', slug: 'ai/module/agent' },
|
||
{ label: 'Training nibiru-coder', slug: 'ai/module/training' },
|
||
{ label: 'Ask the Oracle', slug: 'ai/oracle' },
|
||
{ label: 'Training corpus (LoRA)', slug: 'ai/corpus' },
|
||
{ label: 'Roadmap', slug: 'ai/roadmap' },
|
||
],
|
||
},
|
||
],
|
||
}),
|
||
],
|
||
});
|