Sweep: link fixes, splash i18n parity, neuronetz badge, mobile, training source

LINK AUDIT — every internal link now resolves to a real page:
  - BrandHeader doc-nav 'Showcase' target → /{locale}/showcase/projects/
    (the showcase/ directory has no index, only patterns/projects)
  - Translated-slug bugs across de/es/fr/ja: ~30 broken links from the
    overnight translator that auto-localised path segments. Mapped back to
    the actual English slugs:
      de:  warum-nibiru → why-nibiru, kuenstliche-intelligenz/orakel → ai/oracle,
           kI/modul/* → ai/module/*, praesentation/projekte → showcase/projects
      es:  ai/modulo/* → ai/module/*, diseno/* → design/*,
           porque-nibiru / por-que-nibiru → why-nibiru,
           presentacion/proyectos → showcase/projects
      fr:  ia/module/* → ai/module/*, ia/module/formation → ai/module/training,
           pourquoi-nibiru → why-nibiru, presentation/projets → showcase/projects
      ja:  ai/milestones → ai/roadmap
  - Cross-locale leaks: bare `/ai/oracle` (no locale) → `/{locale}/ai/oracle/`
    in de/index.mdx, fr/index.mdx, fr/ai/module/overview.md, en/ai/corpus.md
  - `/en/start/` (which 404s — start/ has no index) was hardcoded in five
    design/components.md atelier-button hrefs across all locales →
    `/{locale}/start/installation/`
  - `/en/reference` removed from en/downloads.mdx — the reference doc tree
    isn't built yet; replaced with a github link to the v2 markdown source
  - Collapsed stray double-slashes (`/de/why-nibiru//` etc.) introduced by
    the slug-replacement sed sweep

Final audit shows 0 broken internal links (down from ~37).

TRAINING SOURCE NOW SHIPS — root cause of "I cannot find the training data
in the repository": the curated source files were gitignored.
  - .gitignore at scripts/extraction/ now whitelists framework-reference-v2.md
    and lora-augmentation.summary.txt alongside lora-augmentation.jsonl
  - The 1620-line v2 reference, the 323-record augmentation jsonl, and the
    summary report all enter the repo so the production Docker build sees
    them and contributors can find them by browsing gitea

NEURONETZ AI DEEPLINK BADGE — small "AI by Neuronetz ↗" pill in the splash
footer's bottom strip. Logo mark mirrored locally to
public/img/external/neuronetz-mark.svg (pulled from neuronetz.ai/favicon.svg)
so the page doesn't hot-link off-domain on every paint. Magenta border on
hover; opens neuronetz.ai in a new tab with rel=noopener.

SPLASH I18N PARITY — de/es/fr/ja index.mdx now import + render the same
component stack as en (CometTrail · MmvcStage · MissionControl · LaunchSequence
· SpacecraftGrid · EditorialContent · LandingFooter · ToTop · LandingScripts),
so every locale shows the full splash structure. The component bodies
themselves are still English (proper i18n is the next step); for now this
brings structural parity.

MOBILE RESPONSIVE SWEEP:
  - LandingFooter: 4-col grid stacks 2-col @ 768px and 1-col @ 480px;
    bottom strip wraps vertical at 480px
  - MmvcStage: 5-step progress rail tightens its gaps under 720px and
    drops the bar segments entirely under 480px so the labels fit
  - Docs bridge §11: tighter H1/H2 spacing, breadcrumbs/doc-meta on
    narrow viewports, pagination cards stack 1-col, help-strip stacks
    vertical, tables get horizontal-scroll on overflow
  - Doc-header: nav-version chip hides under 480px so the search-pill
    + brand fit comfortably

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
stephan
2026-05-08 19:36:25 +02:00
parent 22392161ba
commit f8f58c4ab9
105 changed files with 2394 additions and 729 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<rect width="32" height="32" fill="#f4f2ed"/>
<circle cx="16" cy="16" r="6" fill="#8b2b1f"/>
</svg>

After

Width:  |  Height:  |  Size: 165 B

View File

@@ -1,9 +1,13 @@
# Extraction outputs from research-agent runs are typically large + transient. # Extraction outputs from research-agent runs are typically large + transient.
# Keep them local by default — except the curated augmentation file which # Keep them local by default — except the curated files that need to ship so
# needs to ship for the Docker build to fold it into the LoRA corpus. # the Docker build can fold them into the LoRA corpus AND so contributors
# can find the source-of-truth reference in the repo.
*.md *.md
*.json *.json
*.jsonl *.jsonl
*.txt
# …with one explicit exception: # …with explicit exceptions:
!framework-reference-v2.md
!lora-augmentation.jsonl !lora-augmentation.jsonl
!lora-augmentation.summary.txt

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
Nibiru Framework — LoRA Augmentation Corpus Summary
=====================================================
Source: /home/stephan/PhpstormProjects/Nibiru/docs/scripts/extraction/framework-reference-v2.md
Output: /home/stephan/PhpstormProjects/Nibiru/docs/scripts/extraction/lora-augmentation.jsonl
Format: JSONL (one record per line)
Records: 323
Validation
----------
All 323 lines are valid JSON. All records carry the required keys:
instruction, input, output, metadata.{language, source, section, kind, citations}.
(Verified with `jq` schema check — 0 records failed.)
Breakdown by `kind`
-------------------
code-recall 121
workflow 97
inikey 29
comparison 26
gotcha 16
debug 16
edge-case 15
refactor 3
Coverage
--------
All 14 top-level sections of the v2 reference are exercised:
- §0 Executive Summary
- §1 Namespace Map
- §2 Singletons & Factories (Config, Pdo, Db, Form, Controller, View)
- §3 Database Layer (drivers, autogenerator pipeline, multi-database)
- §4 Module System (structure, registry, loading flow, plugin pattern, lifecycle)
- §5 Form System (architecture, substitution, type classes, ordering, div wrap, assembly, production usage)
- §6 Request/Response Lifecycle
- §7 INI Key Reference
- §8 Namespace Conventions
- §9 Constants
- §10 Production Patterns NOT in Core
- §11 Gotchas & Footguns
- §12 Request Handling Trace
- Appendices: File Index, Quick Reference
Cross-section workflow / debug / refactor records (~30) cover full-stack scenarios:
adding a new module, adding a new table, switching DB sections per model,
debugging Postgres-mode mask footgun, end-to-end form-to-DB save flow, etc.
Sample records (3 for sanity-check)
-----------------------------------
Sample 1 (kind=code-recall, line 14):
{"instruction":"Show me the exact `__construct()` body the autogenerator produces for a MySQL model.","input":"","output":"```php\npublic function __construct()\n{\n Pdo::settingsSection('DATABASE');\n self::initTable( self::TABLE );\n}\n```\nFrom `loach.mssql.database-connection/application/model/WarehouseLoach/timeanddate.php:30-34`. The first line writes the static `Pdo::$section`; the second registers the table schema with the adapter's static `$table` array.","metadata":{"language":"en","source":"framework-reference-v2","section":"2.2 Pdo","kind":"code-recall","citations":["loach.mssql.database-connection/application/model/WarehouseLoach/timeanddate.php:30-34","framework-reference-v2.md §2.2"]}}
Sample 2 (kind=inikey, line 100):
{"instruction":"Which INI key sets the PHP timezone?","input":"","output":"`[SETTINGS][timezone]`. Read by `Dispatcher::run()`. Defaults to `UTC`. From `framework-reference-v2.md §7 [SETTINGS]`.","metadata":{"language":"en","source":"framework-reference-v2","section":"7 INI [SETTINGS]","kind":"inikey","citations":["framework-reference-v2.md §7"]}}
Sample 3 (kind=workflow, line 220):
{"instruction":"How does the autogenerator handle a brand-new application with zero existing model files?","input":"","output":"`Model::createOutFolder()` at `core/c/model.php:28` does `mkdir -p` (chmod 0777) for the target folder, then `createClassFiles()` at line 37 loops through scanned tables (or one table if `--table=` was passed) and calls `generateClassByTableName($table)` for each. For each class file:\n1. If overwrite mode, `unlink` existing.\n2. Otherwise `fopen('w'); fclose()` to create an empty stub.\n3. Build template, do all 8 placeholder substitutions plus driver/odbc switching.\n4. Write file and chmod 0777.\n\nSo zero existing files becomes a full set of generated models on the first request. From `framework-reference-v2.md §3.2`.","metadata":{"language":"en","source":"framework-reference-v2","section":"3.2 Autogenerator Pipeline","kind":"workflow","citations":["core/c/model.php:28","core/c/model.php:37","core/c/model.php:53","framework-reference-v2.md §3.2"]}}
Notes for downstream merge
--------------------------
- Every record has a real file:line citation OR a quoted code block.
- Code blocks are quoted from the v2 reference; nothing is invented.
- Phrasing varies across natural developer styles ("Walk me through…",
"Quick question:…", "Why does…", "I'm trying to…", "Show me…",
"I'm getting `Class … not found`…", etc.).
- The `kind` taxonomy follows the spec: edge-case, comparison, workflow,
debug, refactor, code-recall, inikey, gotcha.

View File

@@ -33,7 +33,7 @@ const docNav: Array<{ label: string; href: string; current?: boolean }> = [
{ label: 'MMVC', href: `/${currentLocale}/core/architecture/` }, { label: 'MMVC', href: `/${currentLocale}/core/architecture/` },
{ label: 'AI module',href: `/${currentLocale}/ai/module/overview/` }, { label: 'AI module',href: `/${currentLocale}/ai/module/overview/` },
{ label: 'CLI', href: `/${currentLocale}/start/quick-start/` }, { label: 'CLI', href: `/${currentLocale}/start/quick-start/` },
{ label: 'Showcase', href: `/${currentLocale}/showcase/` }, { label: 'Showcase', href: `/${currentLocale}/showcase/projects/` },
]; ];
--- ---

View File

@@ -50,6 +50,12 @@
</div> </div>
<div class="container footer-bottom"> <div class="container footer-bottom">
<span>© 2026 Nibiru · MIT licensed</span> <span>© 2026 Nibiru · MIT licensed</span>
<a class="powered-by" href="https://neuronetz.ai/" target="_blank" rel="noopener" aria-label="Powered by Neuronetz AI — opens neuronetz.ai">
<img src="/img/external/neuronetz-mark.svg" alt="" width="18" height="18" loading="lazy" />
<span class="powered-by-label">AI by</span>
<span class="powered-by-name">Neuronetz</span>
<span class="powered-by-arrow" aria-hidden="true">↗</span>
</a>
<span>Built in orbit · v0.9.2</span> <span>Built in orbit · v0.9.2</span>
</div> </div>
</footer> </footer>
@@ -85,7 +91,18 @@
padding: 0 32px; padding: 0 32px;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.footer-inner { grid-template-columns: 1fr 1fr; } .footer-inner { grid-template-columns: 1fr 1fr; gap: 32px; }
.footer { padding: 60px 0 40px; }
}
@media (max-width: 480px) {
.footer-inner { grid-template-columns: 1fr; gap: 28px; padding: 0 20px; }
.footer-bottom {
flex-direction: column;
align-items: flex-start;
gap: 14px;
padding: 24px 20px 0;
margin-top: 48px;
}
} }
.footer-brand .brand { .footer-brand .brand {
margin-bottom: 16px; margin-bottom: 16px;
@@ -150,5 +167,55 @@
flex-wrap: wrap; flex-wrap: wrap;
gap: 12px; gap: 12px;
} }
/* "AI by Neuronetz" badge — small, deliberately understated.
The mark is the same favicon-tier asset neuronetz.ai exposes; we mirror
it locally at /img/external/neuronetz-mark.svg so the page doesn't
hot-link off-domain on every paint. */
.powered-by {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px 4px 6px;
background: rgba(184, 107, 255, 0.06);
border: 1px solid var(--nibiru-line);
border-radius: 999px;
color: rgba(244, 238, 219, 0.78);
text-decoration: none;
font-family: var(--font-mono);
font-size: 10px;
letter-spacing: 0.10em;
text-transform: uppercase;
transition: border-color 200ms ease, color 200ms ease, background-color 200ms ease;
}
.powered-by:hover {
border-color: var(--nibiru-nebula-mag, #b86bff);
color: var(--nibiru-star);
background: rgba(184, 107, 255, 0.12);
}
.powered-by img {
border-radius: 50%;
background: #f4f2ed;
padding: 1px;
flex: none;
}
.powered-by-label { color: var(--nibiru-muted); }
.powered-by-name {
color: var(--nibiru-star);
font-weight: 500;
text-transform: none;
letter-spacing: -0.01em;
font-size: 11px;
font-family: var(--nibiru-font-display, 'Space Grotesk', sans-serif);
}
.powered-by-arrow {
font-size: 9px;
color: var(--nibiru-muted);
transition: transform 200ms ease, color 200ms ease;
}
.powered-by:hover .powered-by-arrow {
transform: translate(1px, -1px);
color: var(--nibiru-nebula-mag, #b86bff);
}
</style> </style>

View File

@@ -205,10 +205,23 @@
} }
@media (max-width: 720px) { @media (max-width: 720px) {
.mmvc-progress { gap: 4px; font-size: 9px; padding: 0 8px; } .mmvc-progress { gap: 3px; font-size: 9px; padding: 0 6px; }
.mmvc-progress .step { gap: 4px; padding-right: 4px; } .mmvc-progress .step { gap: 3px; padding-right: 3px; }
.mmvc-progress .step .bar { width: 14px; } .mmvc-progress .step .bar { width: 10px; }
.mmvc-progress .step.active .bar { width: 24px; } .mmvc-progress .step.active .bar { width: 18px; }
}
/* Below 480px the 5-step rail starts colliding with the panel headline.
Drop the bar segments entirely and keep only the labels at minimum size. */
@media (max-width: 480px) {
.mmvc-progress { font-size: 8px; gap: 6px; padding: 0 4px; }
.mmvc-progress .step .bar { display: none; }
.mmvc-progress .step { padding-right: 0; gap: 0; }
.mmvc-copy { padding: 0 16px; }
.mmvc-copy h3 { font-size: clamp(28px, 9vw, 44px); }
.mmvc-copy p { font-size: 15px; }
.mmvc-copy .step-num { font-size: 10px; margin-bottom: 16px; }
.mmvc-copy .step-num::before, .mmvc-copy .step-num::after { width: 16px; }
} }
@media (max-width: 960px) { @media (max-width: 960px) {

View File

@@ -60,7 +60,7 @@ Rohdaten für die Verwendung als RAG-Retrieval-Daten:
"content": "Modules implementing `SplSubject` can broadcast events…" "content": "Modules implementing `SplSubject` can broadcast events…"
} }
``` ```
Dies ist genau die Datei, die intern von [Oracle](/ai/oracle/) verwendet wird. Dies ist genau die Datei, die intern von [Oracle](/de/ai/oracle/) verwendet wird.
## Wie Chunks abgeleitet werden ## Wie Chunks abgeleitet werden

View File

