Der Editor soll sich per Plugin erweitern lassen. Ein Plugin ist eine einzelne .js-Datei; hochladen und aktivieren kommt später (eigene Issues, siehe unten). Dieses Issue legt die Architektur fest und liefert v1 nur mit dem, was das Demo-Plugin braucht.
Demo-Plugin (Ziel von v1):
ein eigener Tab
jedes Asset des Projekts als Node in einem Graph: leerer Node, Asset-Name als Titel. Kein Editing, keine Connections; Pan, Zoom und Fit gehen.
Asset anlegen, umbenennen oder löschen → der Tab zieht nach.
Entscheidungen (Planung 2026-09-17):
Aktivierung: Alles, was in data/plugins/ liegt, ist aktiv. Aktiv/inaktiv unter Settings → #114.
Serverweit, nicht pro Projekt.
Quelle im Repo:plugins/demo-plugin.js. npm run build und tools/seed-e2e.mjs kopieren sie nach data/plugins/ (damit hat auch CI das Plugin). data/plugins/ kommt in .gitignore.
Umfang: Architektur + API v1 nur für die Demo. Schreiben mit Undo, eigene Node-Typen im Behavior-Graph, eigene Asset-Typen, Inspector-Felder und Laufzeit-Hooks kommen als Folge-Issues, sobald es echte Nutzer dafür gibt – eine „vollständige“ API ohne Nutzer wird geraten.
Nur im Editor, nicht im laufenden Spiel und nicht im Export.
Sicherheit: Ein Plugin läuft mit allen Rechten des Editors. Für v1 hingenommen → #115.
Keine Typen (.d.ts) für Plugin-Autoren vorerst.
Zu bauen:
Plugin-Format: ES-Modul mit export const meta = { id, name, version, api: 1 }, export function activate(api), optional export function deactivate(). Das Plugin importiert nichts – insbesondere kein eigenes Pixi (ein WebGL-Canvas, eine Pixi-Instanz); alles kommt über api.
Route plugins in api/index.php: listet data/plugins/*.js und liefert eine Datei mit Content-Type: text/javascript aus. Nur Dateinamen aus dem Ordner, kein Pfad (kein ..).
Loader im Editor: lädt beim Start alle Plugins per import() und ruft activate. Fehler beim Laden oder in activate → Console + Hinweis, der Editor läuft weiter. Ein Plugin mit unbekannter api-Version wird nicht aktiviert.
api.tabs.add({ id, label, create }) – dynamische Tabs zusätzlich zur festen CentreTab-Liste in src/editor/Editor.ts; deren Typsicherheit für die eingebauten Tabs bleibt.
api.ui.NodeBoard – reine Anzeige, setNodes([{ id, title, x, y }]), Pan/Zoom/Fit über FreeViewport, Look von NodeView. GraphCanvas wird dafür nicht wiederverwendet (hängt an EditorState und am Behavior-Graph).
Demo-Pluginplugins/demo-plugin.js: Tab „Assets“, ein Node je Asset, im Raster angeordnet.
Tests: Unit-Tests für Loader (defektes Plugin, falsche api-Version, Fehler in activate) und API; ein e2e-Modul öffnet den Tab und zählt die Nodes, auch nach Asset anlegen und löschen.
Wiki: neue Seite „Plugins“ (Format, API v1, Grenzen).
Offen:
Ladereihenfolge mehrerer Plugins. Vorschlag: nach Dateiname.
Zwei Plugins (oder ein Plugin und ein eingebauter Tab) mit derselben Tab-Id. Vorschlag: der zweite wird abgelehnt, Console-Warnung.
Folge-Issues:#114 Aktivieren unter Settings · #115 Sicherheit · #116 Upload
Der Editor soll sich per Plugin erweitern lassen. Ein Plugin ist eine einzelne `.js`-Datei; hochladen und aktivieren kommt später (eigene Issues, siehe unten). Dieses Issue legt die Architektur fest und liefert **v1 nur mit dem, was das Demo-Plugin braucht**.
**Demo-Plugin (Ziel von v1):**
- ein eigener Tab
- jedes Asset des Projekts als Node in einem Graph: leerer Node, Asset-Name als Titel. Kein Editing, keine Connections; Pan, Zoom und Fit gehen.
- Asset anlegen, umbenennen oder löschen → der Tab zieht nach.
**Entscheidungen (Planung 2026-09-17):**
1. **Aktivierung:** Alles, was in `data/plugins/` liegt, ist aktiv. Aktiv/inaktiv unter Settings → #114.
2. **Serverweit**, nicht pro Projekt.
3. **Quelle im Repo:** `plugins/demo-plugin.js`. `npm run build` und `tools/seed-e2e.mjs` kopieren sie nach `data/plugins/` (damit hat auch CI das Plugin). `data/plugins/` kommt in `.gitignore`.
4. **Umfang:** Architektur + API v1 nur für die Demo. Schreiben mit Undo, eigene Node-Typen im Behavior-Graph, eigene Asset-Typen, Inspector-Felder und Laufzeit-Hooks kommen als Folge-Issues, sobald es echte Nutzer dafür gibt – eine „vollständige“ API ohne Nutzer wird geraten.
5. **Nur im Editor**, nicht im laufenden Spiel und nicht im Export.
6. **Sicherheit:** Ein Plugin läuft mit allen Rechten des Editors. Für v1 hingenommen → #115.
7. **Keine Typen** (`.d.ts`) für Plugin-Autoren vorerst.
**Zu bauen:**
- **Plugin-Format:** ES-Modul mit `export const meta = { id, name, version, api: 1 }`, `export function activate(api)`, optional `export function deactivate()`. Das Plugin importiert nichts – insbesondere kein eigenes Pixi (ein WebGL-Canvas, eine Pixi-Instanz); alles kommt über `api`.
- **Route `plugins`** in `api/index.php`: listet `data/plugins/*.js` und liefert eine Datei mit `Content-Type: text/javascript` aus. Nur Dateinamen aus dem Ordner, kein Pfad (kein `..`).
- **Loader im Editor:** lädt beim Start alle Plugins per `import()` und ruft `activate`. Fehler beim Laden oder in `activate` → Console + Hinweis, der Editor läuft weiter. Ein Plugin mit unbekannter `api`-Version wird nicht aktiviert.
- **Plugin-API v1** (versioniert):
- `api.project` – Assets lesen (`id`, `name`, `type`), Änderungen abonnieren (`on('change', fn)`, liefert eine Abmelde-Funktion)
- `api.tabs.add({ id, label, create })` – dynamische Tabs zusätzlich zur festen `CentreTab`-Liste in `src/editor/Editor.ts`; deren Typsicherheit für die eingebauten Tabs bleibt.
- `api.ui.NodeBoard` – reine Anzeige, `setNodes([{ id, title, x, y }])`, Pan/Zoom/Fit über `FreeViewport`, Look von `NodeView`. `GraphCanvas` wird dafür **nicht** wiederverwendet (hängt an `EditorState` und am Behavior-Graph).
- **Demo-Plugin** `plugins/demo-plugin.js`: Tab „Assets“, ein Node je Asset, im Raster angeordnet.
- **Tests:** Unit-Tests für Loader (defektes Plugin, falsche `api`-Version, Fehler in `activate`) und API; ein e2e-Modul öffnet den Tab und zählt die Nodes, auch nach Asset anlegen und löschen.
- **Wiki:** neue Seite „Plugins“ (Format, API v1, Grenzen).
**Offen:**
- Ladereihenfolge mehrerer Plugins. Vorschlag: nach Dateiname.
- Zwei Plugins (oder ein Plugin und ein eingebauter Tab) mit derselben Tab-Id. Vorschlag: der zweite wird abgelehnt, Console-Warnung.
**Folge-Issues:** #114 Aktivieren unter Settings · #115 Sicherheit · #116 Upload
Umgesetzt in 57a97ba (Wiki 0576bba, neue Seite Plugins).
Was jetzt geht
Route plugins (api/index.php, api/lib/PluginStore.php): GET ?r=plugins listet die .js-Dateien in data/plugins/, sortiert nach Dateiname. GET ?r=plugins/{file} liefert eine Datei als text/javascript (nosniff, no-cache). Nur nackte Dateinamen: ../projects, x/../../api/index.php, .hidden.js, index.php → 404, POST → 405 (geprüft mit php -S im PHP-Container).
Loader (src/editor/plugins/): lädt nach dem Projekt, nacheinander in Dateinamen-Reihenfolge. Datei lädt nicht, meta/activate fehlt, falsche api-Version, doppelte meta.id, activate wirft → Plugin inaktiv, Zeile in der Console, Hinweis in der Toolbar; Editor und übrige Plugins laufen weiter.
API v1:api.project.assets() (Kopien), api.project.on('change', fn) (nur bei geänderter Asset-Liste: anlegen, umbenennen, löschen, Undo, Projektwechsel; liefert eine Abmelde-Funktion), api.tabs.add(…), api.ui.NodeBoard.
Offene Punkte entschieden wie vorgeschlagen: Reihenfolge nach Dateiname; belegte Tab-Id wird abgelehnt (false + Warnung, create wird nicht aufgerufen).
Demo-Pluginplugins/demo-plugin.js: Tab Assets, ein Node je Asset, vier pro Reihe. Build und seed-e2e kopieren nach data/plugins/ (gitignored).
Abweichungen
Nicht FreeViewport (der ist für Scene-Objekte): Grid, Zoom-Grenzen und Fit kommen aus dem Behavior-Graph und liegen jetzt gemeinsam in graph/graphView.ts.
Das Board bringt selbst einen Fit-Knopf und die Node-Zahl mit, weil ein Plugin in v1 keine Knöpfe bauen kann.
deactivate wird geprüft, aber noch nicht aufgerufen (kommt mit #114). Wirft activate nach tabs.add, bleibt der Tab stehen. Die Nodes zeigen den Einklapp-Pfeil aus NodeView.
ESLint prüft plugins/*.js und verbietet dort import.
Tests (lokal)
npm run check → 93 Files, 1246 Tests (vorher 1228), CHECK EXIT 0
npm run build → BUILD EXIT 0
GM_STEPS=38 (frisch geseedet) → ALL PASSED – 17 checks
GM_STEPS=1-16,19,23,26,36,38 (frisch geseedet) → ALL PASSED – 600 checks
18 neue Unit-Tests; Gegenprobe mit 4 absichtlich eingebauten Fehlern (Versionsprüfung, change-Filter, Tab-Id, doppelte Plugin-Id) → je genau ein Test rot, Originale zurück und per cmp geprüft.
Neues e2e-Modul 38 mit 15 Checks. Summe damit 963 (gerechnet, CI noch nicht gelesen).
Umgesetzt in `57a97ba` (Wiki `0576bba`, neue Seite **Plugins**).
## Was jetzt geht
- **Route `plugins`** (`api/index.php`, `api/lib/PluginStore.php`): `GET ?r=plugins` listet die `.js`-Dateien in `data/plugins/`, sortiert nach Dateiname. `GET ?r=plugins/{file}` liefert eine Datei als `text/javascript` (`nosniff`, `no-cache`). Nur nackte Dateinamen: `../projects`, `x/../../api/index.php`, `.hidden.js`, `index.php` → 404, `POST` → 405 (geprüft mit `php -S` im PHP-Container).
- **Loader** (`src/editor/plugins/`): lädt nach dem Projekt, nacheinander in Dateinamen-Reihenfolge. Datei lädt nicht, `meta`/`activate` fehlt, falsche `api`-Version, doppelte `meta.id`, `activate` wirft → Plugin inaktiv, Zeile in der Console, Hinweis in der Toolbar; Editor und übrige Plugins laufen weiter.
- **API v1:** `api.project.assets()` (Kopien), `api.project.on('change', fn)` (nur bei geänderter Asset-Liste: anlegen, umbenennen, löschen, Undo, Projektwechsel; liefert eine Abmelde-Funktion), `api.tabs.add(…)`, `api.ui.NodeBoard`.
- **Offene Punkte entschieden wie vorgeschlagen:** Reihenfolge nach Dateiname; belegte Tab-Id wird abgelehnt (`false` + Warnung, `create` wird nicht aufgerufen).
- **Demo-Plugin** `plugins/demo-plugin.js`: Tab *Assets*, ein Node je Asset, vier pro Reihe. Build und `seed-e2e` kopieren nach `data/plugins/` (gitignored).
## Abweichungen
- Nicht `FreeViewport` (der ist für Scene-Objekte): Grid, Zoom-Grenzen und Fit kommen aus dem Behavior-Graph und liegen jetzt gemeinsam in `graph/graphView.ts`.
- Das Board bringt selbst einen Fit-Knopf und die Node-Zahl mit, weil ein Plugin in v1 keine Knöpfe bauen kann.
- `deactivate` wird geprüft, aber noch nicht aufgerufen (kommt mit #114). Wirft `activate` nach `tabs.add`, bleibt der Tab stehen. Die Nodes zeigen den Einklapp-Pfeil aus `NodeView`.
- ESLint prüft `plugins/*.js` und verbietet dort `import`.
## Tests (lokal)
```
npm run check → 93 Files, 1246 Tests (vorher 1228), CHECK EXIT 0
npm run build → BUILD EXIT 0
GM_STEPS=38 (frisch geseedet) → ALL PASSED – 17 checks
GM_STEPS=1-16,19,23,26,36,38 (frisch geseedet) → ALL PASSED – 600 checks
```
- 18 neue Unit-Tests; Gegenprobe mit 4 absichtlich eingebauten Fehlern (Versionsprüfung, `change`-Filter, Tab-Id, doppelte Plugin-Id) → je genau ein Test rot, Originale zurück und per `cmp` geprüft.
- Neues e2e-Modul 38 mit 15 Checks. Summe damit **963** (gerechnet, CI noch nicht gelesen).
Folgethemen: #114 (aktivieren/deaktivieren), #115 (Sicherheit), #116 (Upload).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Der Editor soll sich per Plugin erweitern lassen. Ein Plugin ist eine einzelne
.js-Datei; hochladen und aktivieren kommt später (eigene Issues, siehe unten). Dieses Issue legt die Architektur fest und liefert v1 nur mit dem, was das Demo-Plugin braucht.Demo-Plugin (Ziel von v1):
Entscheidungen (Planung 2026-09-17):
data/plugins/liegt, ist aktiv. Aktiv/inaktiv unter Settings → #114.plugins/demo-plugin.js.npm run buildundtools/seed-e2e.mjskopieren sie nachdata/plugins/(damit hat auch CI das Plugin).data/plugins/kommt in.gitignore..d.ts) für Plugin-Autoren vorerst.Zu bauen:
export const meta = { id, name, version, api: 1 },export function activate(api), optionalexport function deactivate(). Das Plugin importiert nichts – insbesondere kein eigenes Pixi (ein WebGL-Canvas, eine Pixi-Instanz); alles kommt überapi.pluginsinapi/index.php: listetdata/plugins/*.jsund liefert eine Datei mitContent-Type: text/javascriptaus. Nur Dateinamen aus dem Ordner, kein Pfad (kein..).import()und ruftactivate. Fehler beim Laden oder inactivate→ Console + Hinweis, der Editor läuft weiter. Ein Plugin mit unbekannterapi-Version wird nicht aktiviert.api.project– Assets lesen (id,name,type), Änderungen abonnieren (on('change', fn), liefert eine Abmelde-Funktion)api.tabs.add({ id, label, create })– dynamische Tabs zusätzlich zur festenCentreTab-Liste insrc/editor/Editor.ts; deren Typsicherheit für die eingebauten Tabs bleibt.api.ui.NodeBoard– reine Anzeige,setNodes([{ id, title, x, y }]), Pan/Zoom/Fit überFreeViewport, Look vonNodeView.GraphCanvaswird dafür nicht wiederverwendet (hängt anEditorStateund am Behavior-Graph).plugins/demo-plugin.js: Tab „Assets“, ein Node je Asset, im Raster angeordnet.api-Version, Fehler inactivate) und API; ein e2e-Modul öffnet den Tab und zählt die Nodes, auch nach Asset anlegen und löschen.Offen:
Folge-Issues: #114 Aktivieren unter Settings · #115 Sicherheit · #116 Upload
Umgesetzt in
57a97ba(Wiki0576bba, neue Seite Plugins).Was jetzt geht
plugins(api/index.php,api/lib/PluginStore.php):GET ?r=pluginslistet die.js-Dateien indata/plugins/, sortiert nach Dateiname.GET ?r=plugins/{file}liefert eine Datei alstext/javascript(nosniff,no-cache). Nur nackte Dateinamen:../projects,x/../../api/index.php,.hidden.js,index.php→ 404,POST→ 405 (geprüft mitphp -Sim PHP-Container).src/editor/plugins/): lädt nach dem Projekt, nacheinander in Dateinamen-Reihenfolge. Datei lädt nicht,meta/activatefehlt, falscheapi-Version, doppeltemeta.id,activatewirft → Plugin inaktiv, Zeile in der Console, Hinweis in der Toolbar; Editor und übrige Plugins laufen weiter.api.project.assets()(Kopien),api.project.on('change', fn)(nur bei geänderter Asset-Liste: anlegen, umbenennen, löschen, Undo, Projektwechsel; liefert eine Abmelde-Funktion),api.tabs.add(…),api.ui.NodeBoard.false+ Warnung,createwird nicht aufgerufen).plugins/demo-plugin.js: Tab Assets, ein Node je Asset, vier pro Reihe. Build undseed-e2ekopieren nachdata/plugins/(gitignored).Abweichungen
FreeViewport(der ist für Scene-Objekte): Grid, Zoom-Grenzen und Fit kommen aus dem Behavior-Graph und liegen jetzt gemeinsam ingraph/graphView.ts.deactivatewird geprüft, aber noch nicht aufgerufen (kommt mit #114). Wirftactivatenachtabs.add, bleibt der Tab stehen. Die Nodes zeigen den Einklapp-Pfeil ausNodeView.plugins/*.jsund verbietet dortimport.Tests (lokal)
change-Filter, Tab-Id, doppelte Plugin-Id) → je genau ein Test rot, Originale zurück und percmpgeprüft.Folgethemen: #114 (aktivieren/deaktivieren), #115 (Sicherheit), #116 (Upload).