Files
nibiru-framework.com/docs/design-system/source/docs-system/components/navigation.jsx
stephan 48c839d927 Initial public push: docs cosmos v4 + AI module + framework groundwork
This is the snapshot the production landing site (nibiru-framework.com) is
deployed from. Brings together the recent splash + docs migration to the v4
"Cosmos" design system, the new in-framework AI module, and the framework
groundwork that backs the framework-reference extraction.

What lands:
- docs/: Astro + Starlight site with the v4 dark cosmic palette, GalaxyHero
  canvas constellation, Mission Control chat (wired to /api/oracle →
  api.neuronetz.ai via providers.mjs Ollama), 5-panel MMVC stage
  (Model · AI · Module · Controller · View), translated EN/DE/JA/ES/FR
  content, PWA + sitemap + llms.txt + Umami analytics.
- docs/design-system/: canonical mockup bundle (source/index-v2.html for
  splash, source/docs-system.html + preview/ for docs, SPEC.md, tokens).
- docs/scripts/extraction/framework-reference-v2.md: deep framework
  reference (~1.6k lines, file:line citations, every public factory and
  idiom — basis for the LoRA training corpus.
- application/module/ai/: AI module with chat / embed / RAG / agent
  plugins, plus pdoQuery / httpGet / fileRead tools and Modelfile +
  smoke-test in training/.
- application/module/users/: user / ACL / form-factory traits used as the
  reference plugin pattern for the framework docs.
- application/settings/config/database/: schema + seed migrations
  including the AI module tables (200–203).
- Form factory + autogenerator changes the framework-reference-v2 covers.

Production secrets stay out: docs/.env, settings.production.ini and
ai.production.ini are all gitignored (.example files are in tree).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 15:22:18 +02:00

193 lines
7.2 KiB
JavaScript

/* global React */
const { useState } = React;
// ============================================================
// TOP NAV
// ============================================================
function TopNav({ theme = "dark", onToggleTheme = () => {}, locale = "EN" }) {
return (
<header className="topnav">
<div className="topnav-brand">
<LotusMark />
<div className="topnav-brand-text">
<div className="topnav-brand-name">Nibiru</div>
<div className="topnav-brand-tag">Create, Invent, Impress</div>
</div>
</div>
<button className="topnav-search" aria-label="Search docs">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="11" cy="11" r="8"/>
<path d="m21 21-4.35-4.35"/>
</svg>
<span>Search</span>
<kbd> K</kbd>
</button>
<div className="topnav-right">
<a href="#" className="topnav-icon" aria-label="GitHub">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-1.97c-3.2.69-3.87-1.54-3.87-1.54-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.02 1.75 2.68 1.25 3.34.96.1-.74.4-1.25.72-1.54-2.55-.29-5.24-1.28-5.24-5.69 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.04 0 0 .96-.31 3.15 1.18a10.95 10.95 0 0 1 5.74 0c2.18-1.49 3.14-1.18 3.14-1.18.62 1.58.23 2.75.11 3.04.74.81 1.18 1.84 1.18 3.1 0 4.42-2.69 5.39-5.26 5.68.41.36.78 1.06.78 2.13v3.16c0 .31.21.67.8.56C20.21 21.39 23.5 17.08 23.5 12 23.5 5.65 18.35.5 12 .5Z"/>
</svg>
</a>
<button className="topnav-icon" aria-label="Toggle theme" onClick={onToggleTheme}>
{theme === "dark" ? (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
) : (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="4"/>
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
</svg>
)}
</button>
<button className="topnav-locale" aria-label="Locale">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="10"/>
<path d="M2 12h20M12 2a15 15 0 0 1 0 20M12 2a15 15 0 0 0 0 20"/>
</svg>
<span>{locale}</span>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<path d="m6 9 6 6 6-6"/>
</svg>
</button>
</div>
</header>
);
}
function LotusMark({ size = 36 }) {
// Real Nibiru logo, sourced from window.NIBIRU_LOTUS so the bundler picks it up
// (it would miss a relative path inside a JSX template string).
const src = (typeof window !== "undefined" && window.NIBIRU_LOTUS) || "docs-system/assets/lotus.png";
return (
<img
src={src}
width={size}
height={Math.round(size * 569 / 861)}
alt="Nibiru"
className="lotus-mark"
style={{ display: "block" }}
/>
);
}
// ============================================================
// SIDEBAR
// ============================================================
function Sidebar() {
const [openSections, setOpenSections] = useState({
"get-started": true, "framework": true, "cli": false, "advanced": false,
});
const toggle = (id) => setOpenSections((s) => ({ ...s, [id]: !s[id] }));
const nav = [
{
id: "get-started", label: "Get Started",
items: [
{ label: "What is Nibiru?", active: true },
{ label: "Why Nibiru, not Laravel" },
{ label: "Installation" },
{ label: "Quick Start" },
{ label: "Project Structure" },
{ label: "Run It Locally" },
{ label: "Deployment" },
],
},
{
id: "framework", label: "The Framework",
items: [
{ label: "Architecture (MMVC)" },
{ label: "Bootstrap & Dispatcher" },
{ label: "Routing" },
{ label: "Controllers" },
{ label: "Views & Smarty" },
{ label: "Models" },
{ label: "Modules", badge: "core" },
{ label: "Forms" },
{ label: "Database & Migrations" },
{ label: "Auth" },
{ label: "Config & Settings" },
{ label: "Pagination" },
{ label: "Registry" },
],
},
{
id: "cli", label: "CLI",
items: [
{ label: "nibiru new" },
{ label: "nibiru migrate" },
{ label: "nibiru make" },
],
},
{
id: "advanced", label: "Advanced",
items: [
{ label: "Plugins" },
{ label: "Observer pattern" },
{ label: "Caching", badge: "new" },
],
},
];
return (
<aside className="sidebar">
<nav>
{nav.map((section) => (
<div key={section.id} className="sidebar-section">
<button
className="sidebar-heading"
onClick={() => toggle(section.id)}
aria-expanded={openSections[section.id]}
>
<span>{section.label}</span>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"
style={{ transform: openSections[section.id] ? "rotate(0deg)" : "rotate(-90deg)", transition: "transform 200ms" }}>
<path d="m6 9 6 6 6-6"/>
</svg>
</button>
{openSections[section.id] && (
<ul className="sidebar-list">
{section.items.map((item) => (
<li key={item.label}>
<a href="#" className={"sidebar-item" + (item.active ? " active" : "")}>
<span>{item.label}</span>
{item.badge && <span className={"sidebar-badge sidebar-badge-" + item.badge}>{item.badge}</span>}
</a>
</li>
))}
</ul>
)}
</div>
))}
</nav>
</aside>
);
}
// ============================================================
// RIGHT TOC ("On this page")
// ============================================================
function RightTOC({ items, activeId }) {
return (
<aside className="toc">
<div className="toc-label">On this page</div>
<ul className="toc-list">
{items.map((item) => (
<li key={item.id} className={"toc-item toc-level-" + item.level}>
<a href={"#" + item.id} className={item.id === activeId ? "active" : ""}>
{item.label}
</a>
</li>
))}
</ul>
<div className="toc-edit">
<a href="#"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg> Edit this page</a>
</div>
</aside>
);
}
Object.assign(window, { TopNav, Sidebar, RightTOC, LotusMark });