@@ -10,19 +10,19 @@ Das Chat-Plugin ist das einfachste Element des KI-Moduls. Es umhüllt Ollamas `/
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$chat = $ai->chat(); $chat = $ai->chat();
$chat->system('Be terse.'); // optional system prompt $chat->system('Be terse.'); / optional system prompt
$chat->model('qwen2.5-coder:14b'); // override the configured model $chat->model('qwen2.5-coder:14b'); / override the configured model
$chat->temperature(0.2); // override config $chat->temperature(0.2); / override config
$chat->maxTokens(512); // override config $chat->maxTokens(512); / override config
$chat->user('Hello'); // append a user message $chat->user('Hello'); / append a user message
$chat->assistant('Hi.'); // append an assistant message (rare) $chat->assistant('Hi.'); / append an assistant message (rare)
$reply = $chat->complete(); // run the call, return text $reply = $chat->complete(); / run the call, return text
$reply = $chat->ask('How are you?'); // = ->user(...)->complete() $reply = $chat->ask('How are you?'); / = ->user(...)->complete()
$chat->reset(); // clear messages, keep model + system $chat->reset(); / clear messages, keep model + system
$chat->history(); // [{role, content}, …] $chat->history(); / [{role, content}, ]
``` ```
## Einmalig ## Einmalig
```php ```php
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('Name three Nibiru singletons.'); $chat->user('Name three Nibiru singletons.');
$singletons = $chat->complete(); // appended to history $singletons = $chat->complete(); / appended to history
$chat->user('What does the second one do?'); $chat->user('What does the second one do?');
$detail = $chat->complete(); // model has full context $detail = $chat->complete(); / model has full context
``` ```
## Überschreiben des Modells und des Stils pro Aufruf ## Überschreiben des Modells und des Stils pro Aufruf
```php ```php

View File

@@ -9,12 +9,12 @@ Das Embed-Plugin ist ein dünner Wrapper um Ollama's `/api/embeddings` plus drei
```php ```php
$embed = (new \Nibiru\Module\Ai\Ai())->embed(); $embed = (new \Nibiru\Module\Ai\Ai())->embed();
$vec = $embed->one('controller'); // float[] $vec = $embed->one('controller'); / float[]
$vectors = $embed->batch(['a', 'b', 'c']); // float[][] $vectors = $embed->batch(['a', 'b', 'c']); / float[][]
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1 $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[] $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
``` ```
## Muster: Entfernen ähnlicher Zeichenfolgen ## Muster: Entfernen ähnlicher Zeichenfolgen
```php ```php
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
return $best[0]; return $best[0];
} }
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication' echo bestTag('User::isAuthorized', $tagVecs, $embed); / 'authentication'
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably) echo bestTag('Pageination::setTable', $tagVecs, $embed); / 'database' (probably)
``` ```
## Speicherung ## Speicherung
@@ -58,7 +58,7 @@ Einbettungen sind Gleitkommazahlenarrays typischerweise 768 Gleitkommazahlen
Verwenden Sie `Embed::pack()`, um sie als 4-Byte-Gleitkommazahlen in Base64 zu kodieren: Verwenden Sie `Embed::pack()`, um sie als 4-Byte-Gleitkommazahlen in Base64 zu kodieren:
```php ```php
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string $compact = Embed::pack($vec); / ~4 KB ~5.3 KB base64 string
$vec = Embed::unpack($compact); $vec = Embed::unpack($compact);
``` ```
Das RAG-Plugin verwendet dieses Format intern für seine JSON-Dateien. Das RAG-Plugin verwendet dieses Format intern für seine JSON-Dateien.

View File

@@ -5,7 +5,7 @@ description: "Erster-Klasse KI auf Nibiru Chat, Embeddings, RAG, Agenten
Nibiru bietet ein **KIM-Modul** (`application/module/ai/`), das jeder Nibiru-Anwendung eine erstklassige KI-Oberfläche gibt. PHP-Code kann mit einem lokalen LLM chatten, Text einbetten, RAG über eigene Daten ausführen oder einen Agenten mit Tools starten alles ohne einen Byte an eine bezahlte API zu senden. Nibiru bietet ein **KIM-Modul** (`application/module/ai/`), das jeder Nibiru-Anwendung eine erstklassige KI-Oberfläche gibt. PHP-Code kann mit einem lokalen LLM chatten, Text einbetten, RAG über eigene Daten ausführen oder einen Agenten mit Tools starten alles ohne einen Byte an eine bezahlte API zu senden.
Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf Ihrer Ollama-Instanz](/de/kuenstliche-intelligenz/orakel/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt. Das Modul ist standardmäßig mit Ihrem eigenen [Ollama auf Ihrer Ollama-Instanz](/de/ai/oracle/) verbunden, sodass die Inferenz auf Ihrer Hardware, in Ihrem Netzwerk und Ihren Bedingungen erfolgt.
## Was Sie erhalten ## Was Sie erhalten
@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
// Multi-turn // Multi-turn
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('How do I scaffold a module?'); $chat->user('How do I scaffold a module?');
$chat->user('And add Graylog hooks?'); // referrs to previous turn $chat->user('And add Graylog hooks?'); / referrs to previous turn
echo $chat->complete(); echo $chat->complete();
// Override per call // Override per call
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
``` ```
Kompakte Speicherung: Kompakte Speicherung:
```php ```php
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
``` ```
### 3. RAG — Ingestieren, Abrufen, Verankern ### 3. RAG — Ingestieren, Abrufen, Verankern
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
$rag = $ai->rag('product-help'); $rag = $ai->rag('product-help');
// One-time ingestion // One-time ingestion
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
$rag->ingestFile('/var/data/manual.pdf.txt'); $rag->ingestFile('/var/data/manual.pdf.txt');
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$agent = $ai->agent()->withTools([ $agent = $ai->agent()->withTools([
new Tools\PdoQuery(), // read-only SQL new Tools\PdoQuery(), / read-only SQL
new Tools\HttpGet(), // fetch URLs new Tools\HttpGet(), / fetch URLs
new Tools\FileRead(), // read project files new Tools\FileRead(), / read project files
]); ]);
echo $agent->run('How many active users registered last week?'); echo $agent->run('How many active users registered last week?');
@@ -154,7 +154,7 @@ Die Gestaltungsentfernung:
## Nächste ## Nächste
- [Chat-Plugin-Referenz](/de/kI/modul/chat/) - [Chat-Plugin-Referenz](/de/ai/module/chat/)
- [RAG-Plugin-Referenz](/de/kI/modul/rag/) - [RAG-Plugin-Referenz](/de/ai/module/rag/)
- [Agent-Plugin-Referenz](/de/kI/modul/agent/) - [Agent-Plugin-Referenz](/de/ai/module/agent/)
- [Training nibiru-coder](/de/kI/modul/training/) - [Training nibiru-coder](/de/ai/module/training/)

View File

@@ -10,9 +10,9 @@ Das RAG-Plugin ist die Killerfunktion des KI-Moduls für Produktbauer. Es verwan
use Nibiru\Module\Ai\Ai; use Nibiru\Module\Ai\Ai;
$ai = new Ai(); $ai = new Ai();
$rag = $ai->rag('product-help'); // a named collection $rag = $ai->rag('product-help'); / a named collection
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/ $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
echo $rag->ask('How do I cancel my subscription?'); echo $rag->ask('How do I cancel my subscription?');
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
``` ```
## API-Referenz ## API-Referenz
```php ```php
$rag = $ai->rag('name'); // get/create a named collection $rag = $ai->rag('name'); / get/create a named collection
// --- Ingestion --- // --- Ingestion ---
$rag->ingestText($text, $metadata = []); // single chunk $rag->ingestText($text, $metadata = []); / single chunk
$count = $rag->ingestFile('path'); // returns chunks added $count = $rag->ingestFile('path'); / returns chunks added
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive $count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
// --- Querying --- // --- Querying ---
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …] $hits = $rag->search('query', $k = null); / [{score, text, metadata}, ]
$answer = $rag->ask('question', $k = null); // top-K → chat call $answer = $rag->ask('question', $k = null); / top-K chat call
// --- Maintenance --- // --- Maintenance ---
$rag->reset(); // forget everything (deletes file) $rag->reset(); / forget everything (deletes file)
$n = $rag->size(); // number of chunks $n = $rag->size(); / number of chunks
``` ```
## Einstellungsregler ## Einstellungsregler
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
## Was kommt als nächstes? ## Was kommt als nächstes?
- [Agent-Plugin →](/de/kI/modul/agent/) für Werkzeuge, nicht für Abruf. - [Agent-Plugin →](/de/ai/module/agent/) für Werkzeuge, nicht für Abruf.
- [Trainingsnibiru-coder →](/de/kI/modul/trainings/) um den Chat so zu gestalten, dass er halb in der Stimme des Frameworks antwortet. - [Trainingsnibiru-coder →](/de/ai/module/training/) um den Chat so zu gestalten, dass er halb in der Stimme des Frameworks antwortet.

View File

@@ -114,7 +114,7 @@ public function pageAction() {
View::forwardTo('/login'); View::forwardTo('/login');
return; return;
} }
// ... / ...
} }
``` ```
Für rollebasierte Überprüfungen verwenden die Showcase-Anwendungen den `Acl`-Plugin aus dem gleichen Modul: Für rollebasierte Überprüfungen verwenden die Showcase-Anwendungen den `Acl`-Plugin aus dem gleichen Modul:
@@ -178,7 +178,7 @@ public function submitAction() {
http_response_code(419); http_response_code(419);
return; return;
} }
// ...handle submission... / ...handle submission...
} }
``` ```
Fügen Sie `<input type="hidden" name="csrf" value="{$csrf}">` in Ihr Formular ein. Fügen Sie `<input type="hidden" name="csrf" value="{$csrf}">` in Ihr Formular ein.

View File

@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
## Konfiguration lesen ## Konfiguration lesen
```php ```php
$cfg = \Nibiru\Config::getInstance()->getConfig(); $cfg = \Nibiru\Config::getInstance()->getConfig();
$cfg['DATABASE']['driver']; // 'pdo' $cfg['DATABASE']['driver']; / 'pdo'
$cfg['SETTINGS']['page.url']; // 'https://my-app.local' $cfg['SETTINGS']['page.url']; / 'https://my-app.local'
``` ```
Verwenden Sie für tief geschachtelte Konfigurationen die typisierten Konstanten aus der View-Schnittstelle: Verwenden Sie für tief geschachtelte Konfigurationen die typisierten Konstanten aus der View-Schnittstelle:
```php ```php
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css'] $cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
``` ```
## Modulkonfigurationen ## Modulkonfigurationen
Jeder Modul unter `application/module/<name>/settings/` kann seine eigenen INI-Dateien haben. Der Registrierungsservice erkennt sie automatisch und macht sie über verfügbar: Jeder Modul unter `application/module/<name>/settings/` kann seine eigenen INI-Dateien haben. Der Registrierungsservice erkennt sie automatisch und macht sie über verfügbar:
```php ```php
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; // from [USERS] section in users.ini $users->session_lifetime; / from [USERS] section in users.ini
``` ```
Der Registrierung wird `<module>.<env>.ini` vor `<module>.ini` bevorzugt, sodass Sie automatisch Überschreibungen pro Umgebung erhalten. Der Registrierung wird `<module>.<env>.ini` vor `<module>.ini` bevorzugt, sodass Sie automatisch Überschreibungen pro Umgebung erhalten.

View File

@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
``` ```
Hilfsfunktionen aus dem Basiscontroller: Hilfsfunktionen aus dem Basiscontroller:
```php ```php
$this->getRequest('id', false); // $_REQUEST['id'] ?? false $this->getRequest('id', false); / $_REQUEST['id'] ?? false
$this->getPost('email', ''); // $_POST['email'] ?? '' $this->getPost('email', ''); / $_POST['email'] ?? ''
$this->getGet('page', 1); // $_GET['page'] ?? 1 $this->getGet('page', 1); / $_GET['page'] ?? 1
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI'] $this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
$this->getFiles('upload'); // $_FILES['upload'] $this->getFiles('upload'); / $_FILES['upload']
$this->getSession('auth'); // $_SESSION['auth'] $this->getSession('auth'); / $_SESSION['auth']
``` ```
Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tests durch eine untergeordnete Klasse ersetzen. Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tests durch eine untergeordnete Klasse ersetzen.
@@ -69,8 +69,8 @@ Diese bestehen, weil `Controller` `final`-freundlich ist: Sie können sie in Tes
Um innerhalb einer Aktion umzuleiten: Um innerhalb einer Aktion umzuleiten:
```php ```php
View::forwardTo('/login'); // 302 to the URL, exits View::forwardTo('/login'); / 302 to the URL, exits
View::forwardToJsonHeader(); // sets Content-Type: application/json View::forwardToJsonHeader(); / sets Content-Type: application/json
``` ```
`forwardToJsonHeader()` ist das kanonische Muster für JSON-Endpunkte setzen Sie den Header, weisen Sie `data` zu und geben Sie zurück. Der Anzeigebereich kümmert sich danach um die Reste. `forwardToJsonHeader()` ist das kanonische Muster für JSON-Endpunkte setzen Sie den Header, weisen Sie `data` zu und geben Sie zurück. Der Anzeigebereich kümmert sich danach um die Reste.

View File

@@ -30,17 +30,17 @@ public function run() {
if (Config::getInstance()->getConfig() if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) { [self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); // 1. (re)generate models from schema new Model(false); / 1. (re)generate models from schema
} }
Router::getInstance()->route(); // 2. parse the URL Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); // 3. load model files Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); // 4. load module classes Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName(); $tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php"; $controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { // 5. controller file exists if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile; require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller"; $class = "Nibiru\\{$tpl}Controller";
$controller = new $class(); $controller = new $class();
@@ -49,7 +49,7 @@ public function run() {
$action = $_REQUEST['_action'] . 'Action'; $action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction(); $controller->navigationAction();
if (method_exists($controller, $action)) { if (method_exists($controller, $action)) {
$controller->$action(); // 6. optional named action $controller->$action(); / 6. optional named action
} }
$controller->pageAction(); $controller->pageAction();
} else { } else {
@@ -57,9 +57,9 @@ public function run() {
$controller->pageAction(); $controller->pageAction();
} }
Display::getInstance()->display(); // 7. render Smarty Display::getInstance()->display(); / 7. render Smarty
} else { } else {
// 8. soft 404 — render the configured error controller / 8. soft 404 render the configured error controller
} }
} }
``` ```

View File

@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
``` ```
### Lebenszyklus ### Lebenszyklus
``` ```
create // reset the static buffer; call before building a new form create / reset the static buffer; call before building a new form
addForm // wrap the buffer in <form>...</form> and return as a string addForm / wrap the buffer in <form>...</form> and return as a string
``` ```
:::caution[Die Namensinconsistenz ist absichtlich, zumindest teilweise] :::caution[Die Namensinconsistenz ist absichtlich, zumindest teilweise]
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — das Präfix entspricht, ob das Element ein `<input type="…">` (Input-Prefix) oder ein anderer Tag (Type-Prefix) ist. Es ist unangenehm, aber stabil; die CLI-`./nibiru -c`-Scaffolds verwenden dasselbe Muster, sodass die Muskelspeicherung funktioniert. `addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — das Präfix entspricht, ob das Element ein `<input type="…">` (Input-Prefix) oder ein anderer Tag (Type-Prefix) ist. Es ist unangenehm, aber stabil; die CLI-`./nibiru -c`-Scaffolds verwenden dasselbe Muster, sodass die Muskelspeicherung funktioniert.
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
```php ```php
use Nibiru\Factory\Form; use Nibiru\Factory\Form;
Form::create(); // reset the static buffer Form::create(); / reset the static buffer
Form::addOpenDiv(['class' => 'form-group']); Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']); Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
@@ -187,7 +187,7 @@ class formsController extends Controller {
'required' => 'required', 'required' => 'required',
'class' => 'contacts-input form-control', 'class' => 'contacts-input form-control',
]); ]);
// ...more fields... / ...more fields...
$this->form = Form::addForm([ $this->form = Form::addForm([
'name' => 'newregister', 'name' => 'newregister',
'method' => 'post', 'method' => 'post',

View File

@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
Lesen Sie es von überall aus: Lesen Sie es von überall aus:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; // 7200 $cfg->session_lifetime; / 7200
$cfg->password_min_length; // 12 $cfg->password_min_length; / 12
$cfg->allowed_roles; // [admin, editor, standard] $cfg->allowed_roles; / [admin, editor, standard]
``` ```
Umgebungsüberschreibungen: Eine Datei mit dem Namen `users.production.ini` wird vorzugsweise gegenüber `users.ini` verwendet, wenn `APPLICATION_ENV=production`. Umgebungsüberschreibungen: Eine Datei mit dem Namen `users.production.ini` wird vorzugsweise gegenüber `users.ini` verwendet, wenn `APPLICATION_ENV=production`.
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); // internally calls notify() $analytics->trackPageView(); / internally calls notify()
``` ```
Jeder Beobachter erhält die Analytics-Instanz und zieht die Ereignisdaten aus, über die er sich interessiert. Jeder Beobachter erhält die Analytics-Instanz und zieht die Ereignisdaten aus, über die er sich interessiert.
@@ -200,7 +200,7 @@ Die Namen sind **kleinbuchstabeigeordnete Ordnername**, genau wie sie unter `app
Pluginklassen liegen im **Plural**-Namespace `Plugins`: Pluginklassen liegen im **Plural**-Namespace `Plugins`:
```php ```php
// application/module/billing/plugins/invoice.php // application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; // ← plural namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ } class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
``` ```
Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlschläge auf. Der CLI-Scaffold-Befehl (`./nibiru -m billing`) generiert den richtigen Namespace für Sie. Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlschläge auf. Der CLI-Scaffold-Befehl (`./nibiru -m billing`) generiert den richtigen Namespace für Sie.
@@ -210,7 +210,7 @@ Namespace und Klassenname nicht übereinstimmen und es treten Autoloader-Fehlsch
Sie **registrieren** Ihre Modul-INI-Dateien nicht — der [Registry](/de/core/registry/) entdeckt sie automatisch, indem er `application/module/<name>/settings/*.ini` durchläuft, nachdem `[AUTOLOADER]` die Modulklassen lädt. Jede Sektion `[<MODULE>]` (in Großbuchstaben) in einer INI wird zur Verfügung gestellt als: Sie **registrieren** Ihre Modul-INI-Dateien nicht — der [Registry](/de/core/registry/) entdeckt sie automatisch, indem er `application/module/<name>/settings/*.ini` durchläuft, nachdem `[AUTOLOADER]` die Modulklassen lädt. Jede Sektion `[<MODULE>]` (in Großbuchstaben) in einer INI wird zur Verfügung gestellt als:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; // [BILLING] invoice.prefixproperty $cfg->invoice_prefix; / [BILLING] invoice.prefix property
``` ```
Der Registrierungsmodul bevorzugt `<Modul>.<Umgebung>.ini` (z.B. `billing.production.ini`), wenn `APPLICATION_ENV` übereinstimmt. Der Registrierungsmodul bevorzugt `<Modul>.<Umgebung>.ini` (z.B. `billing.production.ini`), wenn `APPLICATION_ENV` übereinstimmt.

View File

@@ -14,7 +14,7 @@ class productsController extends Controller
{ {
public function pageAction() { public function pageAction() {
$products = new products(); $products = new products();
Pageination::setEntriesPerPage(25); // optional; default from INI Pageination::setEntriesPerPage(25); / optional; default from INI
Pageination::setTable($products); Pageination::setTable($products);
$rows = Pageination::loadTableAsArray(); $rows = Pageination::loadTableAsArray();

View File

@@ -20,7 +20,7 @@ Für jedes Modul unter `application/module/<name>/settings/`:
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; $users->session_lifetime;
$users->password_min_length; $users->password_min_length;
$users->allowed_roles; // array $users->allowed_roles; / array
``` ```
Innerhalb des Moduls selbst ist die Konvention, dies in einen Setter zu verpacken: Innerhalb des Moduls selbst ist die Konvention, dies in einen Setter zu verpacken:
```php ```php

View File

@@ -40,7 +40,7 @@ Alles nach dem Aktionssegment wird zu einem `$_REQUEST`-Schlüssel gemäß der P
// /users/edit/42 → $_REQUEST['id'] = '42' // /users/edit/42 → $_REQUEST['id'] = '42'
public function editAction() { public function editAction() {
$id = (int) ($_REQUEST['id'] ?? 0); $id = (int) ($_REQUEST['id'] ?? 0);
// ... / ...
} }
``` ```
Für nicht-numerische oder benannte Parameter bevorzugen Sie Abfragezeichenfolgen: Für nicht-numerische oder benannte Parameter bevorzugen Sie Abfragezeichenfolgen:
@@ -62,9 +62,9 @@ Wenn eine URL dem Muster entspricht, werden die Captured Groups den benannten `p
## Routen-Hilfsprogramme ## Routen-Hilfsprogramme
```php ```php
Router::getInstance()->currentPage(); // 'products' Router::getInstance()->currentPage(); / 'products'
Router::getInstance()->tplName(); // 'products' (controller stem for templates) Router::getInstance()->tplName(); / 'products' (controller stem for templates)
Router::getInstance()->getController(); // alias for currentPage() Router::getInstance()->getController(); / alias for currentPage()
``` ```
Diese sind nützlich innerhalb von Controllern und Templates: Diese sind nützlich innerhalb von Controllern und Templates:
```smarty ```smarty

View File

@@ -7,7 +7,7 @@ description: "Schaltflächen, Karten, Aufrufe, Heldenbildschirm bereit zum K
Ein schwarzer Rechteck auf einem cremefarbenen Hintergrund. Editorial. Kein Gradient, kein Glühen. Ein schwarzer Rechteck auf einem cremefarbenen Hintergrund. Editorial. Kein Gradient, kein Glühen.
```html ```html
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/de/start/installation/">
<span>Read the docs</span> <span>Read the docs</span>
<span class="atelier-button__arrow" aria-hidden="true"></span> <span class="atelier-button__arrow" aria-hidden="true"></span>
</a> </a>
@@ -162,7 +162,7 @@ Asymmetrische Gitter mit zwei Spalten. Große Editorial-Nummer hinter dem Text.
Nibiru is a modular PHP framework for builders who ship. Nibiru is a modular PHP framework for builders who ship.
</p> </p>
<div class="atelier-hero__cta"> <div class="atelier-hero__cta">
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/de/start/installation/">
Read the docs <span aria-hidden="true"></span> Read the docs <span aria-hidden="true"></span>
</a> </a>
</div> </div>

View File

@@ -19,73 +19,25 @@ hero:
variant: minimal variant: minimal
--- ---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; import CometTrail from '../../../components/CometTrail.astro';
import MmvcStage from '../../../components/MmvcStage.astro';
import MissionControl from '../../../components/MissionControl.astro';
import LaunchSequence from '../../../components/LaunchSequence.astro';
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
import EditorialContent from '../../../components/EditorialContent.astro';
import LandingFooter from '../../../components/LandingFooter.astro';
import ToTop from '../../../components/ToTop.astro';
import LandingScripts from '../../../components/LandingScripts.astro';
## Eine Konstellation von Funktionen <CometTrail />
<MmvcStage />
<MissionControl />
<LaunchSequence />
<SpacecraftGrid />
<EditorialContent />
<LandingFooter />
<ToTop />
<CardGrid stagger> {/* Loads three.js + the original mockup scene code. MUST be the last node so
<Card title="MMVC-Architektur" icon="puzzle"> every #id the scene targets exists in the DOM when the script runs. */}
Module umschließen MVC mit Traits, Plugins, Interfaces und Settings. Das zweite **M** ist lose gekoppelt — und besitzt das Observer-Muster (`SplSubject`) ab Werk. <LandingScripts />
</Card>
<Card title="Multi-Datenbank-Kern" icon="seti:db">
Fünf Treiber im Orbit: `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) und `postgresql`. Wechsel durch Ändern eines INI-Schlüssels.
</Card>
<Card title="Smarty Views" icon="document">
Template-getriebene Views, ein globaler `View::assign()`-Helfer und ein heißer `templates_c`-Cache. Plus geteilte Partials, Navigations-Includes und Pagination-Templates.
</Card>
<Card title="Flüssiger Form-Builder" icon="pencil">
28+ Feldtypen — Text, Passwort, Switch, Color, Range, File-Upload — komponiert über statische `Form::add…`-Aufrufe und als ein einziger HTML-String gerendert.
</Card>
<Card title="Echtes CLI-Tool" icon="seti:powershell">
`./nibiru` generiert Module, Controller, Plugins, Migrationen und CMS-Seiten. Es führt Migrationen gegen `local`, `staging` oder `production` aus.
</Card>
<Card title="KI-nativ, demnächst" icon="rocket">
Nibiru wird das erste PHP-Framework mit einem eingebauten <a href="/de/ai/oracle/">RAG-basierten Orakel</a>, das auf seinem eigenen Wissen trainiert ist — und einem veröffentlichten Korpus für künftige Fine-Tunes.
</Card>
</CardGrid>
## Schnellstart
```bash
# Klonen
git clone https://github.com/alllinux/Nibiru meine-app && cd meine-app
# PHP-Abhängigkeiten installieren (Smarty, PHPMailer, Guzzle, …)
composer install
# Berechtigungen + Ordner-Bootstrap
./nibiru -s
# Erste Migration ausführen
./nibiru -mi local
# Controller generieren
./nibiru -c products
```
```php
// application/controller/productsController.php
namespace Nibiru;
use Nibiru\Adapter\Controller;
class productsController extends Controller {
public function pageAction() {
View::assign([
'title' => 'Produkte — Nibiru',
'products' => [['id' => 1, 'name' => 'Marduk-Goldbeschichtung']],
]);
}
public function navigationAction() {
JsonNavigation::getInstance()->loadJsonNavigationArray();
}
}
```
## Wohin als Nächstes?
<CardGrid>
<LinkCard title="Was ist Nibiru?" href="/de/start/what-is-nibiru/" description="Die 90-Sekunden-Tour: MMVC, der Dispatcher, der Request-Lifecycle." />
<LinkCard title="Architektur" href="/core/architecture/" description="Wie Module, Controller, Views und das Registry einander umkreisen." />
<LinkCard title="Showcase" href="/showcase/projects/" description="Echte Apps in Produktion auf Nibiru — Rechnungswesen, E-Commerce, industrielles PIM." />
<LinkCard title="Frag das Orakel" href="/de/ai/oracle/" description="Öffne den schwebenden Chat. Das Orakel ist auf genau dieser Dokumentation geerdet." />
</CardGrid>

View File

@@ -61,7 +61,7 @@ Mehrere Tracker ohne Controller-Kopplung.
$analytics = new Analytics(); $analytics = new Analytics();
$analytics->attach(new Plugin\Matomo()); $analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible()); $analytics->attach(new Plugin\Plausible());
$analytics->trackPageView(); // calls notify() internally $analytics->trackPageView(); / calls notify() internally
``` ```
Jeder Beobachter ruft nur die Felder ab, die er interessiert sind. Hinzufügen eines Trackers ist eine Änderung in einer Zeile. Jeder Beobachter ruft nur die Felder ab, die er interessiert sind. Hinzufügen eines Trackers ist eine Änderung in einer Zeile.

View File

@@ -55,7 +55,7 @@ public function detailAction()
try { try {
$machine = Machine::init()->getMachine((int) $machineId); $machine = Machine::init()->getMachine((int) $machineId);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$machine = null; // DB blip → page still renders with fallback. $machine = null; / DB blip page still renders with fallback.
} }
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId"; $machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
@@ -110,7 +110,7 @@ Fügen Sie eine neue Smarty-Vorlage ins System ein, der Editor weiß sofort, wel
## Was ist tatsächlich besonderes, zusammengefasst ## Was ist tatsächlich besonderes, zusammengefasst
Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. Jeder verweist auf seine Beweise auf der Seite [Warum Nibiru](/de/warum-nibiru/). Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. Jeder verweist auf seine Beweise auf der Seite [Warum Nibiru](/de/why-nibiru/).
| | Was Nibiru macht | Was Laravel/Symfony macht | | | Was Nibiru macht | Was Laravel/Symfony macht |
|---|---|---| |---|---|---|
@@ -120,7 +120,7 @@ Die fünf Unterschiedsmaker unten stammen aus den darüberliegenden Codebasen. J
| Authentifizierung | 3 Zeilen im Controller-Konstruktor. | Middleware-Stack + Policy-Klassen + Gates. | | Authentifizierung | 3 Zeilen im Controller-Konstruktor. | Middleware-Stack + Policy-Klassen + Gates. |
| Ereignisse | `SplSubject` + `SplObserver` aus der PHP Standardbibliothek. | Benutzerdefinierter Event-Dispatcher + Listener-Registry + Warteschlange. | | Ereignisse | `SplSubject` + `SplObserver` aus der PHP Standardbibliothek. | Benutzerdefinierter Event-Dispatcher + Listener-Registry + Warteschlange. |
Lesen Sie die [vollständige Ausbreakdown mit Codeverweisen →](/de/warum-nibiru/) Lesen Sie die [vollständige Ausbreakdown mit Codeverweisen →](/de/why-nibiru/)
--- ---

View File

@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
use Traits\PageBuilderForm; use Traits\PageBuilderForm;
use Traits\CmsPageStructureModifier; use Traits\CmsPageStructureModifier;
use Traits\FormElements; use Traits\FormElements;
// …8 more traits / …8 more traits
} }
``` ```
**Gesamtwirkung**: keine Konstruktor-Injektion, keine Dienstleistungsanbieterregistrierung, keine Aufrufe von `bind()` / `singleton()` in einer Konfigurationsdatei. Ein neuer Entwickler kann nach dem Trait-Namen suchen und alle Aufrufer sehen. **Gesamtwirkung**: keine Konstruktor-Injektion, keine Dienstleistungsanbieterregistrierung, keine Aufrufe von `bind()` / `singleton()` in einer Konfigurationsdatei. Ein neuer Entwickler kann nach dem Trait-Namen suchen und alle Aufrufer sehen.
@@ -108,7 +108,7 @@ Der API-Controller nimmt den umgekehrten Ansatz: eine Whitelist öffentlicher En
```php ```php
// public endpoints can be listed up-front, auth wraps the rest. // public endpoints can be listed up-front, auth wraps the rest.
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) { if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
// public, skip auth / public, skip auth
} else { } else {
$this->user = new User(); $this->user = new User();
$this->acl = new Acl(); $this->acl = new Acl();
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
} }
public function indexMachines(): void { public function indexMachines(): void {
// …do the work… / …do the work…
$this->notify(); // analytics, cache invalidator, audit log all see it. $this->notify(); / analytics, cache invalidator, audit log all see it.
} }
} }
``` ```
@@ -154,4 +154,4 @@ Ein einzelner Entwickler oder eine kleine Team kann eine echte Produktions-App b
Wenn Sie lieber Ihren Code sehen möchten, nehmen Sie ihn. Wenn Sie lieber Ihren Code sehen möchten, nehmen Sie ihn.
→ [Die Präsentation ansehen →](/de/praesentation/projekte/) → [Die Präsentation ansehen →](/de/showcase/projects/)

View File

@@ -70,7 +70,7 @@ Raw chunks for use as RAG retrieval data:
} }
``` ```
This is exactly the file the [Oracle](/ai/oracle/) uses internally. This is exactly the file the [Oracle](/en/ai/oracle/) uses internally.
## How chunks are derived ## How chunks are derived

View File

@@ -11,19 +11,19 @@ The Chat plugin is the simplest piece of the AI module. It wraps Ollama's `/api/
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$chat = $ai->chat(); $chat = $ai->chat();
$chat->system('Be terse.'); // optional system prompt $chat->system('Be terse.'); / optional system prompt
$chat->model('qwen2.5-coder:14b'); // override the configured model $chat->model('qwen2.5-coder:14b'); / override the configured model
$chat->temperature(0.2); // override config $chat->temperature(0.2); / override config
$chat->maxTokens(512); // override config $chat->maxTokens(512); / override config
$chat->user('Hello'); // append a user message $chat->user('Hello'); / append a user message
$chat->assistant('Hi.'); // append an assistant message (rare) $chat->assistant('Hi.'); / append an assistant message (rare)
$reply = $chat->complete(); // run the call, return text $reply = $chat->complete(); / run the call, return text
$reply = $chat->ask('How are you?'); // = ->user(...)->complete() $reply = $chat->ask('How are you?'); / = ->user(...)->complete()
$chat->reset(); // clear messages, keep model + system $chat->reset(); / clear messages, keep model + system
$chat->history(); // [{role, content}, …] $chat->history(); / [{role, content}, ]
``` ```
## One-shot ## One-shot
@@ -40,10 +40,10 @@ echo (new \Nibiru\Module\Ai\Ai())
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('Name three Nibiru singletons.'); $chat->user('Name three Nibiru singletons.');
$singletons = $chat->complete(); // appended to history $singletons = $chat->complete(); / appended to history
$chat->user('What does the second one do?'); $chat->user('What does the second one do?');
$detail = $chat->complete(); // model has full context $detail = $chat->complete(); / model has full context
``` ```
## Overriding model + style per call ## Overriding model + style per call

View File

@@ -10,12 +10,12 @@ The Embed plugin is a thin wrapper around Ollama's `/api/embeddings` plus three
```php ```php
$embed = (new \Nibiru\Module\Ai\Ai())->embed(); $embed = (new \Nibiru\Module\Ai\Ai())->embed();
$vec = $embed->one('controller'); // float[] $vec = $embed->one('controller'); / float[]
$vectors = $embed->batch(['a', 'b', 'c']); // float[][] $vectors = $embed->batch(['a', 'b', 'c']); / float[][]
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1 $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[] $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
``` ```
## Pattern: deduplicate near-duplicate strings ## Pattern: deduplicate near-duplicate strings
@@ -54,8 +54,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
return $best[0]; return $best[0];
} }
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication' echo bestTag('User::isAuthorized', $tagVecs, $embed); / 'authentication'
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably) echo bestTag('Pageination::setTable', $tagVecs, $embed); / 'database' (probably)
``` ```
## Storage ## Storage
@@ -65,7 +65,7 @@ Embeddings are float arrays — typically 768 floats for `nomic-embed-text`, 102
Use `Embed::pack()` to base64-encode them as 4-byte floats: Use `Embed::pack()` to base64-encode them as 4-byte floats:
```php ```php
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string $compact = Embed::pack($vec); / ~4 KB ~5.3 KB base64 string
$vec = Embed::unpack($compact); $vec = Embed::unpack($compact);
``` ```

