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>
This commit is contained in:
291
docs/design-system/source/docs-system/components/extras.jsx
Normal file
291
docs/design-system/source/docs-system/components/extras.jsx
Normal file
@@ -0,0 +1,291 @@
|
||||
/* global React */
|
||||
const { useState: useEState } = React;
|
||||
|
||||
// ============================================================
|
||||
// TABS
|
||||
// ============================================================
|
||||
function Tabs({ tabs }) {
|
||||
const [active, setActive] = useEState(0);
|
||||
return (
|
||||
<div className="tabs">
|
||||
<div className="tabs-list" role="tablist">
|
||||
{tabs.map((t, i) => (
|
||||
<button
|
||||
key={i}
|
||||
role="tab"
|
||||
aria-selected={i === active}
|
||||
className={"tabs-trigger" + (i === active ? " active" : "")}
|
||||
onClick={() => setActive(i)}
|
||||
>
|
||||
{t.icon && <span className="tabs-icon">{t.icon}</span>}
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="tabs-panel">
|
||||
{tabs[active].content}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API BLOCK (parameter list)
|
||||
// ============================================================
|
||||
function ApiBlock({ signature, params = [], returns }) {
|
||||
return (
|
||||
<div className="api-block">
|
||||
<div className="api-signature">
|
||||
<span className="tk-fn">{signature.name}</span>
|
||||
<span className="api-paren">(</span>
|
||||
{signature.args.map((a, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<span className="tk-var">{a.name}</span>
|
||||
<span className="api-colon">: </span>
|
||||
<span className="tk-cn">{a.type}</span>
|
||||
{i < signature.args.length - 1 && <span className="api-comma">, </span>}
|
||||
</React.Fragment>
|
||||
))}
|
||||
<span className="api-paren">)</span>
|
||||
{signature.returns && (
|
||||
<>
|
||||
<span className="api-arrow"> → </span>
|
||||
<span className="tk-cn">{signature.returns}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="api-section-label">Parameters</div>
|
||||
<ul className="api-params">
|
||||
{params.map((p, i) => (
|
||||
<li key={i} className="api-param">
|
||||
<div className="api-param-head">
|
||||
<span className="api-param-name">{p.name}</span>
|
||||
<span className="api-param-type">{p.type}</span>
|
||||
{p.required && <span className="api-param-required">required</span>}
|
||||
{p.default !== undefined && <span className="api-param-default">default: <code>{p.default}</code></span>}
|
||||
</div>
|
||||
<div className="api-param-desc">{p.desc}</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{returns && (
|
||||
<>
|
||||
<div className="api-section-label">Returns</div>
|
||||
<div className="api-returns">
|
||||
<span className="tk-cn">{returns.type}</span>
|
||||
<span className="api-returns-desc"> — {returns.desc}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CARD GRID (feature cards)
|
||||
// ============================================================
|
||||
function CardGrid({ cards }) {
|
||||
return (
|
||||
<div className="card-grid">
|
||||
{cards.map((c, i) => (
|
||||
<a href="#" key={i} className="feature-card">
|
||||
<div className="feature-card-icon" style={{ background: c.glow }}>
|
||||
{c.icon}
|
||||
</div>
|
||||
<div className="feature-card-title">{c.title}</div>
|
||||
<div className="feature-card-desc">{c.desc}</div>
|
||||
<div className="feature-card-arrow">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M7 17 17 7M7 7h10v10"/>
|
||||
</svg>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SEARCH MODAL
|
||||
// ============================================================
|
||||
function SearchModal() {
|
||||
const recent = ["Routing", "Form::addInputType", "Migrations"];
|
||||
const results = [
|
||||
{ section: "The Framework", title: "Routing", excerpt: "URL-pattern + SEO-URL parsing, soft 404, automatic action lookup.", kind: "page" },
|
||||
{ section: "The Framework", title: "Routing › Soft 404", excerpt: "When a route doesn't match, the dispatcher falls through to a soft 404 view.", kind: "section" },
|
||||
{ section: "Get Started", title: "Quick Start › Define a route", excerpt: "Routes are declared in config/routes.php and matched by URL pattern.", kind: "section" },
|
||||
{ section: "API", title: "Router::register()", excerpt: "Register a new route at runtime. Supports pattern matching with named parameters.", kind: "api" },
|
||||
];
|
||||
return (
|
||||
<div className="search-modal">
|
||||
<div className="search-input-wrap">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="search-icon">
|
||||
<circle cx="11" cy="11" r="8"/>
|
||||
<path d="m21 21-4.35-4.35"/>
|
||||
</svg>
|
||||
<input className="search-input" placeholder="Search docs, API, examples..." defaultValue="rout" />
|
||||
<kbd className="search-kbd">esc</kbd>
|
||||
</div>
|
||||
<div className="search-recent">
|
||||
<div className="search-section-label">Recent</div>
|
||||
<div className="search-recent-list">
|
||||
{recent.map((r, i) => (
|
||||
<button key={i} className="search-chip">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
{r}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="search-results">
|
||||
<div className="search-section-label">Results</div>
|
||||
{results.map((r, i) => (
|
||||
<button key={i} className={"search-result" + (i === 0 ? " active" : "")}>
|
||||
<div className={"search-result-kind kind-" + r.kind}>{r.kind}</div>
|
||||
<div className="search-result-body">
|
||||
<div className="search-result-title">
|
||||
<span className="search-result-section">{r.section}</span>
|
||||
<span className="search-result-sep">›</span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.title.replace(/(rout)/i, "<mark>$1</mark>") }} />
|
||||
</div>
|
||||
<div className="search-result-excerpt">{r.excerpt}</div>
|
||||
</div>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="search-result-go">
|
||||
<path d="M7 17 17 7M7 7h10v10"/>
|
||||
</svg>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="search-footer">
|
||||
<span><kbd>↑↓</kbd> navigate</span>
|
||||
<span><kbd>↵</kbd> open</span>
|
||||
<span><kbd>⌘K</kbd> close</span>
|
||||
<div className="search-credit">Powered by <strong>Nibiru Search</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// FAB (floating help button)
|
||||
// ============================================================
|
||||
function FAB() {
|
||||
return (
|
||||
<div className="fab-wrap">
|
||||
<button className="fab" aria-label="Help">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
|
||||
<path d="M12 5v14M5 12h14"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div className="fab-tooltip">Ask AI assistant</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PAGINATION (prev / next page)
|
||||
// ============================================================
|
||||
function Pagination({ prev, next }) {
|
||||
return (
|
||||
<nav className="page-nav">
|
||||
{prev ? (
|
||||
<a href="#" className="page-nav-link page-nav-prev">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="m15 18-6-6 6-6"/></svg>
|
||||
<div className="page-nav-stack">
|
||||
<div className="page-nav-label">Previous</div>
|
||||
<div className="page-nav-title">{prev}</div>
|
||||
</div>
|
||||
</a>
|
||||
) : <div />}
|
||||
{next ? (
|
||||
<a href="#" className="page-nav-link page-nav-next">
|
||||
<div className="page-nav-stack">
|
||||
<div className="page-nav-label">Next</div>
|
||||
<div className="page-nav-title">{next}</div>
|
||||
</div>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
) : <div />}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// MOBILE DRAWER
|
||||
// ============================================================
|
||||
function MobileDrawer() {
|
||||
return (
|
||||
<div className="mobile-drawer">
|
||||
<div className="mobile-drawer-header">
|
||||
<div className="mobile-drawer-brand">
|
||||
<LotusMark size={26} />
|
||||
<span>Nibiru</span>
|
||||
</div>
|
||||
<button className="mobile-drawer-close" aria-label="Close">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="m18 6-12 12M6 6l12 12"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
<button className="mobile-drawer-search">
|
||||
<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>
|
||||
<Sidebar />
|
||||
<div className="mobile-drawer-footer">
|
||||
<button className="mobile-drawer-pill">
|
||||
<svg width="14" height="14" 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.41"/></svg>
|
||||
Theme
|
||||
</button>
|
||||
<button className="mobile-drawer-pill">EN</button>
|
||||
<a href="#" className="mobile-drawer-pill">GitHub ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 404 / EMPTY STATE
|
||||
// ============================================================
|
||||
function NotFound() {
|
||||
return (
|
||||
<div className="not-found">
|
||||
<div className="not-found-orbit">
|
||||
<svg viewBox="0 0 320 200" width="100%" height="100%" aria-hidden="true">
|
||||
<defs>
|
||||
<radialGradient id="lostStar" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stopColor="#fff4d9"/>
|
||||
<stop offset="50%" stopColor="#ffb574"/>
|
||||
<stop offset="100%" stopColor="#b86bff" stopOpacity="0"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
{/* dashed orbit */}
|
||||
<ellipse cx="160" cy="100" rx="140" ry="60" fill="none" stroke="rgba(184,107,255,0.3)" strokeWidth="1" strokeDasharray="3 6"/>
|
||||
<ellipse cx="160" cy="100" rx="100" ry="42" fill="none" stroke="rgba(184,107,255,0.18)" strokeWidth="1" strokeDasharray="3 6"/>
|
||||
{/* central star */}
|
||||
<circle cx="160" cy="100" r="50" fill="url(#lostStar)"/>
|
||||
<circle cx="160" cy="100" r="6" fill="#fff4d9"/>
|
||||
{/* drifting probe (off-orbit) */}
|
||||
<g transform="translate(280 40)">
|
||||
<circle r="3" fill="#6ad9ff"/>
|
||||
<circle r="8" fill="#6ad9ff" opacity="0.2"/>
|
||||
</g>
|
||||
<text x="290" y="35" fill="#6ad9ff" fontSize="10" fontFamily="JetBrains Mono">PROBE-404</text>
|
||||
<line x1="160" y1="100" x2="280" y2="40" stroke="#6ad9ff" strokeWidth="1" strokeDasharray="2 3" opacity="0.4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="not-found-code">404 / off-orbit</div>
|
||||
<h2 className="not-found-title">This page drifted away from Nibiru.</h2>
|
||||
<p className="not-found-desc">
|
||||
The probe couldn't find the page you requested. It may have been moved, renamed, or pulled into a different orbit.
|
||||
</p>
|
||||
<div className="not-found-actions">
|
||||
<a href="#" className="btn btn-primary">Return to docs</a>
|
||||
<a href="#" className="btn btn-ghost">Open search</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(window, { Tabs, ApiBlock, CardGrid, SearchModal, FAB, Pagination, MobileDrawer, NotFound });
|
||||
Reference in New Issue
Block a user