Files
nibiru-framework.com/docs/design-system/docs-page-mockup.html
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

759 lines
24 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Documentation page · Nibiru — design mockup</title>
<meta name="description" content="Design mockup for a typical Nibiru documentation page. Layout, type, components and surfaces — built on the v4 Cosmos design system.">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#0a0414">
<!-- Type: Inter Tight (body) + Space Grotesk (display) + JetBrains Mono -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@200..800&family=Space+Grotesk:wght@300..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Canonical Nibiru tokens (also embedded inline below for portability) -->
<link rel="stylesheet" href="./tokens.css">
<style>
/* ============================================================================
* Documentation-page mockup — Nibiru v4 Cosmos
*
* Standalone HTML the designer can open in a browser and riff on. Mirrors
* the production stack (Astro + Starlight) at the visual layer: header on
* top, sidebar nav on the left, article in the middle, on-page TOC on
* the right. Pure CSS, no JS — open it with file:// and it just works.
*
* Hand off: the designer should treat this as the "before" baseline for
* doc pages. The startpage (separate file) sets the brand tone; this
* shows how that tone translates to a content-heavy reading layout.
* ========================================================================= */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
font-family: 'Inter Tight', ui-sans-serif, system-ui, sans-serif;
background: var(--nibiru-space, #0a0414);
background-image:
radial-gradient(ellipse 70% 50% at 0% 0%, rgba(184, 107, 255, 0.10), transparent 60%),
radial-gradient(ellipse 60% 40% at 100% 0%, rgba(91, 141, 255, 0.07), transparent 60%);
background-attachment: fixed;
color: var(--nibiru-star, #f4eedb);
font-size: 16px;
line-height: 1.55;
font-feature-settings: "ss01", "cv11";
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
::selection { background: rgba(184, 107, 255, 0.4); color: var(--nibiru-star, #f4eedb); }
.mono, code, kbd, pre {
font-family: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}
.display { font-family: 'Space Grotesk', 'Inter Tight', ui-sans-serif, sans-serif; }
/* ============== HEADER ============== */
.doc-header {
position: sticky; top: 0; z-index: 60;
height: 64px;
display: flex; align-items: center;
padding: 0 24px;
background: rgba(6, 3, 15, 0.72);
border-bottom: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
backdrop-filter: saturate(150%) blur(14px);
-webkit-backdrop-filter: saturate(150%) blur(14px);
}
.doc-header-row {
display: flex; align-items: center; justify-content: space-between;
width: 100%;
gap: 24px;
}
.brand {
display: flex; align-items: center; gap: 10px; flex: none;
font-family: 'Space Grotesk', sans-serif;
}
.brand-mark {
width: 28px; height: 28px;
border-radius: 50%;
background:
radial-gradient(circle at 35% 35%, #f4eedb 0%, #b86bff 40%, #5b8dff 80%, #1c0f3a 100%);
box-shadow: 0 0 12px rgba(184, 107, 255, 0.5);
}
.brand-name {
font-size: 18px; font-weight: 500; letter-spacing: -0.02em;
color: var(--nibiru-star, #f4eedb);
}
.brand-name em { font-style: normal; font-weight: 300; }
.nav-version {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.04em;
color: var(--nibiru-muted, #6e6680);
padding: 3px 7px;
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 999px;
margin-left: 6px;
display: inline-flex; align-items: center; gap: 6px;
}
.nav-version .dot {
width: 5px; height: 5px; border-radius: 50%;
background: #7ad6a3; box-shadow: 0 0 8px rgba(122, 214, 163, 0.7);
}
.doc-nav {
display: flex; gap: 24px; align-items: center;
flex: 1; justify-content: center;
}
.doc-nav a {
font-size: 14px;
color: rgba(244, 238, 219, 0.7);
transition: color 160ms ease;
}
.doc-nav a:hover, .doc-nav a[aria-current="page"] {
color: var(--nibiru-star, #f4eedb);
}
.search-inline {
display: flex; align-items: center; gap: 10px;
min-width: 220px;
height: 36px;
padding: 0 12px;
background: rgba(244, 238, 219, 0.04);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 999px;
color: rgba(244, 238, 219, 0.55);
font-size: 13px;
transition: border-color 160ms ease, background-color 160ms ease;
}
.search-inline:hover { border-color: var(--nibiru-line-strong, rgba(244, 238, 219, 0.28)); }
.search-inline svg { width: 14px; height: 14px; flex: none; }
.search-inline kbd {
margin-left: auto;
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
color: var(--nibiru-muted, #6e6680);
padding: 2px 6px;
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 4px;
background: rgba(244, 238, 219, 0.03);
}
.lang-switcher {
display: flex; gap: 4px;
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
}
.lang-switcher a { padding: 4px 8px; border-radius: 6px; }
.lang-switcher a[aria-current="true"] {
color: var(--nibiru-star, #f4eedb);
background: rgba(184, 107, 255, 0.12);
}
/* ============== LAYOUT SHELL ============== */
.doc-shell {
display: grid;
grid-template-columns: 280px minmax(0, 1fr) 240px;
gap: 0;
max-width: 1400px;
margin: 0 auto;
min-height: calc(100vh - 64px);
}
@media (max-width: 1100px) {
.doc-shell { grid-template-columns: 240px 1fr; }
.doc-toc { display: none; }
}
@media (max-width: 768px) {
.doc-shell { grid-template-columns: 1fr; }
.doc-sidebar { display: none; }
}
/* ============== SIDEBAR ============== */
.doc-sidebar {
position: sticky; top: 64px;
height: calc(100vh - 64px);
overflow-y: auto;
padding: 28px 16px 28px 24px;
border-right: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
background: rgba(10, 4, 20, 0.4);
backdrop-filter: blur(8px);
font-size: 14px;
scrollbar-width: thin;
scrollbar-color: rgba(184, 107, 255, 0.3) transparent;
}
.doc-sidebar h3 {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
margin: 22px 0 8px;
font-weight: 500;
}
.doc-sidebar h3:first-child { margin-top: 0; }
.doc-sidebar ul {
list-style: none; padding: 0; margin: 0;
display: flex; flex-direction: column; gap: 1px;
}
.doc-sidebar a {
display: block;
padding: 7px 12px;
color: rgba(244, 238, 219, 0.7);
border-left: 1.5px solid transparent;
border-radius: 0 8px 8px 0;
transition: color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.doc-sidebar a:hover {
color: var(--nibiru-star, #f4eedb);
border-left-color: var(--nibiru-iris-soft, #d4b4ff);
background: rgba(184, 107, 255, 0.06);
}
.doc-sidebar a[aria-current="page"] {
color: var(--nibiru-star, #f4eedb);
font-weight: 500;
border-left-color: var(--nibiru-nebula-mag, #b86bff);
background: rgba(184, 107, 255, 0.10);
}
/* ============== ARTICLE ============== */
.doc-article {
min-width: 0;
padding: 56px 64px 120px;
max-width: 800px;
margin: 0 auto;
}
@media (max-width: 768px) {
.doc-article { padding: 32px 24px 80px; }
}
.breadcrumbs {
display: flex; align-items: center; gap: 8px;
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
color: var(--nibiru-muted, #6e6680);
margin-bottom: 18px;
flex-wrap: wrap;
}
.breadcrumbs a:hover { color: var(--nibiru-star, #f4eedb); }
.breadcrumbs .sep { color: var(--nibiru-line-strong, rgba(244, 238, 219, 0.28)); }
.doc-article h1 {
font-family: 'Space Grotesk', sans-serif;
font-size: clamp(2rem, 1.5rem + 1.6vw, 2.75rem);
line-height: 1.05;
letter-spacing: -0.04em;
font-weight: 400;
margin: 0 0 18px;
color: var(--nibiru-star, #f4eedb);
}
.doc-article h1 em {
font-style: normal; font-weight: 500;
background: linear-gradient(110deg, #ffb574 0%, #b86bff 70%);
-webkit-background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
.doc-lede {
font-size: 1.18rem;
line-height: 1.55;
color: rgba(244, 238, 219, 0.72);
max-width: 56ch;
margin: 0 0 36px;
}
.doc-meta {
display: flex; gap: 28px; align-items: center;
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
padding-bottom: 28px;
margin-bottom: 36px;
border-bottom: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
flex-wrap: wrap;
}
.doc-meta strong { color: rgba(244, 238, 219, 0.85); font-weight: 500; }
.doc-meta .pulse {
width: 6px; height: 6px; border-radius: 50%;
background: #7ad6a3;
box-shadow: 0 0 8px rgba(122, 214, 163, 0.7);
}
.doc-article h2 {
font-family: 'Space Grotesk', sans-serif;
font-size: 1.6rem;
font-weight: 500;
letter-spacing: -0.025em;
line-height: 1.15;
margin: 56px 0 16px;
padding-top: 24px;
border-top: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
color: var(--nibiru-star, #f4eedb);
}
.doc-article h3 {
font-family: 'Space Grotesk', sans-serif;
font-size: 1.18rem;
font-weight: 500;
letter-spacing: -0.02em;
margin: 36px 0 12px;
color: var(--nibiru-iris-soft, #d4b4ff);
}
.doc-article h4 {
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
margin: 28px 0 10px;
font-weight: 500;
}
.doc-article p {
color: rgba(244, 238, 219, 0.85);
margin: 0 0 18px;
line-height: 1.65;
}
.doc-article strong { color: var(--nibiru-star, #f4eedb); font-weight: 600; }
.doc-article a {
color: var(--nibiru-iris-soft, #d4b4ff);
border-bottom: 1px solid rgba(184, 107, 255, 0.4);
transition: color 200ms ease, border-color 200ms ease;
}
.doc-article a:hover {
color: var(--nibiru-star, #f4eedb);
border-bottom-color: var(--nibiru-nebula-mag, #b86bff);
}
.doc-article ul, .doc-article ol {
margin: 0 0 18px; padding-left: 24px;
}
.doc-article li { margin: 6px 0; color: rgba(244, 238, 219, 0.85); }
/* ============== INLINE CODE ============== */
.doc-article :not(pre) > code {
font-family: 'JetBrains Mono', monospace;
font-size: 0.86em;
background: rgba(184, 107, 255, 0.14);
color: var(--nibiru-iris-soft, #d4b4ff);
padding: 0.05em 0.4em;
border-radius: 6px;
border: 1px solid rgba(184, 107, 255, 0.20);
letter-spacing: 0.04em;
}
/* ============== CODE BLOCK ============== */
.code-block {
background: var(--nibiru-code-bg, #050208);
border: 1px solid var(--nibiru-line-strong, rgba(244, 238, 219, 0.28));
border-radius: 14px;
overflow: hidden;
margin: 22px 0;
box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.7);
}
.code-block-head {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 16px;
border-bottom: 1px solid rgba(244, 238, 219, 0.08);
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em; text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
}
.code-block-head .file { color: var(--nibiru-nebula-mag, #b86bff); }
.code-block-head .copy {
background: transparent;
border: 1px solid rgba(244, 238, 219, 0.12);
color: rgba(244, 238, 219, 0.7);
padding: 4px 10px;
border-radius: 6px;
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em;
cursor: pointer;
}
.code-block-head .copy:hover { border-color: var(--nibiru-iris-soft, #d4b4ff); color: var(--nibiru-star, #f4eedb); }
.code-block pre {
margin: 0; padding: 18px 20px;
font-family: 'JetBrains Mono', monospace;
font-size: 13px; line-height: 1.7;
color: var(--nibiru-star, #f4eedb);
overflow-x: auto;
}
.tk-c { color: #6e6680; font-style: italic; }
.tk-k { color: #c8a8ff; }
.tk-cls { color: #f0d4ff; }
.tk-fn { color: #b8d4ff; }
.tk-s { color: #ffd9a3; }
.tk-v { color: #ffb4d8; }
.tk-attr { color: #d4baff; }
.tk-n { color: #b8e0c2; }
/* ============== CALLOUT / ASIDE ============== */
.callout {
background: rgba(184, 107, 255, 0.06);
border: 1px solid rgba(244, 238, 219, 0.10);
border-left: 3px solid var(--nibiru-nebula-mag, #b86bff);
padding: 14px 16px 14px 18px;
border-radius: 0 10px 10px 0;
margin: 22px 0;
}
.callout--tip { border-left-color: var(--nibiru-nebula-amber, #ffb574); background: rgba(255, 181, 116, 0.06); }
.callout--danger { border-left-color: #ff8a9d; background: rgba(255, 138, 157, 0.06); }
.callout-title {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em; text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
margin-bottom: 6px;
font-weight: 500;
}
.callout p { margin: 0; }
/* ============== TABLE ============== */
.doc-article table {
width: 100%;
border-collapse: collapse;
margin: 22px 0;
font-size: 14px;
}
.doc-article thead {
border-bottom: 2px solid var(--nibiru-line-strong, rgba(244, 238, 219, 0.28));
}
.doc-article th {
text-align: left;
padding: 10px 12px 10px 0;
font-family: 'JetBrains Mono', monospace;
font-weight: 500;
font-size: 11px;
letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
}
.doc-article td {
padding: 10px 12px 10px 0;
border-bottom: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
color: rgba(244, 238, 219, 0.85);
vertical-align: top;
}
/* ============== ON-PAGE TOC (right rail) ============== */
.doc-toc {
position: sticky; top: 64px;
height: calc(100vh - 64px);
overflow-y: auto;
padding: 56px 24px 56px 16px;
border-left: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
font-size: 13px;
}
.doc-toc h4 {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
margin: 0 0 12px;
font-weight: 500;
}
.doc-toc ul {
list-style: none; padding: 0; margin: 0;
display: flex; flex-direction: column; gap: 4px;
}
.doc-toc a {
color: rgba(244, 238, 219, 0.6);
display: block;
padding: 4px 0;
transition: color 160ms ease;
line-height: 1.4;
}
.doc-toc a:hover { color: var(--nibiru-star, #f4eedb); }
.doc-toc a.active {
color: var(--nibiru-star, #f4eedb);
border-left: 2px solid var(--nibiru-nebula-mag, #b86bff);
margin-left: -10px;
padding-left: 8px;
}
.doc-toc .l2 { padding-left: 12px; font-size: 12px; }
/* ============== PAGE FOOTER (prev / next) ============== */
.page-pagination {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-top: 80px;
padding-top: 36px;
border-top: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
}
@media (max-width: 600px) {
.page-pagination { grid-template-columns: 1fr; }
}
.page-pagination a {
background: var(--nibiru-night, #120825);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 14px;
padding: 16px 18px;
transition: border-color 200ms, transform 200ms;
}
.page-pagination a:hover {
border-color: var(--nibiru-nebula-mag, #b86bff);
transform: translateY(-1px);
}
.page-pagination .lbl {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; letter-spacing: 0.10em;
text-transform: uppercase;
color: var(--nibiru-muted, #6e6680);
display: block;
margin-bottom: 4px;
}
.page-pagination .ttl {
font-size: 15px; font-weight: 500;
color: var(--nibiru-star, #f4eedb);
}
.page-pagination .next { text-align: right; }
/* ============== HELP STRIP ============== */
.help-strip {
margin-top: 48px;
padding: 18px 22px;
background: rgba(244, 238, 219, 0.03);
border: 1px solid var(--nibiru-line, rgba(244, 238, 219, 0.12));
border-radius: 12px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
font-size: 14px;
}
.help-strip-text { color: rgba(244, 238, 219, 0.75); }
.help-strip-actions { display: flex; gap: 8px; }
.help-strip-actions a {
padding: 7px 14px;
border: 1px solid var(--nibiru-line-strong, rgba(244, 238, 219, 0.28));
border-radius: 999px;
font-size: 13px;
color: rgba(244, 238, 219, 0.85);
transition: color 160ms, border-color 160ms;
border-bottom: 1px solid var(--nibiru-line-strong, rgba(244, 238, 219, 0.28));
}
.help-strip-actions a:hover {
color: var(--nibiru-star, #f4eedb);
border-color: var(--nibiru-iris-soft, #d4b4ff);
}
</style>
</head>
<body>
<!-- ====================== HEADER ====================== -->
<header class="doc-header">
<div class="doc-header-row">
<a class="brand" href="#">
<span class="brand-mark"></span>
<span class="brand-name">Nibiru<em> docs</em></span>
<span class="nav-version"><span class="dot"></span>v0.9.2</span>
</a>
<nav class="doc-nav" aria-label="Primary">
<a href="#" aria-current="page">Docs</a>
<a href="#">MMVC</a>
<a href="#">AI module</a>
<a href="#">CLI</a>
<a href="#">Showcase</a>
</nav>
<div class="doc-header-tools" style="display: flex; align-items: center; gap: 12px;">
<div class="search-inline" role="search">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-3-3"/></svg>
<span>Search the docs</span>
<kbd>⌘ K</kbd>
</div>
<div class="lang-switcher" aria-label="Language">
<a href="#" aria-current="true">EN</a>
<a href="#">DE</a>
<a href="#">JA</a>
<a href="#">ES</a>
<a href="#">FR</a>
</div>
</div>
</div>
</header>
<!-- ====================== SHELL ====================== -->
<div class="doc-shell">
<!-- ====== SIDEBAR ====== -->
<aside class="doc-sidebar" aria-label="Section navigation">
<h3>Get started</h3>
<ul>
<li><a href="#">What is Nibiru?</a></li>
<li><a href="#">Installation</a></li>
<li><a href="#">Quick start</a></li>
<li><a href="#" aria-current="page">Project structure</a></li>
<li><a href="#">Run it locally</a></li>
<li><a href="#">Deployment</a></li>
</ul>
<h3>The framework</h3>
<ul>
<li><a href="#">Architecture (MMVC)</a></li>
<li><a href="#">Bootstrap &amp; Dispatcher</a></li>
<li><a href="#">Routing</a></li>
<li><a href="#">Controllers</a></li>
<li><a href="#">Views &amp; Smarty</a></li>
<li><a href="#">Models</a></li>
<li><a href="#">Modules</a></li>
<li><a href="#">Forms</a></li>
<li><a href="#">Database &amp; Migrations</a></li>
<li><a href="#">Auth</a></li>
</ul>
<h3>AI in Nibiru</h3>
<ul>
<li><a href="#">The AI module</a></li>
<li><a href="#">Chat plugin</a></li>
<li><a href="#">Embed plugin</a></li>
<li><a href="#">RAG plugin</a></li>
<li><a href="#">Agent plugin</a></li>
<li><a href="#">Ask the Oracle</a></li>
</ul>
</aside>
<!-- ====== ARTICLE ====== -->
<main class="doc-article">
<nav class="breadcrumbs" aria-label="Breadcrumb">
<a href="#">Docs</a>
<span class="sep">/</span>
<a href="#">Get started</a>
<span class="sep">/</span>
<span>Project structure</span>
</nav>
<h1>Project <em>structure</em></h1>
<p class="doc-lede">Where Nibiru puts things, and why. The shape on disk maps to the four MMVC roles — read top-down and the framework will explain itself.</p>
<div class="doc-meta">
<span><span class="pulse"></span> <strong>Stable</strong> · v0.9.2</span>
<span>Updated <strong>2 days ago</strong></span>
<span>Reading time <strong>~ 6 min</strong></span>
<span>Edit on <strong>GitHub</strong></span>
</div>
<p>A fresh Nibiru install is just a Composer-managed PHP project with a single CLI binary, an <code>application/</code> tree for your code, and a <code>core/</code> tree for the framework itself. Everything else — Smarty's compiled templates, the public docroot, the migrations history — falls out of those four conventions.</p>
<h2 id="overview">Overview</h2>
<p>Open the repo and you'll see this:</p>
<div class="code-block">
<div class="code-block-head">
<span class="file">tree.txt</span>
<button class="copy" type="button">Copy</button>
</div>
<pre><span class="tk-c"># A Nibiru project, top level</span>
my-app/
├── application/ <span class="tk-c">// your code lives here</span>
│ ├── controller/
│ ├── module/
│ ├── view/
│ └── settings/
├── core/ <span class="tk-c">// the framework — don't edit</span>
├── public/ <span class="tk-c">// docroot — point your vhost here</span>
├── nibiru <span class="tk-c">// the CLI binary</span>
├── composer.json
└── index.php <span class="tk-c">// front controller</span></pre>
</div>
<h3 id="application">application/</h3>
<p>Your code. Each role in <strong>MMVC</strong> is its own subdirectory: <code>controller/</code>, <code>module/</code>, <code>view/</code>. The fourth — Models — live inside their own <code>module/&lt;name&gt;/models/</code> subtrees, because in MMVC every module owns the data shape it touches.</p>
<div class="callout callout--tip">
<div class="callout-title">Tip</div>
<p>Run <code>./nibiru -c products</code> and the CLI will scaffold the controller, the module, the navigation entry and a Smarty template into the right places automatically.</p>
</div>
<h3 id="core">core/</h3>
<p>The framework itself. <strong>Don't edit it.</strong> If you find yourself reaching in here, the answer is almost always to extend a class in <code>application/</code> instead. The single exception: <a href="#">contributing back</a>.</p>
<h3 id="public">public/</h3>
<p>Your vhost docroot. Static assets are served directly from this directory; everything else falls through to the front controller via <code>index.php</code>. The <code>.htaccess</code> in the project root takes care of rewrites for Apache; the included <a href="#"><code>vhost.conf</code></a> shows the equivalent for nginx.</p>
<h2 id="naming">Naming conventions</h2>
<p>Every controller is <code>&lt;name&gt;Controller.php</code>, every module sits at <code>application/module/&lt;name&gt;/</code>, every Smarty template lives at <code>application/view/templates/&lt;name&gt;.tpl</code>. The CLI enforces this — you don't have to remember it.</p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Created by</th>
<th>Owns</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>application/controller/</code></td>
<td><code>./nibiru -c &lt;name&gt;</code></td>
<td>Routing, dispatch, lifecycle</td>
</tr>
<tr>
<td><code>application/module/</code></td>
<td><code>./nibiru -m &lt;name&gt;</code></td>
<td>Capabilities, plugins, models</td>
</tr>
<tr>
<td><code>application/view/templates/</code></td>
<td>scaffolded with controller</td>
<td>Smarty 3 templates</td>
</tr>
<tr>
<td><code>application/settings/migrations/</code></td>
<td><code>./nibiru -mi-add &lt;n&gt;</code></td>
<td>Timestamped SQL migrations</td>
</tr>
</tbody>
</table>
<div class="callout">
<div class="callout-title">Note</div>
<p>Smarty templates compile to <code>application/view/templates_c/</code> at first request. That directory is gitignored — clear it with <code>./nibiru -cache-clear</code> if a template ever sticks.</p>
</div>
<h2 id="next">Next</h2>
<p>You know where things go. Now wire one up — the <a href="#">Quick start</a> takes you from an empty project to a running controller in five commands.</p>
<!-- prev / next -->
<nav class="page-pagination" aria-label="Page navigation">
<a class="prev" href="#">
<span class="lbl">← Previous</span>
<span class="ttl">Quick start</span>
</a>
<a class="next" href="#">
<span class="lbl">Next →</span>
<span class="ttl">Run it locally</span>
</a>
</nav>
<!-- help strip -->
<aside class="help-strip">
<span class="help-strip-text">Was this page helpful?</span>
<div class="help-strip-actions">
<a href="#">Yes</a>
<a href="#">Suggest an edit</a>
</div>
</aside>
</main>
<!-- ====== ON-PAGE TOC ====== -->
<aside class="doc-toc" aria-label="On this page">
<h4>On this page</h4>
<ul>
<li><a href="#overview" class="active">Overview</a></li>
<li><a href="#application" class="l2">application/</a></li>
<li><a href="#core" class="l2">core/</a></li>
<li><a href="#public" class="l2">public/</a></li>
<li><a href="#naming">Naming conventions</a></li>
<li><a href="#next">Next</a></li>
</ul>
<h4 style="margin-top: 36px;">Notes</h4>
<p style="font-size: 12px; color: var(--nibiru-muted, #6e6680); line-height: 1.5;">
This is a design mockup, not a real docs page. The TOC scroll-spy, search modal,
and language-switcher are wired-up in the production Astro/Starlight build —
treat the visual elements here as the brief.
</p>
</aside>
</div>
</body>
</html>