View File

@@ -62,7 +62,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
// Multi-turn // Multi-turn
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('How do I scaffold a module?'); $chat->user('How do I scaffold a module?');
$chat->user('And add Graylog hooks?'); // referrs to previous turn $chat->user('And add Graylog hooks?'); / referrs to previous turn
echo $chat->complete(); echo $chat->complete();
// Override per call // Override per call
@@ -89,7 +89,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
Compact storage: Compact storage:
```php ```php
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
``` ```
@@ -99,7 +99,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
$rag = $ai->rag('product-help'); $rag = $ai->rag('product-help');
// One-time ingestion // One-time ingestion
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
$rag->ingestFile('/var/data/manual.pdf.txt'); $rag->ingestFile('/var/data/manual.pdf.txt');
@@ -118,9 +118,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$agent = $ai->agent()->withTools([ $agent = $ai->agent()->withTools([
new Tools\PdoQuery(), // read-only SQL new Tools\PdoQuery(), / read-only SQL
new Tools\HttpGet(), // fetch URLs new Tools\HttpGet(), / fetch URLs
new Tools\FileRead(), // read project files new Tools\FileRead(), / read project files
]); ]);
echo $agent->run('How many active users registered last week?'); echo $agent->run('How many active users registered last week?');

View File

@@ -11,9 +11,9 @@ The RAG plugin is the AI module's killer feature for product builders. It turns
use Nibiru\Module\Ai\Ai; use Nibiru\Module\Ai\Ai;
$ai = new Ai(); $ai = new Ai();
$rag = $ai->rag('product-help'); // a named collection $rag = $ai->rag('product-help'); / a named collection
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/ $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
echo $rag->ask('How do I cancel my subscription?'); echo $rag->ask('How do I cancel my subscription?');
@@ -53,20 +53,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
## API reference ## API reference
```php ```php
$rag = $ai->rag('name'); // get/create a named collection $rag = $ai->rag('name'); / get/create a named collection
// --- Ingestion --- // --- Ingestion ---
$rag->ingestText($text, $metadata = []); // single chunk $rag->ingestText($text, $metadata = []); / single chunk
$count = $rag->ingestFile('path'); // returns chunks added $count = $rag->ingestFile('path'); / returns chunks added
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive $count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
// --- Querying --- // --- Querying ---
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …] $hits = $rag->search('query', $k = null); / [{score, text, metadata}, ]
$answer = $rag->ask('question', $k = null); // top-K → chat call $answer = $rag->ask('question', $k = null); / top-K chat call
// --- Maintenance --- // --- Maintenance ---
$rag->reset(); // forget everything (deletes file) $rag->reset(); / forget everything (deletes file)
$n = $rag->size(); // number of chunks $n = $rag->size(); / number of chunks
``` ```
## Tuning knobs ## Tuning knobs

View File

@@ -125,7 +125,7 @@ public function pageAction() {
View::forwardTo('/login'); View::forwardTo('/login');
return; return;
} }
// ... / ...
} }
``` ```
@@ -195,7 +195,7 @@ public function submitAction() {
http_response_code(419); http_response_code(419);
return; return;
} }
// ...handle submission... / ...handle submission...
} }
``` ```

View File

@@ -87,14 +87,14 @@ password_hash = "another-salt-for-AES_DECRYPT"
```php ```php
$cfg = \Nibiru\Config::getInstance()->getConfig(); $cfg = \Nibiru\Config::getInstance()->getConfig();
$cfg['DATABASE']['driver']; // 'pdo' $cfg['DATABASE']['driver']; / 'pdo'
$cfg['SETTINGS']['page.url']; // 'https://my-app.local' $cfg['SETTINGS']['page.url']; / 'https://my-app.local'
``` ```
For deeply-nested configs use the typed constants from the View interface: For deeply-nested configs use the typed constants from the View interface:
```php ```php
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css'] $cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
``` ```
## Module configs ## Module configs
@@ -103,7 +103,7 @@ Each module under `application/module/<name>/settings/` can carry its own INI fi
```php ```php
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; // from [USERS] section in users.ini $users->session_lifetime; / from [USERS] section in users.ini
``` ```
The Registry prefers `<module>.<env>.ini` over `<module>.ini`, so you get per-environment overrides for free. The Registry prefers `<module>.<env>.ini` over `<module>.ini`, so you get per-environment overrides for free.

View File

@@ -61,12 +61,12 @@ View::assign(['products' => $list]);
Convenience helpers from the base controller: Convenience helpers from the base controller:
```php ```php
$this->getRequest('id', false); // $_REQUEST['id'] ?? false $this->getRequest('id', false); / $_REQUEST['id'] ?? false
$this->getPost('email', ''); // $_POST['email'] ?? '' $this->getPost('email', ''); / $_POST['email'] ?? ''
$this->getGet('page', 1); // $_GET['page'] ?? 1 $this->getGet('page', 1); / $_GET['page'] ?? 1
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI'] $this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
$this->getFiles('upload'); // $_FILES['upload'] $this->getFiles('upload'); / $_FILES['upload']
$this->getSession('auth'); // $_SESSION['auth'] $this->getSession('auth'); / $_SESSION['auth']
``` ```
These exist because `Controller` is `final`-friendly: you can mock them in tests by substituting a child class. These exist because `Controller` is `final`-friendly: you can mock them in tests by substituting a child class.
@@ -76,8 +76,8 @@ These exist because `Controller` is `final`-friendly: you can mock them in tests
To redirect inside an action: To redirect inside an action:
```php ```php
View::forwardTo('/login'); // 302 to the URL, exits View::forwardTo('/login'); / 302 to the URL, exits
View::forwardToJsonHeader(); // sets Content-Type: application/json View::forwardToJsonHeader(); / sets Content-Type: application/json
``` ```
`forwardToJsonHeader()` is the canonical pattern for JSON endpoints — set the header, assign `data`, return. The view layer does the rest. `forwardToJsonHeader()` is the canonical pattern for JSON endpoints — set the header, assign `data`, return. The view layer does the rest.

View File

@@ -35,17 +35,17 @@ public function run() {
if (Config::getInstance()->getConfig() if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) { [self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); // 1. (re)generate models from schema new Model(false); / 1. (re)generate models from schema
} }
Router::getInstance()->route(); // 2. parse the URL Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); // 3. load model files Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); // 4. load module classes Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName(); $tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php"; $controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { // 5. controller file exists if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile; require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller"; $class = "Nibiru\\{$tpl}Controller";
$controller = new $class(); $controller = new $class();
@@ -54,7 +54,7 @@ public function run() {
$action = $_REQUEST['_action'] . 'Action'; $action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction(); $controller->navigationAction();
if (method_exists($controller, $action)) { if (method_exists($controller, $action)) {
$controller->$action(); // 6. optional named action $controller->$action(); / 6. optional named action
} }
$controller->pageAction(); $controller->pageAction();
} else { } else {
@@ -62,9 +62,9 @@ public function run() {
$controller->pageAction(); $controller->pageAction();
} }
Display::getInstance()->display(); // 7. render Smarty Display::getInstance()->display(); / 7. render Smarty
} else { } else {
// 8. soft 404 — render the configured error controller / 8. soft 404 render the configured error controller
} }
} }
``` ```

View File

@@ -51,8 +51,8 @@ addOpenSpan addCloseSpan
### Lifecycle ### Lifecycle
``` ```
create // reset the static buffer; call before building a new form create / reset the static buffer; call before building a new form
addForm // wrap the buffer in <form>...</form> and return as a string addForm / wrap the buffer in <form>...</form> and return as a string
``` ```
:::caution[The naming inconsistency is intentional, sort of] :::caution[The naming inconsistency is intentional, sort of]
@@ -64,7 +64,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
```php ```php
use Nibiru\Factory\Form; use Nibiru\Factory\Form;
Form::create(); // reset the static buffer Form::create(); / reset the static buffer
Form::addOpenDiv(['class' => 'form-group']); Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']); Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
@@ -220,7 +220,7 @@ class formsController extends Controller {
'required' => 'required', 'required' => 'required',
'class' => 'contacts-input form-control', 'class' => 'contacts-input form-control',
]); ]);
// ...more fields... / ...more fields...
$this->form = Form::addForm([ $this->form = Form::addForm([
'name' => 'newregister', 'name' => 'newregister',
'method' => 'post', 'method' => 'post',

View File

@@ -160,9 +160,9 @@ Read it back from anywhere:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; // 7200 $cfg->session_lifetime; / 7200
$cfg->password_min_length; // 12 $cfg->password_min_length; / 12
$cfg->allowed_roles; // [admin, editor, standard] $cfg->allowed_roles; / [admin, editor, standard]
``` ```
Environment overlays: a file named `users.production.ini` is preferred over `users.ini` when `APPLICATION_ENV=production`. Environment overlays: a file named `users.production.ini` is preferred over `users.ini` when `APPLICATION_ENV=production`.
@@ -177,7 +177,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); // internally calls notify() $analytics->trackPageView(); / internally calls notify()
``` ```
Each observer's `update($subject)` receives the analytics instance and pulls the event data it cares about. Each observer's `update($subject)` receives the analytics instance and pulls the event data it cares about.
@@ -219,7 +219,7 @@ Plugin classes live under the **plural** namespace `Plugins`:
```php ```php
// application/module/billing/plugins/invoice.php // application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; // ← plural namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ } class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
``` ```
@@ -231,7 +231,7 @@ You **don't** register your module's INI files — the [Registry](/en/core/regis
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; // [BILLING] invoice.prefixproperty $cfg->invoice_prefix; / [BILLING] invoice.prefix property
``` ```
The Registry prefers `<module>.<env>.ini` (e.g. `billing.production.ini`) when `APPLICATION_ENV` matches. The Registry prefers `<module>.<env>.ini` (e.g. `billing.production.ini`) when `APPLICATION_ENV` matches.

View File

@@ -15,7 +15,7 @@ class productsController extends Controller
{ {
public function pageAction() { public function pageAction() {
$products = new products(); $products = new products();
Pageination::setEntriesPerPage(25); // optional; default from INI Pageination::setEntriesPerPage(25); / optional; default from INI
Pageination::setTable($products); Pageination::setTable($products);
$rows = Pageination::loadTableAsArray(); $rows = Pageination::loadTableAsArray();

View File

@@ -21,7 +21,7 @@ For each module under `application/module/<name>/settings/`:
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; $users->session_lifetime;
$users->password_min_length; $users->password_min_length;
$users->allowed_roles; // array $users->allowed_roles; / array
``` ```
Inside the module itself the convention is to wrap this in a setter: Inside the module itself the convention is to wrap this in a setter:

View File

@@ -47,7 +47,7 @@ Anything past the action segment becomes a `$_REQUEST` key paired by position:
// /users/edit/42 → $_REQUEST['id'] = '42' // /users/edit/42 → $_REQUEST['id'] = '42'
public function editAction() { public function editAction() {
$id = (int) ($_REQUEST['id'] ?? 0); $id = (int) ($_REQUEST['id'] ?? 0);
// ... / ...
} }
``` ```
@@ -75,9 +75,9 @@ When a URL matches the pattern, capture groups are assigned to the named `params
## Routing helpers ## Routing helpers
```php ```php
Router::getInstance()->currentPage(); // 'products' Router::getInstance()->currentPage(); / 'products'
Router::getInstance()->tplName(); // 'products' (controller stem for templates) Router::getInstance()->tplName(); / 'products' (controller stem for templates)
Router::getInstance()->getController(); // alias for currentPage() Router::getInstance()->getController(); / alias for currentPage()
``` ```
These are useful inside controllers and templates: These are useful inside controllers and templates:

View File

@@ -8,7 +8,7 @@ description: Buttons, cards, callouts, hero — copy-paste ready.
A black-on-cream rectangle. Editorial. No gradient, no glow. A black-on-cream rectangle. Editorial. No gradient, no glow.
```html ```html
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/en/start/installation/">
<span>Read the docs</span> <span>Read the docs</span>
<span class="atelier-button__arrow" aria-hidden="true"></span> <span class="atelier-button__arrow" aria-hidden="true"></span>
</a> </a>
@@ -179,7 +179,7 @@ Asymmetric two-column grid. Big editorial number behind the copy. The brand mark
Nibiru is a modular PHP framework for builders who ship. Nibiru is a modular PHP framework for builders who ship.
</p> </p>
<div class="atelier-hero__cta"> <div class="atelier-hero__cta">
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/en/start/installation/">
Read the docs <span aria-hidden="true"></span> Read the docs <span aria-hidden="true"></span>
</a> </a>
</div> </div>

View File

@@ -6,7 +6,7 @@ description: Downloadable Nibiru framework training corpus — chunks, instructi
import DownloadsManifest from '../../../components/DownloadsManifest.astro'; import DownloadsManifest from '../../../components/DownloadsManifest.astro';
A pre-built training corpus for fine-tuning your own LoRA on Nibiru. Generated A pre-built training corpus for fine-tuning your own LoRA on Nibiru. Generated
deterministically from two sources: the deep [framework reference](/en/reference/) deterministically from two sources: the deep [framework reference](https://github.com/alllinux/Nibiru/blob/master/docs/scripts/extraction/framework-reference-v2.md)
(every public factory, namespace, idiom and gotcha cited file:line) and the (every public factory, namespace, idiom and gotcha cited file:line) and the
public docs in five languages. public docs in five languages.

View File

@@ -70,7 +70,7 @@ Multiple trackers without controller coupling.
$analytics = new Analytics(); $analytics = new Analytics();
$analytics->attach(new Plugin\Matomo()); $analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible()); $analytics->attach(new Plugin\Plausible());
$analytics->trackPageView(); // calls notify() internally $analytics->trackPageView(); / calls notify() internally
``` ```
Each observer's `update($subject)` pulls only the fields it cares about. Adding a tracker is a one-line change. Each observer's `update($subject)` pulls only the fields it cares about. Adding a tracker is a one-line change.

View File

@@ -56,7 +56,7 @@ public function detailAction()
try { try {
$machine = Machine::init()->getMachine((int) $machineId); $machine = Machine::init()->getMachine((int) $machineId);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$machine = null; // DB blip → page still renders with fallback. $machine = null; / DB blip page still renders with fallback.
} }
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId"; $machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";

View File

@@ -53,7 +53,7 @@ class Cms implements Interfaces\Cms, SplSubject
use Traits\PageBuilderForm; use Traits\PageBuilderForm;
use Traits\CmsPageStructureModifier; use Traits\CmsPageStructureModifier;
use Traits\FormElements; use Traits\FormElements;
// …8 more traits / …8 more traits
} }
``` ```
@@ -117,7 +117,7 @@ The API controller takes the inverse approach: a whitelist of public endpoints i
```php ```php
// public endpoints can be listed up-front, auth wraps the rest. // public endpoints can be listed up-front, auth wraps the rest.
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) { if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
// public, skip auth / public, skip auth
} else { } else {
$this->user = new User(); $this->user = new User();
$this->acl = new Acl(); $this->acl = new Acl();
@@ -148,8 +148,8 @@ class Machineryscout implements IModule, \SplSubject
} }
public function indexMachines(): void { public function indexMachines(): void {
// …do the work… / …do the work…
$this->notify(); // analytics, cache invalidator, audit log all see it. $this->notify(); / analytics, cache invalidator, audit log all see it.
} }
} }
``` ```

View File

@@ -60,7 +60,7 @@ Fragmentos sin procesar para su uso como datos de recuperación RAG:
"content": "Modules implementing `SplSubject` can broadcast events…" "content": "Modules implementing `SplSubject` can broadcast events…"
} }
``` ```
Este es exactamente el archivo que usa internamente [Oracle](/ai/oracle/). Este es exactamente el archivo que usa internamente [Oracle](/es/ai/oracle/).
## Cómo se derivan los fragmentos ## Cómo se derivan los fragmentos

View File

@@ -10,19 +10,19 @@ El complemento de chat es la pieza más simple del módulo de IA. Envuelve la AP
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$chat = $ai->chat(); $chat = $ai->chat();
$chat->system('Be terse.'); // optional system prompt $chat->system('Be terse.'); / optional system prompt
$chat->model('qwen2.5-coder:14b'); // override the configured model $chat->model('qwen2.5-coder:14b'); / override the configured model
$chat->temperature(0.2); // override config $chat->temperature(0.2); / override config
$chat->maxTokens(512); // override config $chat->maxTokens(512); / override config
$chat->user('Hello'); // append a user message $chat->user('Hello'); / append a user message
$chat->assistant('Hi.'); // append an assistant message (rare) $chat->assistant('Hi.'); / append an assistant message (rare)
$reply = $chat->complete(); // run the call, return text $reply = $chat->complete(); / run the call, return text
$reply = $chat->ask('How are you?'); // = ->user(...)->complete() $reply = $chat->ask('How are you?'); / = ->user(...)->complete()
$chat->reset(); // clear messages, keep model + system $chat->reset(); / clear messages, keep model + system
$chat->history(); // [{role, content}, …] $chat->history(); / [{role, content}, ]
``` ```
## En un solo paso ## En un solo paso
```php ```php
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('Name three Nibiru singletons.'); $chat->user('Name three Nibiru singletons.');
$singletons = $chat->complete(); // appended to history $singletons = $chat->complete(); / appended to history
$chat->user('What does the second one do?'); $chat->user('What does the second one do?');
$detail = $chat->complete(); // model has full context $detail = $chat->complete(); / model has full context
``` ```
## Sobrescribir modelo y estilo por llamada ## Sobrescribir modelo y estilo por llamada
```php ```php

View File

@@ -9,12 +9,12 @@ El complemento Embed es un envoltorio ligero alrededor de `/api/embeddings` de O
```php ```php
$embed = (new \Nibiru\Module\Ai\Ai())->embed(); $embed = (new \Nibiru\Module\Ai\Ai())->embed();
$vec = $embed->one('controller'); // float[] $vec = $embed->one('controller'); / float[]
$vectors = $embed->batch(['a', 'b', 'c']); // float[][] $vectors = $embed->batch(['a', 'b', 'c']); / float[][]
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1 $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[] $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
``` ```
## Patrón: eliminar cadenas similares ## Patrón: eliminar cadenas similares
```php ```php
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
return $best[0]; return $best[0];
} }
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication' echo bestTag('User::isAuthorized', $tagVecs, $embed); / 'authentication'
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably) echo bestTag('Pageination::setTable', $tagVecs, $embed); / 'database' (probably)
``` ```
## Almacenamiento ## Almacenamiento
@@ -58,7 +58,7 @@ Las incrustaciones son arreglos de flotantes — generalmente 768 flotantes para
Utiliza `Embed::pack()` para codificarlos en base64 como flotantes de 4 bytes: Utiliza `Embed::pack()` para codificarlos en base64 como flotantes de 4 bytes:
```php ```php
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string $compact = Embed::pack($vec); / ~4 KB ~5.3 KB base64 string
$vec = Embed::unpack($compact); $vec = Embed::unpack($compact);
``` ```
El complemento RAG utiliza este formato internamente para sus archivos JSON. El complemento RAG utiliza este formato internamente para sus archivos JSON.

View File

@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
// Multi-turn // Multi-turn
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('How do I scaffold a module?'); $chat->user('How do I scaffold a module?');
$chat->user('And add Graylog hooks?'); // referrs to previous turn $chat->user('And add Graylog hooks?'); / referrs to previous turn
echo $chat->complete(); echo $chat->complete();
// Override per call // Override per call
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
``` ```
Almacenamiento compacto: Almacenamiento compacto:
```php ```php
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
``` ```
### 3. RAG — ingesta, recuperación, anclaje ### 3. RAG — ingesta, recuperación, anclaje
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
$rag = $ai->rag('product-help'); $rag = $ai->rag('product-help');
// One-time ingestion // One-time ingestion
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
$rag->ingestFile('/var/data/manual.pdf.txt'); $rag->ingestFile('/var/data/manual.pdf.txt');
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$agent = $ai->agent()->withTools([ $agent = $ai->agent()->withTools([
new Tools\PdoQuery(), // read-only SQL new Tools\PdoQuery(), / read-only SQL
new Tools\HttpGet(), // fetch URLs new Tools\HttpGet(), / fetch URLs
new Tools\FileRead(), // read project files new Tools\FileRead(), / read project files
]); ]);
echo $agent->run('How many active users registered last week?'); echo $agent->run('How many active users registered last week?');
@@ -154,7 +154,7 @@ La filosofía de diseño:
## Siguiente ## Siguiente
- [Referencia del complemento de chat](/es/ai/modulo/chat/) - [Referencia del complemento de chat](/es/ai/module/chat/)
- [Referencia del complemento RAG](/es/ai/modulo/rag/) - [Referencia del complemento RAG](/es/ai/module/rag/)
- [Referencia del complemento de agente](/es/ai/modulo/agent/) - [Referencia del complemento de agente](/es/ai/module/agent/)
- [Entrenamiento nibiru-coder](/es/ai/modulo/training/) - [Entrenamiento nibiru-coder](/es/ai/module/training/)

View File

@@ -10,9 +10,9 @@ El complemento RAG es la característica clave del módulo de IA para los constr
use Nibiru\Module\Ai\Ai; use Nibiru\Module\Ai\Ai;
$ai = new Ai(); $ai = new Ai();
$rag = $ai->rag('product-help'); // a named collection $rag = $ai->rag('product-help'); / a named collection
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/ $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
echo $rag->ask('How do I cancel my subscription?'); echo $rag->ask('How do I cancel my subscription?');
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
``` ```
## Referencia de la API ## Referencia de la API
```php ```php
$rag = $ai->rag('name'); // get/create a named collection $rag = $ai->rag('name'); / get/create a named collection
// --- Ingestion --- // --- Ingestion ---
$rag->ingestText($text, $metadata = []); // single chunk $rag->ingestText($text, $metadata = []); / single chunk
$count = $rag->ingestFile('path'); // returns chunks added $count = $rag->ingestFile('path'); / returns chunks added
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive $count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
// --- Querying --- // --- Querying ---
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …] $hits = $rag->search('query', $k = null); / [{score, text, metadata}, ]
$answer = $rag->ask('question', $k = null); // top-K → chat call $answer = $rag->ask('question', $k = null); / top-K chat call
// --- Maintenance --- // --- Maintenance ---
$rag->reset(); // forget everything (deletes file) $rag->reset(); / forget everything (deletes file)
$n = $rag->size(); // number of chunks $n = $rag->size(); / number of chunks
``` ```
## Perillas de ajuste ## Perillas de ajuste
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
## ¿Qué sigue? ## ¿Qué sigue?
- [Plugin de agente →](/es/ai/modulo/agente/) para herramientas, no recuperación. - [Plugin de agente →](/es/ai/module/agent/) para herramientas, no recuperación.
- [Entrenamiento nibiru-coder →](/es/ai/modulo/entrenamiento/) para que el chat responda a la mitad en voz del marco. - [Entrenamiento nibiru-coder →](/es/ai/module/training/) para que el chat responda a la mitad en voz del marco.

View File

@@ -114,7 +114,7 @@ public function pageAction() {
View::forwardTo('/login'); View::forwardTo('/login');
return; return;
} }
// ... / ...
} }
``` ```
Para las verificaciones basadas en roles, las aplicaciones de demostración utilizan el complemento `Acl` del mismo módulo: Para las verificaciones basadas en roles, las aplicaciones de demostración utilizan el complemento `Acl` del mismo módulo:
@@ -178,7 +178,7 @@ public function submitAction() {
http_response_code(419); http_response_code(419);
return; return;
} }
// ...handle submission... / ...handle submission...
} }
``` ```
Incorpore `<input type="hidden" name="csrf" value="{$csrf}">` en su formulario. Incorpore `<input type="hidden" name="csrf" value="{$csrf}">` en su formulario.

View File

@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
## Leyendo la configuración ## Leyendo la configuración
```php ```php
$cfg = \Nibiru\Config::getInstance()->getConfig(); $cfg = \Nibiru\Config::getInstance()->getConfig();
$cfg['DATABASE']['driver']; // 'pdo' $cfg['DATABASE']['driver']; / 'pdo'
$cfg['SETTINGS']['page.url']; // 'https://my-app.local' $cfg['SETTINGS']['page.url']; / 'https://my-app.local'
``` ```
Para configuraciones profundamente anidadas, utilice las constantes tipadas de la interfaz View: Para configuraciones profundamente anidadas, utilice las constantes tipadas de la interfaz View:
```php ```php
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css'] $cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
``` ```
## Configuraciones del módulo ## Configuraciones del módulo
Cada módulo bajo `application/module/<nombre>/settings/` puede llevar sus propios archivos INI. El Registro los detecta automáticamente y los expone a través de: Cada módulo bajo `application/module/<nombre>/settings/` puede llevar sus propios archivos INI. El Registro los detecta automáticamente y los expone a través de:
```php ```php
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; // from [USERS] section in users.ini $users->session_lifetime; / from [USERS] section in users.ini
``` ```
El Registro prefiere `<módulo>.<entorno>.ini` sobre `<módulo>.ini`, por lo que obtienes anulaciones por entorno de forma gratuita. El Registro prefiere `<módulo>.<entorno>.ini` sobre `<módulo>.ini`, por lo que obtienes anulaciones por entorno de forma gratuita.

View File

@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
``` ```
Ayudantes de conveniencia del controlador base: Ayudantes de conveniencia del controlador base:
```php ```php
$this->getRequest('id', false); // $_REQUEST['id'] ?? false $this->getRequest('id', false); / $_REQUEST['id'] ?? false
$this->getPost('email', ''); // $_POST['email'] ?? '' $this->getPost('email', ''); / $_POST['email'] ?? ''
$this->getGet('page', 1); // $_GET['page'] ?? 1 $this->getGet('page', 1); / $_GET['page'] ?? 1
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI'] $this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
$this->getFiles('upload'); // $_FILES['upload'] $this->getFiles('upload'); / $_FILES['upload']
$this->getSession('auth'); // $_SESSION['auth'] $this->getSession('auth'); / $_SESSION['auth']
``` ```
Estos existen porque `Controller` es amigable con `final`: puedes simularlos en pruebas sustituyendo una clase hija. Estos existen porque `Controller` es amigable con `final`: puedes simularlos en pruebas sustituyendo una clase hija.
@@ -69,8 +69,8 @@ Estos existen porque `Controller` es amigable con `final`: puedes simularlos en
Para redirigir dentro de una acción: Para redirigir dentro de una acción:
```php ```php
View::forwardTo('/login'); // 302 to the URL, exits View::forwardTo('/login'); / 302 to the URL, exits
View::forwardToJsonHeader(); // sets Content-Type: application/json View::forwardToJsonHeader(); / sets Content-Type: application/json
``` ```
`forwardToJsonHeader()` es el patrón canónico para puntos finales JSON — establece la cabecera, asigna `data`, y devuelve. La capa de vista se encarga del resto. `forwardToJsonHeader()` es el patrón canónico para puntos finales JSON — establece la cabecera, asigna `data`, y devuelve. La capa de vista se encarga del resto.

View File

@@ -30,17 +30,17 @@ public function run() {
if (Config::getInstance()->getConfig() if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) { [self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); // 1. (re)generate models from schema new Model(false); / 1. (re)generate models from schema
} }
Router::getInstance()->route(); // 2. parse the URL Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); // 3. load model files Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); // 4. load module classes Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName(); $tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php"; $controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { // 5. controller file exists if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile; require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller"; $class = "Nibiru\\{$tpl}Controller";
$controller = new $class(); $controller = new $class();
@@ -49,7 +49,7 @@ public function run() {
$action = $_REQUEST['_action'] . 'Action'; $action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction(); $controller->navigationAction();
if (method_exists($controller, $action)) { if (method_exists($controller, $action)) {
$controller->$action(); // 6. optional named action $controller->$action(); / 6. optional named action
} }
$controller->pageAction(); $controller->pageAction();
} else { } else {
@@ -57,9 +57,9 @@ public function run() {
$controller->pageAction(); $controller->pageAction();
} }
Display::getInstance()->display(); // 7. render Smarty Display::getInstance()->display(); / 7. render Smarty
} else { } else {
// 8. soft 404 — render the configured error controller / 8. soft 404 render the configured error controller
} }
} }
``` ```

View File

@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
``` ```
### Ciclo de vida ### Ciclo de vida
``` ```
create // reset the static buffer; call before building a new form create / reset the static buffer; call before building a new form
addForm // wrap the buffer in <form>...</form> and return as a string addForm / wrap the buffer in <form>...</form> and return as a string
``` ```
:::caution[La inconsistencia de nombres es intencional, más bien] :::caution[La inconsistencia de nombres es intencional, más bien]
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — el prefijo coincide si el elemento es un `<input type="…">` (prefijo Input) o alguna otra etiqueta (prefijo Type). Es incómodo pero estable; la CLI de `./nibiru -c` usa el mismo patrón, por lo que la memoria muscular funciona. `addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — el prefijo coincide si el elemento es un `<input type="…">` (prefijo Input) o alguna otra etiqueta (prefijo Type). Es incómodo pero estable; la CLI de `./nibiru -c` usa el mismo patrón, por lo que la memoria muscular funciona.
@@ -50,7 +50,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
```php ```php
use Nibiru\Factory\Form; use Nibiru\Factory\Form;
Form::create(); // reset the static buffer Form::create(); / reset the static buffer
Form::addOpenDiv(['class' => 'form-group']); Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']); Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
@@ -186,7 +186,7 @@ class formsController extends Controller {
'required' => 'required', 'required' => 'required',
'class' => 'contacts-input form-control', 'class' => 'contacts-input form-control',
]); ]);
// ...more fields... / ...more fields...
$this->form = Form::addForm([ $this->form = Form::addForm([
'name' => 'newregister', 'name' => 'newregister',
'method' => 'post', 'method' => 'post',

View File

@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
Léelo desde cualquier lugar: Léelo desde cualquier lugar:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; // 7200 $cfg->session_lifetime; / 7200
$cfg->password_min_length; // 12 $cfg->password_min_length; / 12
$cfg->allowed_roles; // [admin, editor, standard] $cfg->allowed_roles; / [admin, editor, standard]
``` ```
Capas de entorno: un archivo llamado `users.production.ini` se prefiere sobre `users.ini` cuando `APPLICATION_ENV=production`. Capas de entorno: un archivo llamado `users.production.ini` se prefiere sobre `users.ini` cuando `APPLICATION_ENV=production`.
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); // internally calls notify() $analytics->trackPageView(); / internally calls notify()
``` ```
Cada observador recibe la instancia de análisis en su método `update($subject)` y extrae los datos del evento que le interesan. Cada observador recibe la instancia de análisis en su método `update($subject)` y extrae los datos del evento que le interesan.
@@ -200,7 +200,7 @@ Los nombres son **nombres de carpetas en minúsculas**, exactamente como aparece
Las clases de plugin residen bajo el espacio de nombres **plural** `Plugins`: Las clases de plugin residen bajo el espacio de nombres **plural** `Plugins`:
```php ```php
// application/module/billing/plugins/invoice.php // application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; // ← plural namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ } class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
``` ```
Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La plantilla de la CLI (`./nibiru -m billing`) genera el espacio de nombres correcto para ti. Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La plantilla de la CLI (`./nibiru -m billing`) genera el espacio de nombres correcto para ti.
@@ -210,7 +210,7 @@ Desacuerda el espacio de nombres y obtendrás fallos en la carga automática. La
No **registras** los archivos INI de tu módulo — el [Registro](/es/core/registry/) los descubre automáticamente recorriendo `application/module/<name>/settings/*.ini` después de que `[AUTOLOADER]` carga la clase del módulo. Cada sección `[<MODULE>]` (en mayúsculas) de un INI se vuelve disponible como: No **registras** los archivos INI de tu módulo — el [Registro](/es/core/registry/) los descubre automáticamente recorriendo `application/module/<name>/settings/*.ini` después de que `[AUTOLOADER]` carga la clase del módulo. Cada sección `[<MODULE>]` (en mayúsculas) de un INI se vuelve disponible como:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; // [BILLING] invoice.prefixproperty $cfg->invoice_prefix; / [BILLING] invoice.prefix property
``` ```
El Registro prefiere `<módulo>.<entorno>.ini` (por ejemplo, `facturación.producción.ini`) cuando `APPLICATION_ENV` coincide. El Registro prefiere `<módulo>.<entorno>.ini` (por ejemplo, `facturación.producción.ini`) cuando `APPLICATION_ENV` coincide.

View File

@@ -14,7 +14,7 @@ class productsController extends Controller
{ {
public function pageAction() { public function pageAction() {
$products = new products(); $products = new products();
Pageination::setEntriesPerPage(25); // optional; default from INI Pageination::setEntriesPerPage(25); / optional; default from INI
Pageination::setTable($products); Pageination::setTable($products);
$rows = Pageination::loadTableAsArray(); $rows = Pageination::loadTableAsArray();

View File

@@ -20,7 +20,7 @@ Para cada módulo bajo `application/module/<nombre>/settings/`:
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; $users->session_lifetime;
$users->password_min_length; $users->password_min_length;
$users->allowed_roles; // array $users->allowed_roles; / array
``` ```
Dentro del módulo mismo, la convención es envolver esto en un setter: Dentro del módulo mismo, la convención es envolver esto en un setter:
```php ```php

View File

@@ -40,7 +40,7 @@ Cualquier cosa después del segmento de acción se convierte en una clave `$_REQ
// /users/edit/42 → $_REQUEST['id'] = '42' // /users/edit/42 → $_REQUEST['id'] = '42'
public function editAction() { public function editAction() {
$id = (int) ($_REQUEST['id'] ?? 0); $id = (int) ($_REQUEST['id'] ?? 0);
// ... / ...
} }
``` ```
Para parámetros no numéricos o nombrados, prefiera cadenas de consulta: Para parámetros no numéricos o nombrados, prefiera cadenas de consulta:
@@ -62,9 +62,9 @@ Cuando una URL coincide con el patrón, los grupos capturados se asignan a las c
## Ayudantes de enrutamiento ## Ayudantes de enrutamiento
```php ```php
Router::getInstance()->currentPage(); // 'products' Router::getInstance()->currentPage(); / 'products'
Router::getInstance()->tplName(); // 'products' (controller stem for templates) Router::getInstance()->tplName(); / 'products' (controller stem for templates)
Router::getInstance()->getController(); // alias for currentPage() Router::getInstance()->getController(); / alias for currentPage()
``` ```
Estos son útiles dentro de los controladores y plantillas: Estos son útiles dentro de los controladores y plantillas:
```smarty ```smarty

View File

@@ -7,7 +7,7 @@ description: "Botones, tarjetas, llamadas a la acción, héroe — listos para c
Un rectángulo negro sobre crema. Editorial. Sin gradiente, sin brillo. Un rectángulo negro sobre crema. Editorial. Sin gradiente, sin brillo.
```html ```html
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/es/start/installation/">
<span>Read the docs</span> <span>Read the docs</span>
<span class="atelier-button__arrow" aria-hidden="true"></span> <span class="atelier-button__arrow" aria-hidden="true"></span>
</a> </a>
@@ -162,7 +162,7 @@ Cuadrícula asimétrica de dos columnas. Un número editorial grande detrás del
Nibiru is a modular PHP framework for builders who ship. Nibiru is a modular PHP framework for builders who ship.
</p> </p>
<div class="atelier-hero__cta"> <div class="atelier-hero__cta">
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/es/start/installation/">
Read the docs <span aria-hidden="true"></span> Read the docs <span aria-hidden="true"></span>
</a> </a>
</div> </div>

View File

@@ -48,7 +48,7 @@ Eso es suficiente para estar en marca.
## ¿Qué se documenta aquí? ## ¿Qué se documenta aquí?
- [Paleta](/es/diseno/paleta/) — cada color con su rol. - [Paleta](/es/design/palette/) — cada color con su rol.
- [Tipografía](/es/diseno/tipografia/) — los ejes variables de Bricolage utilizados en serio. - [Tipografía](/es/design/typography/) — los ejes variables de Bricolage utilizados en serio.
- [Componentes](/es/diseno/componentes/) — botones, tarjetas, llamadas a la atención, el lanzador Oracle. - [Componentes](/es/design/components/) — botones, tarjetas, llamadas a la atención, el lanzador Oracle.
- [Movimiento](/es/diseno/movimiento/) — respirar, desvanecerse, sin flash. - [Movimiento](/es/design/motion/) — respirar, desvanecerse, sin flash.

View File

@@ -19,73 +19,25 @@ hero:
variant: minimal variant: minimal
--- ---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; import CometTrail from '../../../components/CometTrail.astro';
import MmvcStage from '../../../components/MmvcStage.astro';
import MissionControl from '../../../components/MissionControl.astro';
import LaunchSequence from '../../../components/LaunchSequence.astro';
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
import EditorialContent from '../../../components/EditorialContent.astro';
import LandingFooter from '../../../components/LandingFooter.astro';
import ToTop from '../../../components/ToTop.astro';
import LandingScripts from '../../../components/LandingScripts.astro';
## Una constelación de capacidades <CometTrail />
<MmvcStage />
<MissionControl />
<LaunchSequence />
<SpacecraftGrid />
<EditorialContent />
<LandingFooter />
<ToTop />
<CardGrid stagger> {/* Loads three.js + the original mockup scene code. MUST be the last node so
<Card title="Arquitectura MMVC" icon="puzzle"> every #id the scene targets exists in the DOM when the script runs. */}
Los módulos envuelven MVC con traits, plugins, interfaces y configuraciones. La segunda **M** está acoplada de forma flexible, con el patrón observador `SplSubject` integrado. <LandingScripts />
</Card>
<Card title="Núcleo multi-base de datos" icon="seti:db">
Cinco controladores en órbita: `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) y `postgresql`. Cambia con solo modificar una clave INI.
</Card>
<Card title="Vistas Smarty" icon="document">
Vistas dirigidas por plantillas, un ayudante global `View::assign()` y una caché caliente en `templates_c`. Más parciales compartidos, includes de navegación y plantillas de paginación.
</Card>
<Card title="Constructor de formularios fluido" icon="pencil">
Más de 28 tipos de campo — text, password, switch, color, range, file upload — compuestos mediante llamadas estáticas a `Form::add…` y renderizados como un único string HTML.
</Card>
<Card title="CLI real" icon="seti:powershell">
`./nibiru` genera módulos, controladores, plugins, migraciones y páginas CMS. Ejecuta migraciones contra `local`, `staging` o `production`.
</Card>
<Card title="IA nativa, pronto" icon="rocket">
Nibiru se está convirtiendo en el primer framework PHP con un <a href="/es/ai/oracle/">Oráculo basado en RAG</a> entrenado con su propio conocimiento — y un corpus publicado para futuros fine-tunes.
</Card>
</CardGrid>
## Inicio rápido
```bash
# Clonar
git clone https://github.com/alllinux/Nibiru mi-app && cd mi-app
# Instalar dependencias PHP (Smarty, PHPMailer, Guzzle, …)
composer install
# Permisos + arranque de carpetas
./nibiru -s
# Ejecutar la primera migración
./nibiru -mi local
# Generar un controlador
./nibiru -c products
```
```php
// application/controller/productsController.php
namespace Nibiru;
use Nibiru\Adapter\Controller;
class productsController extends Controller {
public function pageAction() {
View::assign([
'title' => 'Productos — Nibiru',
'products' => [['id' => 1, 'name' => 'Recubrimiento de Oro Marduk']],
]);
}
public function navigationAction() {
JsonNavigation::getInstance()->loadJsonNavigationArray();
}
}
```
## A dónde ir después
<CardGrid>
<LinkCard title="¿Qué es Nibiru?" href="/es/start/what-is-nibiru/" description="El recorrido de 90 segundos: MMVC, el dispatcher, el ciclo de vida de las peticiones." />
<LinkCard title="Arquitectura" href="/core/architecture/" description="Cómo módulos, controladores, vistas y el registro orbitan entre sí." />
<LinkCard title="Casos de uso" href="/showcase/projects/" description="Apps reales en producción sobre Nibiru — facturación, e-commerce, PIM industrial." />
<LinkCard title="Pregunta al Oráculo" href="/es/ai/oracle/" description="Abre el chat flotante. El Oráculo está fundamentado en esta misma documentación." />
</CardGrid>

View File

@@ -61,7 +61,7 @@ Varios rastreadores sin acoplamiento con el controlador.
$analytics = new Analytics(); $analytics = new Analytics();
$analytics->attach(new Plugin\Matomo()); $analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible()); $analytics->attach(new Plugin\Plausible());
$analytics->trackPageView(); // calls notify() internally $analytics->trackPageView(); / calls notify() internally
``` ```
Cada observador extrae solo los campos que le interesan con su `update($subject)`. Agregar un rastreador es un cambio de una línea. Cada observador extrae solo los campos que le interesan con su `update($subject)`. Agregar un rastreador es un cambio de una línea.

View File

@@ -55,7 +55,7 @@ public function detailAction()
try { try {
$machine = Machine::init()->getMachine((int) $machineId); $machine = Machine::init()->getMachine((int) $machineId);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$machine = null; // DB blip → page still renders with fallback. $machine = null; / DB blip page still renders with fallback.
} }
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId"; $machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
@@ -110,7 +110,7 @@ Inserta una nueva plantilla Smarty en el sistema, el editor sabe de inmediato cu
## ¿Qué realmente es especial, resumido? ## ¿Qué realmente es especial, resumido?
Los cinco diferenciadores a continuación se extraen de los códigos fuente anteriores. Cada uno enlaza a su evidencia en la página [¿Por qué Nibiru](/es/por-que-nibiru/). Los cinco diferenciadores a continuación se extraen de los códigos fuente anteriores. Cada uno enlaza a su evidencia en la página [¿Por qué Nibiru](/es/why-nibiru/).
| | ¿Qué hace Nibiru? | ¿Qué hace Laravel/Symfony? | | | ¿Qué hace Nibiru? | ¿Qué hace Laravel/Symfony? |
|---|---|---| |---|---|---|
@@ -120,7 +120,7 @@ Los cinco diferenciadores a continuación se extraen de los códigos fuente ante
| Auth | 3 líneas en el constructor del controlador. | Pila de middleware + clases de política + portales. | | Auth | 3 líneas en el constructor del controlador. | Pila de middleware + clases de política + portales. |
| Eventos | `SplSubject` + `SplObserver` de la librería estándar de PHP. | Despachador de eventos personalizado + registro de oyentes + cola. | | Eventos | `SplSubject` + `SplObserver` de la librería estándar de PHP. | Despachador de eventos personalizado + registro de oyentes + cola. |
Lee la [desglose completa con referencias de código →](/es/porque-nibiru/) Lee la [desglose completa con referencias de código →](/es/why-nibiru/)
--- ---

View File

@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
use Traits\PageBuilderForm; use Traits\PageBuilderForm;
use Traits\CmsPageStructureModifier; use Traits\CmsPageStructureModifier;
use Traits\FormElements; use Traits\FormElements;
// …8 more traits / …8 more traits
} }
``` ```
**Efecto neto**: cero inyección de constructores, cero registro de proveedores de servicios, cero llamadas a `bind()` / `singleton()` en un archivo de configuración. Un nuevo desarrollador puede buscar el nombre del trait y ver todos los llamadores. **Efecto neto**: cero inyección de constructores, cero registro de proveedores de servicios, cero llamadas a `bind()` / `singleton()` en un archivo de configuración. Un nuevo desarrollador puede buscar el nombre del trait y ver todos los llamadores.
@@ -108,7 +108,7 @@ El controlador de la API toma un enfoque inverso: una lista blanca de puntos fin
```php ```php
// public endpoints can be listed up-front, auth wraps the rest. // public endpoints can be listed up-front, auth wraps the rest.
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) { if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
// public, skip auth / public, skip auth
} else { } else {
$this->user = new User(); $this->user = new User();
$this->acl = new Acl(); $this->acl = new Acl();
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
} }
public function indexMachines(): void { public function indexMachines(): void {
// …do the work… / …do the work…
$this->notify(); // analytics, cache invalidator, audit log all see it. $this->notify(); / analytics, cache invalidator, audit log all see it.
} }
} }
``` ```
@@ -154,4 +154,4 @@ Un desarrollador solitario o un pequeño equipo puede construir y *operar* una a
Si prefieres ver tu código, tómalo. Si prefieres ver tu código, tómalo.
→ [Lee la presentación →](/es/presentacion/proyectos/) → [Lee la presentación →](/es/showcase/projects/)

View File

@@ -60,7 +60,7 @@ Blocs bruts à utiliser comme données de récupération RAG :
"content": "Modules implementing `SplSubject` can broadcast events…" "content": "Modules implementing `SplSubject` can broadcast events…"
} }
``` ```
Ceci est exactement le fichier que l'[Oracle](/ai/oracle/) utilise en interne. Ceci est exactement le fichier que l'[Oracle](/fr/ai/oracle/) utilise en interne.
## Comment les morceaux sont dérivés ## Comment les morceaux sont dérivés

View File

@@ -10,19 +10,19 @@ Le plugin de chat est la pièce la plus simple du module IA. Il encapsule l'API
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$chat = $ai->chat(); $chat = $ai->chat();
$chat->system('Be terse.'); // optional system prompt $chat->system('Be terse.'); / optional system prompt
$chat->model('qwen2.5-coder:14b'); // override the configured model $chat->model('qwen2.5-coder:14b'); / override the configured model
$chat->temperature(0.2); // override config $chat->temperature(0.2); / override config
$chat->maxTokens(512); // override config $chat->maxTokens(512); / override config
$chat->user('Hello'); // append a user message $chat->user('Hello'); / append a user message
$chat->assistant('Hi.'); // append an assistant message (rare) $chat->assistant('Hi.'); / append an assistant message (rare)
$reply = $chat->complete(); // run the call, return text $reply = $chat->complete(); / run the call, return text
$reply = $chat->ask('How are you?'); // = ->user(...)->complete() $reply = $chat->ask('How are you?'); / = ->user(...)->complete()
$chat->reset(); // clear messages, keep model + system $chat->reset(); / clear messages, keep model + system
$chat->history(); // [{role, content}, …] $chat->history(); / [{role, content}, ]
``` ```
## Un coup unique ## Un coup unique
```php ```php
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('Name three Nibiru singletons.'); $chat->user('Name three Nibiru singletons.');
$singletons = $chat->complete(); // appended to history $singletons = $chat->complete(); / appended to history
$chat->user('What does the second one do?'); $chat->user('What does the second one do?');
$detail = $chat->complete(); // model has full context $detail = $chat->complete(); / model has full context
``` ```
## Remplacement du modèle et du style par appel ## Remplacement du modèle et du style par appel
```php ```php

View File

@@ -9,12 +9,12 @@ Le plugin Embed est un simple wrapper autour de `/api/embeddings` d'Ollama, plus
```php ```php
$embed = (new \Nibiru\Module\Ai\Ai())->embed(); $embed = (new \Nibiru\Module\Ai\Ai())->embed();
$vec = $embed->one('controller'); // float[] $vec = $embed->one('controller'); / float[]
$vectors = $embed->batch(['a', 'b', 'c']); // float[][] $vectors = $embed->batch(['a', 'b', 'c']); / float[][]
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1 $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[] $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
``` ```
## Modèle : dédoublonner des chaînes similaires ## Modèle : dédoublonner des chaînes similaires
```php ```php
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
return $best[0]; return $best[0];
} }
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication' echo bestTag('User::isAuthorized', $tagVecs, $embed); / 'authentication'
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably) echo bestTag('Pageination::setTable', $tagVecs, $embed); / 'database' (probably)
``` ```
## Stockage ## Stockage
@@ -58,7 +58,7 @@ Les embeddings sont des tableaux de flottants — généralement 768 flottants p
Utilisez `Embed::pack()` pour les encoder en base64 comme des flottants sur 4 octets : Utilisez `Embed::pack()` pour les encoder en base64 comme des flottants sur 4 octets :
```php ```php
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string $compact = Embed::pack($vec); / ~4 KB ~5.3 KB base64 string
$vec = Embed::unpack($compact); $vec = Embed::unpack($compact);
``` ```
Le plugin RAG utilise ce format internalement pour ses fichiers JSON. Le plugin RAG utilise ce format internalement pour ses fichiers JSON.

View File

@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
// Multi-turn // Multi-turn
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('How do I scaffold a module?'); $chat->user('How do I scaffold a module?');
$chat->user('And add Graylog hooks?'); // referrs to previous turn $chat->user('And add Graylog hooks?'); / referrs to previous turn
echo $chat->complete(); echo $chat->complete();
// Override per call // Override per call
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
``` ```
Stockage compact : Stockage compact :
```php ```php
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
``` ```
### 3. RAG — ingérer, récupérer, ancrer ### 3. RAG — ingérer, récupérer, ancrer
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
$rag = $ai->rag('product-help'); $rag = $ai->rag('product-help');
// One-time ingestion // One-time ingestion
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
$rag->ingestFile('/var/data/manual.pdf.txt'); $rag->ingestFile('/var/data/manual.pdf.txt');
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$agent = $ai->agent()->withTools([ $agent = $ai->agent()->withTools([
new Tools\PdoQuery(), // read-only SQL new Tools\PdoQuery(), / read-only SQL
new Tools\HttpGet(), // fetch URLs new Tools\HttpGet(), / fetch URLs
new Tools\FileRead(), // read project files new Tools\FileRead(), / read project files
]); ]);
echo $agent->run('How many active users registered last week?'); echo $agent->run('How many active users registered last week?');
@@ -154,7 +154,7 @@ La philosophie de conception :
## Suivant ## Suivant
- [Référence du module de chat](/fr/ia/module/chat/) - [Référence du module de chat](/fr/ai/module/chat/)
- [Référence du module RAG](/fr/ia/module/rag/) - [Référence du module RAG](/fr/ai/module/rag/)
- [Référence du module agent](/fr/ia/module/agent/) - [Référence du module agent](/fr/ai/module/agent/)
- [Formation nibiru-coder](/fr/ia/module/training/) - [Formation nibiru-coder](/fr/ai/module/training/)

View File

@@ -10,9 +10,9 @@ Le plugin RAG est la fonctionnalité clé du module IA pour les constructeurs de
use Nibiru\Module\Ai\Ai; use Nibiru\Module\Ai\Ai;
$ai = new Ai(); $ai = new Ai();
$rag = $ai->rag('product-help'); // a named collection $rag = $ai->rag('product-help'); / a named collection
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/ $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
echo $rag->ask('How do I cancel my subscription?'); echo $rag->ask('How do I cancel my subscription?');
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
``` ```
## Référence de l'API ## Référence de l'API
```php ```php
$rag = $ai->rag('name'); // get/create a named collection $rag = $ai->rag('name'); / get/create a named collection
// --- Ingestion --- // --- Ingestion ---
$rag->ingestText($text, $metadata = []); // single chunk $rag->ingestText($text, $metadata = []); / single chunk
$count = $rag->ingestFile('path'); // returns chunks added $count = $rag->ingestFile('path'); / returns chunks added
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive $count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
// --- Querying --- // --- Querying ---
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …] $hits = $rag->search('query', $k = null); / [{score, text, metadata}, ]
$answer = $rag->ask('question', $k = null); // top-K → chat call $answer = $rag->ask('question', $k = null); / top-K chat call
// --- Maintenance --- // --- Maintenance ---
$rag->reset(); // forget everything (deletes file) $rag->reset(); / forget everything (deletes file)
$n = $rag->size(); // number of chunks $n = $rag->size(); / number of chunks
``` ```
## Réglages ## Réglages
@@ -95,5 +95,5 @@ rag.storage_path = "/../../application/module/ai/cache/rag/"
## ¿Qué sigue? ## ¿Qué sigue?
- [Plugin agent →](/fr/ia/module/agent/) pour les outils, pas la récupération. - [Plugin agent →](/fr/ai/module/agent/) pour les outils, pas la récupération.
- [Formation nibiru-coder →](/fr/ia/module/formation/) pour faire répondre le chat à moitié dans la voix du framework. - [Formation nibiru-coder →](/fr/ai/module/training/) pour faire répondre le chat à moitié dans la voix du framework.

View File

@@ -114,7 +114,7 @@ public function pageAction() {
View::forwardTo('/login'); View::forwardTo('/login');
return; return;
} }
// ... / ...
} }
``` ```
Pour les vérifications basées sur le rôle, les applications de démonstration utilisent le plugin `Acl` du même module : Pour les vérifications basées sur le rôle, les applications de démonstration utilisent le plugin `Acl` du même module :
@@ -178,7 +178,7 @@ public function submitAction() {
http_response_code(419); http_response_code(419);
return; return;
} }
// ...handle submission... / ...handle submission...
} }
``` ```
Intégrez `<input type="hidden" name="csrf" value="{$csrf}">` dans votre formulaire. Intégrez `<input type="hidden" name="csrf" value="{$csrf}">` dans votre formulaire.

View File

@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
## Lecture des configurations ## Lecture des configurations
```php ```php
$cfg = \Nibiru\Config::getInstance()->getConfig(); $cfg = \Nibiru\Config::getInstance()->getConfig();
$cfg['DATABASE']['driver']; // 'pdo' $cfg['DATABASE']['driver']; / 'pdo'
$cfg['SETTINGS']['page.url']; // 'https://my-app.local' $cfg['SETTINGS']['page.url']; / 'https://my-app.local'
``` ```
Pour les configurations profondément imbriquées, utilisez les constantes typées de l'interface View : Pour les configurations profondément imbriquées, utilisez les constantes typées de l'interface View :
```php ```php
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css'] $cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
``` ```
## Configurations des modules ## Configurations des modules
Chaque module situé sous `application/module/<nom>/settings/` peut posséder ses propres fichiers INI. Le Registre les détecte automatiquement et les expose via : Chaque module situé sous `application/module/<nom>/settings/` peut posséder ses propres fichiers INI. Le Registre les détecte automatiquement et les expose via :
```php ```php
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; // from [USERS] section in users.ini $users->session_lifetime; / from [USERS] section in users.ini
``` ```
Le Registre préfère `<module>.<env>.ini` par rapport à `<module>.ini`, donc vous obtenez des remplacements par environnement gratuitement. Le Registre préfère `<module>.<env>.ini` par rapport à `<module>.ini`, donc vous obtenez des remplacements par environnement gratuitement.

View File

@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
``` ```
Aides pratiques du contrôleur de base : Aides pratiques du contrôleur de base :
```php ```php
$this->getRequest('id', false); // $_REQUEST['id'] ?? false $this->getRequest('id', false); / $_REQUEST['id'] ?? false
$this->getPost('email', ''); // $_POST['email'] ?? '' $this->getPost('email', ''); / $_POST['email'] ?? ''
$this->getGet('page', 1); // $_GET['page'] ?? 1 $this->getGet('page', 1); / $_GET['page'] ?? 1
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI'] $this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
$this->getFiles('upload'); // $_FILES['upload'] $this->getFiles('upload'); / $_FILES['upload']
$this->getSession('auth'); // $_SESSION['auth'] $this->getSession('auth'); / $_SESSION['auth']
``` ```
Ces éléments existent en raison du fait que `Controller` est compatible avec `final` : vous pouvez les simuler dans les tests en remplaçant par une classe enfant. Ces éléments existent en raison du fait que `Controller` est compatible avec `final` : vous pouvez les simuler dans les tests en remplaçant par une classe enfant.
@@ -69,8 +69,8 @@ Ces éléments existent en raison du fait que `Controller` est compatible avec `
Pour rediriger à l'intérieur d'une action : Pour rediriger à l'intérieur d'une action :
```php ```php
View::forwardTo('/login'); // 302 to the URL, exits View::forwardTo('/login'); / 302 to the URL, exits
View::forwardToJsonHeader(); // sets Content-Type: application/json View::forwardToJsonHeader(); / sets Content-Type: application/json
``` ```
`forwardToJsonHeader()` est le modèle canonique pour les points de terminaison JSON — définissez l'en-tête, attribuez `data`, puis retournez. La couche d'affichage s'occupe du reste. `forwardToJsonHeader()` est le modèle canonique pour les points de terminaison JSON — définissez l'en-tête, attribuez `data`, puis retournez. La couche d'affichage s'occupe du reste.

View File

@@ -30,17 +30,17 @@ public function run() {
if (Config::getInstance()->getConfig() if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) { [self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); // 1. (re)generate models from schema new Model(false); / 1. (re)generate models from schema
} }
Router::getInstance()->route(); // 2. parse the URL Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); // 3. load model files Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); // 4. load module classes Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName(); $tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php"; $controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { // 5. controller file exists if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile; require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller"; $class = "Nibiru\\{$tpl}Controller";
$controller = new $class(); $controller = new $class();
@@ -49,7 +49,7 @@ public function run() {
$action = $_REQUEST['_action'] . 'Action'; $action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction(); $controller->navigationAction();
if (method_exists($controller, $action)) { if (method_exists($controller, $action)) {
$controller->$action(); // 6. optional named action $controller->$action(); / 6. optional named action
} }
$controller->pageAction(); $controller->pageAction();
} else { } else {
@@ -57,9 +57,9 @@ public function run() {
$controller->pageAction(); $controller->pageAction();
} }
Display::getInstance()->display(); // 7. render Smarty Display::getInstance()->display(); / 7. render Smarty
} else { } else {
// 8. soft 404 — render the configured error controller / 8. soft 404 render the configured error controller
} }
} }
``` ```

View File

@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
``` ```
### Cycle de vie ### Cycle de vie
``` ```
create // reset the static buffer; call before building a new form create / reset the static buffer; call before building a new form
addForm // wrap the buffer in <form>...</form> and return as a string addForm / wrap the buffer in <form>...</form> and return as a string
``` ```
:::caution[L'incréance de nommage est intentionnelle, mais pas complètement] :::caution[L'incréance de nommage est intentionnelle, mais pas complètement]
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — le préfixe correspond à ce que l'élément soit un `<input type="…">` (préfixe Input) ou une autre balise (préfixe Type). C'est un peu inconfortable, mais stable ; les modèles de conception utilisés par la CLI `./nibiru -c` suivent le même modèle, donc l'habitude s'impose. `addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — le préfixe correspond à ce que l'élément soit un `<input type="…">` (préfixe Input) ou une autre balise (préfixe Type). C'est un peu inconfortable, mais stable ; les modèles de conception utilisés par la CLI `./nibiru -c` suivent le même modèle, donc l'habitude s'impose.
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
```php ```php
use Nibiru\Factory\Form; use Nibiru\Factory\Form;
Form::create(); // reset the static buffer Form::create(); / reset the static buffer
Form::addOpenDiv(['class' => 'form-group']); Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']); Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
@@ -187,7 +187,7 @@ class formsController extends Controller {
'required' => 'required', 'required' => 'required',
'class' => 'contacts-input form-control', 'class' => 'contacts-input form-control',
]); ]);
// ...more fields... / ...more fields...
$this->form = Form::addForm([ $this->form = Form::addForm([
'name' => 'newregister', 'name' => 'newregister',
'method' => 'post', 'method' => 'post',

View File

@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
Lisez-le à partir d'ailleurs : Lisez-le à partir d'ailleurs :
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; // 7200 $cfg->session_lifetime; / 7200
$cfg->password_min_length; // 12 $cfg->password_min_length; / 12
$cfg->allowed_roles; // [admin, editor, standard] $cfg->allowed_roles; / [admin, editor, standard]
``` ```
Superpositions d'environnement : un fichier nommé `users.production.ini` est préféré par rapport à `users.ini` lorsque `APPLICATION_ENV=production`. Superpositions d'environnement : un fichier nommé `users.production.ini` est préféré par rapport à `users.ini` lorsque `APPLICATION_ENV=production`.
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); // internally calls notify() $analytics->trackPageView(); / internally calls notify()
``` ```
Chaque observateur reçoit l'instance d'analytics via sa méthode `update($subject)` et extrait les données d'événement dont il a besoin. Chaque observateur reçoit l'instance d'analytics via sa méthode `update($subject)` et extrait les données d'événement dont il a besoin.
@@ -200,7 +200,7 @@ Les noms sont des **noms de dossiers en minuscules**, exactement tels qu'ils app
Les classes de plugin se trouvent dans l'espace de noms **pluriel** `Plugins`: Les classes de plugin se trouvent dans l'espace de noms **pluriel** `Plugins`:
```php ```php
// application/module/billing/plugins/invoice.php // application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; // ← plural namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ } class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
``` ```
Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'autoload. Le squelette CLI (`./nibiru -m billing`) génère l'espace de noms correct pour vous. Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'autoload. Le squelette CLI (`./nibiru -m billing`) génère l'espace de noms correct pour vous.
@@ -210,7 +210,7 @@ Ne correspondez pas à l'espace de noms et vous obtiendrez des manquements d'aut
Vous **ne** vous inscrivez pas les fichiers INI de votre module — le [Registry](/en/core/registry/) les découvre automatiquement en parcourant `application/module/<name>/settings/*.ini` après que `[AUTOLOADER]` a chargé la classe du module. Chaque section `[<MODULE>]` (en majuscules) d'un INI devient disponible comme : Vous **ne** vous inscrivez pas les fichiers INI de votre module — le [Registry](/en/core/registry/) les découvre automatiquement en parcourant `application/module/<name>/settings/*.ini` après que `[AUTOLOADER]` a chargé la classe du module. Chaque section `[<MODULE>]` (en majuscules) d'un INI devient disponible comme :
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; // [BILLING] invoice.prefixproperty $cfg->invoice_prefix; / [BILLING] invoice.prefix property
``` ```
Le Registre préfère `<module>.<env>.ini` (par exemple `billing.production.ini`) lorsque `APPLICATION_ENV` correspond. Le Registre préfère `<module>.<env>.ini` (par exemple `billing.production.ini`) lorsque `APPLICATION_ENV` correspond.

View File

@@ -14,7 +14,7 @@ class productsController extends Controller
{ {
public function pageAction() { public function pageAction() {
$products = new products(); $products = new products();
Pageination::setEntriesPerPage(25); // optional; default from INI Pageination::setEntriesPerPage(25); / optional; default from INI
Pageination::setTable($products); Pageination::setTable($products);
$rows = Pageination::loadTableAsArray(); $rows = Pageination::loadTableAsArray();

View File

@@ -20,7 +20,7 @@ Pour chaque module sous `application/module/<nom>/settings/` :
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; $users->session_lifetime;
$users->password_min_length; $users->password_min_length;
$users->allowed_roles; // array $users->allowed_roles; / array
``` ```
À l'intérieur du module lui-même, la convention est d'envelopper cela dans un mutateur : À l'intérieur du module lui-même, la convention est d'envelopper cela dans un mutateur :
```php ```php

View File

@@ -40,7 +40,7 @@ Tout ce qui se trouve après le segment d'action devient une clé `$_REQUEST` pa
// /users/edit/42 → $_REQUEST['id'] = '42' // /users/edit/42 → $_REQUEST['id'] = '42'
public function editAction() { public function editAction() {
$id = (int) ($_REQUEST['id'] ?? 0); $id = (int) ($_REQUEST['id'] ?? 0);
// ... / ...
} }
``` ```
Pour les paramètres non numériques ou nommés, privilégiez les chaînes de requête : Pour les paramètres non numériques ou nommés, privilégiez les chaînes de requête :
@@ -62,9 +62,9 @@ Lorsqu'une URL correspond au modèle, les groupes capturés sont attribués aux
## Aide aux routages ## Aide aux routages
```php ```php
Router::getInstance()->currentPage(); // 'products' Router::getInstance()->currentPage(); / 'products'
Router::getInstance()->tplName(); // 'products' (controller stem for templates) Router::getInstance()->tplName(); / 'products' (controller stem for templates)
Router::getInstance()->getController(); // alias for currentPage() Router::getInstance()->getController(); / alias for currentPage()
``` ```
Ces éléments sont utiles dans les contrôleurs et les modèles : Ces éléments sont utiles dans les contrôleurs et les modèles :
```smarty ```smarty

View File

@@ -7,7 +7,7 @@ description: "Boutons, cartes, appels à laction, héros — prêts à être
Un rectangle noir sur crème. Éditorial. Pas de dégradé, pas d'éclairage. Un rectangle noir sur crème. Éditorial. Pas de dégradé, pas d'éclairage.
```html ```html
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/fr/start/installation/">
<span>Read the docs</span> <span>Read the docs</span>
<span class="atelier-button__arrow" aria-hidden="true"></span> <span class="atelier-button__arrow" aria-hidden="true"></span>
</a> </a>
@@ -162,7 +162,7 @@ Grille asymétrique à deux colonnes. Un grand numéro éditorial derrière le t
Nibiru is a modular PHP framework for builders who ship. Nibiru is a modular PHP framework for builders who ship.
</p> </p>
<div class="atelier-hero__cta"> <div class="atelier-hero__cta">
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/fr/start/installation/">
Read the docs <span aria-hidden="true"></span> Read the docs <span aria-hidden="true"></span>
</a> </a>
</div> </div>

View File

@@ -19,73 +19,25 @@ hero:
variant: minimal variant: minimal
--- ---
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; import CometTrail from '../../../components/CometTrail.astro';
import MmvcStage from '../../../components/MmvcStage.astro';
import MissionControl from '../../../components/MissionControl.astro';
import LaunchSequence from '../../../components/LaunchSequence.astro';
import SpacecraftGrid from '../../../components/SpacecraftGrid.astro';
import EditorialContent from '../../../components/EditorialContent.astro';
import LandingFooter from '../../../components/LandingFooter.astro';
import ToTop from '../../../components/ToTop.astro';
import LandingScripts from '../../../components/LandingScripts.astro';
## Une constellation de capacités <CometTrail />
<MmvcStage />
<MissionControl />
<LaunchSequence />
<SpacecraftGrid />
<EditorialContent />
<LandingFooter />
<ToTop />
<CardGrid stagger> {/* Loads three.js + the original mockup scene code. MUST be the last node so
<Card title="Architecture MMVC" icon="puzzle"> every #id the scene targets exists in the DOM when the script runs. */}
Les modules enveloppent le MVC avec traits, plugins, interfaces et paramètres. Le second **M** est faiblement couplé par conception, avec le pattern observer `SplSubject` intégré. <LandingScripts />
</Card>
<Card title="Cœur multi-bases" icon="seti:db">
Cinq pilotes en orbite : `mysql`, `pdo`, `postgres` (ODBC), `psql` (libpq) et `postgresql`. Basculez en modifiant une seule clé INI.
</Card>
<Card title="Vues Smarty" icon="document">
Vues pilotées par templates, un assistant global `View::assign()`, et un cache chaud `templates_c`. Plus partiels partagés, includes de navigation et templates de pagination.
</Card>
<Card title="Constructeur de formulaires fluide" icon="pencil">
Plus de 28 types de champ — text, password, switch, color, range, file upload — composés via des appels statiques `Form::add…` et rendus en une seule chaîne HTML.
</Card>
<Card title="Véritable outil CLI" icon="seti:powershell">
`./nibiru` génère modules, contrôleurs, plugins, migrations et pages CMS. Il exécute les migrations contre `local`, `staging` ou `production`.
</Card>
<Card title="IA-natif, bientôt" icon="rocket">
Nibiru sera le premier framework PHP avec un <a href="/fr/ai/oracle/">Oracle basé sur RAG</a> entraîné sur sa propre connaissance — et un corpus publié pour de futurs fine-tunes.
</Card>
</CardGrid>
## Démarrage rapide
```bash
# Cloner
git clone https://github.com/alllinux/Nibiru mon-app && cd mon-app
# Installer les dépendances PHP (Smarty, PHPMailer, Guzzle, …)
composer install
# Permissions + amorçage des dossiers
./nibiru -s
# Première migration
./nibiru -mi local
# Générer un contrôleur
./nibiru -c products
```
```php
// application/controller/productsController.php
namespace Nibiru;
use Nibiru\Adapter\Controller;
class productsController extends Controller {
public function pageAction() {
View::assign([
'title' => 'Produits — Nibiru',
'products' => [['id' => 1, 'name' => 'Plaquage d\'or Marduk']],
]);
}
public function navigationAction() {
JsonNavigation::getInstance()->loadJsonNavigationArray();
}
}
```
## Où aller ensuite
<CardGrid>
<LinkCard title="Qu'est-ce que Nibiru ?" href="/fr/start/what-is-nibiru/" description="La visite de 90 secondes : MMVC, le dispatcher, le cycle de vie des requêtes." />
<LinkCard title="Architecture" href="/core/architecture/" description="Comment modules, contrôleurs, vues et registre orbitent les uns autour des autres." />
<LinkCard title="Vitrine" href="/showcase/projects/" description="De vraies apps en production sur Nibiru — facturation, e-commerce, PIM industriel." />
<LinkCard title="Interroger l'Oracle" href="/fr/ai/oracle/" description="Ouvrez le chat flottant. L'Oracle est ancré dans cette documentation même." />
</CardGrid>

View File

@@ -61,7 +61,7 @@ Plusieurs suivi sans couplage avec le contrôleur.
$analytics = new Analytics(); $analytics = new Analytics();
$analytics->attach(new Plugin\Matomo()); $analytics->attach(new Plugin\Matomo());
$analytics->attach(new Plugin\Plausible()); $analytics->attach(new Plugin\Plausible());
$analytics->trackPageView(); // calls notify() internally $analytics->trackPageView(); / calls notify() internally
``` ```
Chaque observateur appelle uniquement les champs dont il s'occupe avec sa méthode `update($subject)`. Ajouter un suivi est une modification d'une seule ligne. Chaque observateur appelle uniquement les champs dont il s'occupe avec sa méthode `update($subject)`. Ajouter un suivi est une modification d'une seule ligne.

View File

@@ -55,7 +55,7 @@ public function detailAction()
try { try {
$machine = Machine::init()->getMachine((int) $machineId); $machine = Machine::init()->getMachine((int) $machineId);
} catch (\Throwable $e) { } catch (\Throwable $e) {
$machine = null; // DB blip → page still renders with fallback. $machine = null; / DB blip page still renders with fallback.
} }
$machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId"; $machineName = $machine['ms_machines_name'] ?? "Maschine #$machineId";
@@ -120,7 +120,7 @@ Les cinq différences ci-dessous sont tirées des bases de code ci-dessus. Chacu
| Authentification | 3 lignes dans le constructeur du contrôleur. | Pile de middlewares + classes de politique + portails. | | Authentification | 3 lignes dans le constructeur du contrôleur. | Pile de middlewares + classes de politique + portails. |
| Événements | `SplSubject` + `SplObserver` de la bibliothèque standard PHP. | Dispatcheur d'événements personnalisé + registre d'écouteurs + file d'attente. | | Événements | `SplSubject` + `SplObserver` de la bibliothèque standard PHP. | Dispatcheur d'événements personnalisé + registre d'écouteurs + file d'attente. |
Lisez le [détail complet avec des références de code →](/fr/pourquoi-nibiru/) Lisez le [détail complet avec des références de code →](/fr/why-nibiru/)
--- ---

View File

@@ -50,7 +50,7 @@ class Cms implements Interfaces\Cms, SplSubject
use Traits\PageBuilderForm; use Traits\PageBuilderForm;
use Traits\CmsPageStructureModifier; use Traits\CmsPageStructureModifier;
use Traits\FormElements; use Traits\FormElements;
// …8 more traits / …8 more traits
} }
``` ```
**Effet net**: aucune injection de constructeur, aucune inscription auprès du fournisseur de services, aucun appel à `bind()` / `singleton()` dans un fichier de configuration. Un nouveau développeur peut rechercher le nom du trait et voir tous les appels. **Effet net**: aucune injection de constructeur, aucune inscription auprès du fournisseur de services, aucun appel à `bind()` / `singleton()` dans un fichier de configuration. Un nouveau développeur peut rechercher le nom du trait et voir tous les appels.
@@ -108,7 +108,7 @@ Le contrôleur API adopte une approche inverse : une liste blanche des points de
```php ```php
// public endpoints can be listed up-front, auth wraps the rest. // public endpoints can be listed up-front, auth wraps the rest.
if (in_array($action, ['category', 'machines', 'ollama', 'team'])) { if (in_array($action, ['category', 'machines', 'ollama', 'team'])) {
// public, skip auth / public, skip auth
} else { } else {
$this->user = new User(); $this->user = new User();
$this->acl = new Acl(); $this->acl = new Acl();
@@ -137,8 +137,8 @@ class Machineryscout implements IModule, \SplSubject
} }
public function indexMachines(): void { public function indexMachines(): void {
// …do the work… / …do the work…
$this->notify(); // analytics, cache invalidator, audit log all see it. $this->notify(); / analytics, cache invalidator, audit log all see it.
} }
} }
``` ```
@@ -154,4 +154,4 @@ Un développeur solo ou une petite équipe peut construire et *exploiter* une v
Si vous préférez voir votre code, prenez-le. Si vous préférez voir votre code, prenez-le.
→ [Lisez la présentation →](/fr/presentation/projets/) → [Lisez la présentation →](/fr/showcase/projects/)

View File

@@ -60,7 +60,7 @@ OpenAIのfine-tune、AnthropicのAPI評価、ほとんどのLoRAツールAxol
"content": "Modules implementing `SplSubject` can broadcast events…" "content": "Modules implementing `SplSubject` can broadcast events…"
} }
``` ```
これは[Oracle](/ai/oracle/)が内部で使用している正確なファイルです。 これは[Oracle](/ja/ai/oracle/)が内部で使用している正確なファイルです。
## チャンクの導出方法 ## チャンクの導出方法

View File

@@ -10,19 +10,19 @@ description: "単一または複数のターンで行われる、任意のOllama
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$chat = $ai->chat(); $chat = $ai->chat();
$chat->system('Be terse.'); // optional system prompt $chat->system('Be terse.'); / optional system prompt
$chat->model('qwen2.5-coder:14b'); // override the configured model $chat->model('qwen2.5-coder:14b'); / override the configured model
$chat->temperature(0.2); // override config $chat->temperature(0.2); / override config
$chat->maxTokens(512); // override config $chat->maxTokens(512); / override config
$chat->user('Hello'); // append a user message $chat->user('Hello'); / append a user message
$chat->assistant('Hi.'); // append an assistant message (rare) $chat->assistant('Hi.'); / append an assistant message (rare)
$reply = $chat->complete(); // run the call, return text $reply = $chat->complete(); / run the call, return text
$reply = $chat->ask('How are you?'); // = ->user(...)->complete() $reply = $chat->ask('How are you?'); / = ->user(...)->complete()
$chat->reset(); // clear messages, keep model + system $chat->reset(); / clear messages, keep model + system
$chat->history(); // [{role, content}, …] $chat->history(); / [{role, content}, ]
``` ```
## 一発 ## 一発
```php ```php
@@ -35,10 +35,10 @@ echo (new \Nibiru\Module\Ai\Ai())
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('Name three Nibiru singletons.'); $chat->user('Name three Nibiru singletons.');
$singletons = $chat->complete(); // appended to history $singletons = $chat->complete(); / appended to history
$chat->user('What does the second one do?'); $chat->user('What does the second one do?');
$detail = $chat->complete(); // model has full context $detail = $chat->complete(); / model has full context
``` ```
## モデルとスタイルを個別呼び出しごとに上書きする ## モデルとスタイルを個別呼び出しごとに上書きする
```php ```php
@@ -65,7 +65,7 @@ chat.provider = "anthropic"
anthropic.api_key = "sk-ant-..." anthropic.api_key = "sk-ant-..."
anthropic.model = "claude-haiku-4-5-20251001" anthropic.model = "claude-haiku-4-5-20251001"
``` ```
チャットプラグインはまだフレームワークモジュールにAnthropicトランスポートを含んでいません — 現在、ドキュメントサイトの`scripts/lib/providers.mjs`パターンが参考となっています。([マイルストーン](/ja/ai/milestones/)をご覧ください。) チャットプラグインはまだフレームワークモジュールにAnthropicトランスポートを含んでいません — 現在、ドキュメントサイトの`scripts/lib/providers.mjs`パターンが参考となっています。([マイルストーン](/ja/ai/roadmap/)をご覧ください。)
## 実践的なパターン: チャットをアクションとして ## 実践的なパターン: チャットをアクションとして
```php ```php

View File

@@ -9,12 +9,12 @@ Embedプラグインは、Ollamaの`/api/embeddings`に薄いラッパーであ
```php ```php
$embed = (new \Nibiru\Module\Ai\Ai())->embed(); $embed = (new \Nibiru\Module\Ai\Ai())->embed();
$vec = $embed->one('controller'); // float[] $vec = $embed->one('controller'); / float[]
$vectors = $embed->batch(['a', 'b', 'c']); // float[][] $vectors = $embed->batch(['a', 'b', 'c']); / float[][]
$score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); // 0..1 $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($a, $b); / 0..1
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); // back to float[] $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); / back to float[]
``` ```
## パターン: 遠近似の文字列を重複除去する ## パターン: 遠近似の文字列を重複除去する
```php ```php
@@ -49,8 +49,8 @@ function bestTag(string $text, array $tagVecs, $embed): string {
return $best[0]; return $best[0];
} }
echo bestTag('User::isAuthorized', $tagVecs, $embed); // → 'authentication' echo bestTag('User::isAuthorized', $tagVecs, $embed); / 'authentication'
echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (probably) echo bestTag('Pageination::setTable', $tagVecs, $embed); / 'database' (probably)
``` ```
## ストレージ ## ストレージ
@@ -58,7 +58,7 @@ echo bestTag('Pageination::setTable', $tagVecs, $embed); // → 'database' (prob
`Embed::pack()` を使用して、それらを 4 バイトの浮動小数点数として base64 エンコードします。 `Embed::pack()` を使用して、それらを 4 バイトの浮動小数点数として base64 エンコードします。
```php ```php
$compact = Embed::pack($vec); // ~4 KB → ~5.3 KB base64 string $compact = Embed::pack($vec); / ~4 KB ~5.3 KB base64 string
$vec = Embed::unpack($compact); $vec = Embed::unpack($compact);
``` ```
RAG プラグインは、この形式を使用して内部の JSON ファイルを管理しています。 RAG プラグインは、この形式を使用して内部の JSON ファイルを管理しています。

View File

@@ -57,7 +57,7 @@ echo $ai->chat()->ask('Explain MMVC in two sentences.');
// Multi-turn // Multi-turn
$chat = $ai->chat(); $chat = $ai->chat();
$chat->user('How do I scaffold a module?'); $chat->user('How do I scaffold a module?');
$chat->user('And add Graylog hooks?'); // referrs to previous turn $chat->user('And add Graylog hooks?'); / referrs to previous turn
echo $chat->complete(); echo $chat->complete();
// Override per call // Override per call
@@ -80,7 +80,7 @@ $score = \Nibiru\Module\Ai\Plugin\Embed::cosine($va, $vb);
``` ```
コンパクトなストレージ: コンパクトなストレージ:
```php ```php
$packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); // base64 string, 4 bytes/dim $packed = \Nibiru\Module\Ai\Plugin\Embed::pack($vec); / base64 string, 4 bytes/dim
$vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed); $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
``` ```
### 3. RAG — 収集、検索、基準化 ### 3. RAG — 収集、検索、基準化
@@ -88,7 +88,7 @@ $vec = \Nibiru\Module\Ai\Plugin\Embed::unpack($packed);
$rag = $ai->rag('product-help'); $rag = $ai->rag('product-help');
// One-time ingestion // One-time ingestion
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
$rag->ingestFile('/var/data/manual.pdf.txt'); $rag->ingestFile('/var/data/manual.pdf.txt');
@@ -105,9 +105,9 @@ use Nibiru\Module\Ai\Plugin\Tools;
$ai = new \Nibiru\Module\Ai\Ai(); $ai = new \Nibiru\Module\Ai\Ai();
$agent = $ai->agent()->withTools([ $agent = $ai->agent()->withTools([
new Tools\PdoQuery(), // read-only SQL new Tools\PdoQuery(), / read-only SQL
new Tools\HttpGet(), // fetch URLs new Tools\HttpGet(), / fetch URLs
new Tools\FileRead(), // read project files new Tools\FileRead(), / read project files
]); ]);
echo $agent->run('How many active users registered last week?'); echo $agent->run('How many active users registered last week?');

View File

@@ -10,9 +10,9 @@ RAG プラグインは、製品ビルダーにとって AI モジュールのキ
use Nibiru\Module\Ai\Ai; use Nibiru\Module\Ai\Ai;
$ai = new Ai(); $ai = new Ai();
$rag = $ai->rag('product-help'); // a named collection $rag = $ai->rag('product-help'); / a named collection
$rag->ingestDir(__DIR__ . '/help/'); // walks .md/.txt/.php under help/ $rag->ingestDir(__DIR__ . '/help/'); / walks .md/.txt/.php under help/
$rag->ingestText('FAQ entry…', ['source' => 'faq-12']); $rag->ingestText('FAQ entry…', ['source' => 'faq-12']);
echo $rag->ask('How do I cancel my subscription?'); echo $rag->ask('How do I cancel my subscription?');
@@ -46,20 +46,20 @@ $logs->ingestText($exception->__toString(), ['ts' => time()]);
``` ```
## API リファレンス ## API リファレンス
```php ```php
$rag = $ai->rag('name'); // get/create a named collection $rag = $ai->rag('name'); / get/create a named collection
// --- Ingestion --- // --- Ingestion ---
$rag->ingestText($text, $metadata = []); // single chunk $rag->ingestText($text, $metadata = []); / single chunk
$count = $rag->ingestFile('path'); // returns chunks added $count = $rag->ingestFile('path'); / returns chunks added
$count = $rag->ingestDir('dir', ['md','txt','php']); // recursive $count = $rag->ingestDir('dir', ['md','txt','php']); / recursive
// --- Querying --- // --- Querying ---
$hits = $rag->search('query', $k = null); // [{score, text, metadata}, …] $hits = $rag->search('query', $k = null); / [{score, text, metadata}, ]
$answer = $rag->ask('question', $k = null); // top-K → chat call $answer = $rag->ask('question', $k = null); / top-K chat call
// --- Maintenance --- // --- Maintenance ---
$rag->reset(); // forget everything (deletes file) $rag->reset(); / forget everything (deletes file)
$n = $rag->size(); // number of chunks $n = $rag->size(); / number of chunks
``` ```
## チューニングの調整項目 ## チューニングの調整項目

View File

@@ -114,7 +114,7 @@ public function pageAction() {
View::forwardTo('/login'); View::forwardTo('/login');
return; return;
} }
// ... / ...
} }
``` ```
ロール認識のチェックのために、デモンストレーションアプリケーションは同じモジュールから `Acl` プラグインを使用しています。 ロール認識のチェックのために、デモンストレーションアプリケーションは同じモジュールから `Acl` プラグインを使用しています。
@@ -178,7 +178,7 @@ public function submitAction() {
http_response_code(419); http_response_code(419);
return; return;
} }
// ...handle submission... / ...handle submission...
} }
``` ```
フォームに `<input type="hidden" name="csrf" value="{$csrf}">` を埋め込んでください。 フォームに `<input type="hidden" name="csrf" value="{$csrf}">` を埋め込んでください。

View File

@@ -82,19 +82,19 @@ password_hash = "another-salt-for-AES_DECRYPT"
## 設定の読み込み ## 設定の読み込み
```php ```php
$cfg = \Nibiru\Config::getInstance()->getConfig(); $cfg = \Nibiru\Config::getInstance()->getConfig();
$cfg['DATABASE']['driver']; // 'pdo' $cfg['DATABASE']['driver']; / 'pdo'
$cfg['SETTINGS']['page.url']; // 'https://my-app.local' $cfg['SETTINGS']['page.url']; / 'https://my-app.local'
``` ```
深くネストされた設定には、View インターフェースから型付けされた定数を使用してください。 深くネストされた設定には、View インターフェースから型付けされた定数を使用してください。
```php ```php
$cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; // ['/public/css/app.css'] $cfg[\Nibiru\View::NIBIRU_SETTINGS]['smarty.css']; / ['/public/css/app.css']
``` ```
## モジュールの設定 ## モジュールの設定
各モジュールの `application/module/<name>/settings/` 以下には独自の INI ファイルを配置できます。Registry はそれらを自動的に取り込み、以下の通りに公開します: 各モジュールの `application/module/<name>/settings/` 以下には独自の INI ファイルを配置できます。Registry はそれらを自動的に取り込み、以下の通りに公開します:
```php ```php
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; // from [USERS] section in users.ini $users->session_lifetime; / from [USERS] section in users.ini
``` ```
レジストリは `<module>.<env>.ini``<module>.ini` よりも優先します。これにより、環境ごとのオーバーライドが無料で利用できます。 レジストリは `<module>.<env>.ini``<module>.ini` よりも優先します。これにより、環境ごとのオーバーライドが無料で利用できます。

View File

@@ -56,12 +56,12 @@ View::assign(['products' => $list]);
``` ```
ベースコントローラーからの便利ヘルパー: ベースコントローラーからの便利ヘルパー:
```php ```php
$this->getRequest('id', false); // $_REQUEST['id'] ?? false $this->getRequest('id', false); / $_REQUEST['id'] ?? false
$this->getPost('email', ''); // $_POST['email'] ?? '' $this->getPost('email', ''); / $_POST['email'] ?? ''
$this->getGet('page', 1); // $_GET['page'] ?? 1 $this->getGet('page', 1); / $_GET['page'] ?? 1
$this->getServer('REQUEST_URI'); // $_SERVER['REQUEST_URI'] $this->getServer('REQUEST_URI'); / $_SERVER['REQUEST_URI']
$this->getFiles('upload'); // $_FILES['upload'] $this->getFiles('upload'); / $_FILES['upload']
$this->getSession('auth'); // $_SESSION['auth'] $this->getSession('auth'); / $_SESSION['auth']
``` ```
これらは存在する理由は、`Controller``final`に耐えられるように設計されていることです:テストで子クラスを置き換えてモックすることができます。 これらは存在する理由は、`Controller``final`に耐えられるように設計されていることです:テストで子クラスを置き換えてモックすることができます。
@@ -69,8 +69,8 @@ $this->getSession('auth'); // $_SESSION['auth']
アクション内でリダイレクトするには: アクション内でリダイレクトするには:
```php ```php
View::forwardTo('/login'); // 302 to the URL, exits View::forwardTo('/login'); / 302 to the URL, exits
View::forwardToJsonHeader(); // sets Content-Type: application/json View::forwardToJsonHeader(); / sets Content-Type: application/json
``` ```
`forwardToJsonHeader()` は、JSON エンドポイントの標準的なパターンです。ヘッダーを設定し、`data` を割り当ててから返す。その後、表示層が残りの処理を行います。 `forwardToJsonHeader()` は、JSON エンドポイントの標準的なパターンです。ヘッダーを設定し、`data` を割り当ててから返す。その後、表示層が残りの処理を行います。

View File

@@ -30,17 +30,17 @@ public function run() {
if (Config::getInstance()->getConfig() if (Config::getInstance()->getConfig()
[self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) { [self::CONFIG_GENERATOR_SECTION][self::GENERATOR_DATABASE]) {
new Model(false); // 1. (re)generate models from schema new Model(false); / 1. (re)generate models from schema
} }
Router::getInstance()->route(); // 2. parse the URL Router::getInstance()->route(); / 2. parse the URL
Auto::loader()->loadModelFiles(); // 3. load model files Auto::loader()->loadModelFiles(); / 3. load model files
Auto::loader()->loadModules(); // 4. load module classes Auto::loader()->loadModules(); / 4. load module classes
$tpl = Router::getInstance()->tplName(); $tpl = Router::getInstance()->tplName();
$controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php"; $controllerFile = __DIR__ . "/../../application/controller/{$tpl}Controller.php";
if (is_file($controllerFile)) { // 5. controller file exists if (is_file($controllerFile)) { / 5. controller file exists
require_once $controllerFile; require_once $controllerFile;
$class = "Nibiru\\{$tpl}Controller"; $class = "Nibiru\\{$tpl}Controller";
$controller = new $class(); $controller = new $class();
@@ -49,7 +49,7 @@ public function run() {
$action = $_REQUEST['_action'] . 'Action'; $action = $_REQUEST['_action'] . 'Action';
$controller->navigationAction(); $controller->navigationAction();
if (method_exists($controller, $action)) { if (method_exists($controller, $action)) {
$controller->$action(); // 6. optional named action $controller->$action(); / 6. optional named action
} }
$controller->pageAction(); $controller->pageAction();
} else { } else {
@@ -57,9 +57,9 @@ public function run() {
$controller->pageAction(); $controller->pageAction();
} }
Display::getInstance()->display(); // 7. render Smarty Display::getInstance()->display(); / 7. render Smarty
} else { } else {
// 8. soft 404 — render the configured error controller / 8. soft 404 render the configured error controller
} }
} }
``` ```

View File

@@ -40,8 +40,8 @@ addOpenSpan addCloseSpan
``` ```
### ライフサイクル ### ライフサイクル
``` ```
create // reset the static buffer; call before building a new form create / reset the static buffer; call before building a new form
addForm // wrap the buffer in <form>...</form> and return as a string addForm / wrap the buffer in <form>...</form> and return as a string
``` ```
:::caution[名前の不一致は意図的なもの、ある程度] :::caution[名前の不一致は意図的なもの、ある程度]
`addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — プレフィックスが要素が `<input type="…">` (Input プレフィックス) であるかどうかInput プレフィックスか、他のタグType プレフィックスであるかどうかに一致します。これは奇妙ですが安定していますCLI の `./nibiru -c` スキャフォールディングは同じパターンを使用しているため、筋肉記憶が働きます。 `addInputTypePassword` vs `addTypePassword`, `addInputTypeFileupload` vs `addTypeFileUpload` — プレフィックスが要素が `<input type="…">` (Input プレフィックス) であるかどうかInput プレフィックスか、他のタグType プレフィックスであるかどうかに一致します。これは奇妙ですが安定していますCLI の `./nibiru -c` スキャフォールディングは同じパターンを使用しているため、筋肉記憶が働きます。
@@ -51,7 +51,7 @@ addForm // wrap the buffer in <form>...</form> and return as a string
```php ```php
use Nibiru\Factory\Form; use Nibiru\Factory\Form;
Form::create(); // reset the static buffer Form::create(); / reset the static buffer
Form::addOpenDiv(['class' => 'form-group']); Form::addOpenDiv(['class' => 'form-group']);
Form::addTypeLabel(['for' => 'login', 'value' => 'Username']); Form::addTypeLabel(['for' => 'login', 'value' => 'Username']);
@@ -187,7 +187,7 @@ class formsController extends Controller {
'required' => 'required', 'required' => 'required',
'class' => 'contacts-input form-control', 'class' => 'contacts-input form-control',
]); ]);
// ...more fields... / ...more fields...
$this->form = Form::addForm([ $this->form = Form::addForm([
'name' => 'newregister', 'name' => 'newregister',
'method' => 'post', 'method' => 'post',

View File

@@ -147,9 +147,9 @@ allowed.roles[] = "standard"
どこからでも読み返す: どこからでも読み返す:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$cfg->session_lifetime; // 7200 $cfg->session_lifetime; / 7200
$cfg->password_min_length; // 12 $cfg->password_min_length; / 12
$cfg->allowed_roles; // [admin, editor, standard] $cfg->allowed_roles; / [admin, editor, standard]
``` ```
環境オーバーレイ:`APPLICATION_ENV=production` の場合、`users.production.ini``users.ini` よりも優先されます。 環境オーバーレイ:`APPLICATION_ENV=production` の場合、`users.production.ini``users.ini` よりも優先されます。
@@ -162,7 +162,7 @@ $analytics = new \Nibiru\Module\Analytics\Analytics();
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Matomo());
$analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible()); $analytics->attach(new \Nibiru\Module\Analytics\Plugin\Plausible());
$analytics->trackPageView(); // internally calls notify() $analytics->trackPageView(); / internally calls notify()
``` ```
各観察者の `update($subject)` は、分析インスタンスを受け取り、関心のあるイベントデータを取得します。 各観察者の `update($subject)` は、分析インスタンスを受け取り、関心のあるイベントデータを取得します。
@@ -200,7 +200,7 @@ class.plugin.pos[] = "" ; reserved
プラグインクラスは、**複数形の**名前空間 `Plugins` の下にあります: プラグインクラスは、**複数形の**名前空間 `Plugins` の下にあります:
```php ```php
// application/module/billing/plugins/invoice.php // application/module/billing/plugins/invoice.php
namespace Nibiru\Module\Billing\Plugins; // ← plural namespace Nibiru\Module\Billing\Plugins; / plural
class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ } class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
``` ```
名前空間が一致しないと、オートローダーのミスが発生します。CLI スキャフォールド (`./nibiru -m billing`) は正しい名前空間を生成してくれます。 名前空間が一致しないと、オートローダーのミスが発生します。CLI スキャフォールド (`./nibiru -m billing`) は正しい名前空間を生成してくれます。
@@ -210,7 +210,7 @@ class Invoice extends \Nibiru\Module\Billing\Billing { /* ... */ }
モジュールの INI ファイルを **登録しないでください** — [Registry](/en/core/registry/) は、`[AUTOLOADER]` がモジュールクラスを読み込んだ後、`application/module/<name>/settings/*.ini` を走査して自動的に発見します。各 INI の `[<MODULE>]`(大文字)セクションは次のようになります: モジュールの INI ファイルを **登録しないでください** — [Registry](/en/core/registry/) は、`[AUTOLOADER]` がモジュールクラスを読み込んだ後、`application/module/<name>/settings/*.ini` を走査して自動的に発見します。各 INI の `[<MODULE>]`(大文字)セクションは次のようになります:
```php ```php
$cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing'); $cfg = \Nibiru\Registry::getInstance()->loadModuleConfigByName('billing');
$cfg->invoice_prefix; // [BILLING] invoice.prefixproperty $cfg->invoice_prefix; / [BILLING] invoice.prefix property
``` ```
レジストリは、`APPLICATION_ENV` が一致する場合に `<module>.<env>.ini`(例:`billing.production.ini`)を優先します。 レジストリは、`APPLICATION_ENV` が一致する場合に `<module>.<env>.ini`(例:`billing.production.ini`)を優先します。

View File

@@ -14,7 +14,7 @@ class productsController extends Controller
{ {
public function pageAction() { public function pageAction() {
$products = new products(); $products = new products();
Pageination::setEntriesPerPage(25); // optional; default from INI Pageination::setEntriesPerPage(25); / optional; default from INI
Pageination::setTable($products); Pageination::setTable($products);
$rows = Pageination::loadTableAsArray(); $rows = Pageination::loadTableAsArray();

View File

@@ -20,7 +20,7 @@ description: "モジュールが自動的に検出され、その設定が実行
$users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users'); $users = \Nibiru\Registry::getInstance()->loadModuleConfigByName('users');
$users->session_lifetime; $users->session_lifetime;
$users->password_min_length; $users->password_min_length;
$users->allowed_roles; // array $users->allowed_roles; / array
``` ```
モジュール自体では、この内容をセッターでラップする慣習があります。 モジュール自体では、この内容をセッターでラップする慣習があります。
```php ```php

View File

@@ -40,7 +40,7 @@ Nibiru は、どの設定も不要で SEO フレンドリーな URL を自動検
// /users/edit/42 → $_REQUEST['id'] = '42' // /users/edit/42 → $_REQUEST['id'] = '42'
public function editAction() { public function editAction() {
$id = (int) ($_REQUEST['id'] ?? 0); $id = (int) ($_REQUEST['id'] ?? 0);
// ... / ...
} }
``` ```
数値的または名前付きパラメータ以外の場合、クエリ文字列を使用することをお勧めします。 数値的または名前付きパラメータ以外の場合、クエリ文字列を使用することをお勧めします。
@@ -62,9 +62,9 @@ api.v1.products.params[] = "id"
## ルーティングヘルパー ## ルーティングヘルパー
```php ```php
Router::getInstance()->currentPage(); // 'products' Router::getInstance()->currentPage(); / 'products'
Router::getInstance()->tplName(); // 'products' (controller stem for templates) Router::getInstance()->tplName(); / 'products' (controller stem for templates)
Router::getInstance()->getController(); // alias for currentPage() Router::getInstance()->getController(); / alias for currentPage()
``` ```
これらはコントローラーとテンプレート内で役立ちます: これらはコントローラーとテンプレート内で役立ちます:
```smarty ```smarty

View File

@@ -7,7 +7,7 @@ description: "ボタン、カード、コールアウト、ヒーロー — コ
黒い文字がクレーム色の背景にある長方形。エディタリー風。グラデーションや光沢はありません。 黒い文字がクレーム色の背景にある長方形。エディタリー風。グラデーションや光沢はありません。
```html ```html
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/ja/start/installation/">
<span>Read the docs</span> <span>Read the docs</span>
<span class="atelier-button__arrow" aria-hidden="true"></span> <span class="atelier-button__arrow" aria-hidden="true"></span>
</a> </a>
@@ -162,7 +162,7 @@ description: "ボタン、カード、コールアウト、ヒーロー — コ
Nibiru is a modular PHP framework for builders who ship. Nibiru is a modular PHP framework for builders who ship.
</p> </p>
<div class="atelier-hero__cta"> <div class="atelier-hero__cta">
<a class="atelier-button atelier-button--primary" href="/en/start/"> <a class="atelier-button atelier-button--primary" href="/ja/start/installation/">
Read the docs <span aria-hidden="true"></span> Read the docs <span aria-hidden="true"></span>
</a> </a>
</div> </div>

Some files were not shown because too many files have changed in this diff Show More