Plugin support: Was kann ein Plugin – Inspector, eigene Felder, Graph (Konzept) #117

Open
opened 2026-09-17 19:01:47 +02:00 by meleki · 2 comments
Owner

Folge von #113. Ein Konzept-Issue: Was kann ein Plugin erreichen und was nicht? Und welche API-Erweiterungen braucht es für den Inspector und für eigene Felder (Text, Number …) in einem Plugin-Tab, mit und ohne Graph? Hier wird nichts gebaut. Ergebnis sind Entscheidungen und daraus geschnittene Folge-Issues.

Stand nach #113 (API v1)

  • api.project.assets() liefert nur Kopien von id, name und type, api.project.on('change') meldet nur Änderungen der Asset-Liste.
  • api.tabs.add fügt einen Tab hinzu, api.ui.NodeBoard zeigt Nodes nur an.
  • Plugins werden nach dem Projekt geladen und wirken nur im Editor.
  • Technisch läuft ein Plugin im selben JS-Kontext und könnte über Umwege überall hineingreifen (#115). Unterstützt ist aber nur die api. Alles andere bricht beim nächsten Umbau.

Machbar mit API-Erweiterung

1. Inspector erweitern – ja

Der Inspector (src/editor/inspector/Inspector.ts) baut je Auswahlart eine Section (Asset, Object, Node, Frame, Track …). Wann neu gebaut und wann nur aufgefrischt wird, entscheidet eine Signatur.

  • Vorschlag: api.inspector.addSection({ id, title, appliesTo(selection), build(section), refresh?(section) }). Die Section kommt unter die eingebauten Sections.
  • Die Plugin-Section bekommt dieselbe Aufteilung in Neuaufbau und Auffrischen. Sonst würde bei jedem Drag-Schritt alles neu gebaut, und ein Feld, in dem gerade getippt wird, verlöre den Fokus. Der Inspector hält das heute schon zurück (rebuildWhenLeft).
  • Die eingebauten Zeilen ändern oder ausblenden kann ein Plugin nicht. Es kann nur ergänzen.

2. Eigene Felder im Tab ohne Graph – ja

Die Widgets gibt es alle schon in src/ui/widgets (TextField, NumberField, Checkbox, Dropdown, ColorField, Slider, Button, Label, dazu Layouts und ScrollContainer).

  • Vorschlag: api.ui bietet sie als Fabriken an, etwa api.ui.numberField({ value, min, max, onChange }) und api.ui.vstack([...]). Das Plugin importiert nichts, weil Pixi nur einmal existieren darf.
  • Hält das Plugin die Werte selbst (z. B. Filter oder Anzeige-Optionen), reicht das.
  • Sollen die Werte im Projekt landen, braucht es Punkt 4.

3. Eigene Felder mit Graph – ja, in zwei Stufen

  • a) Auswahl im Board: Das NodeBoard meldet on('select', nodeId). Das Plugin zeigt die Felder dann in einer eigenen Spalte im Tab oder über Punkt 1 im Inspector. Das ist wenig Aufwand, und die Nodes bleiben einfach.
  • b) Felder direkt am Node: Nodes bekommen deklarierte Felder wie die Inline-Ports im Behavior-Graph (NodeView), z. B. { id, title, fields: [{ key, kind: 'number', value }] }. Dafür muss NodeBoard Eingaben, Fokus und Tastatur auf dem Board sauber lösen. Das ist deutlich mehr Aufwand.
  • Verbindungen und Kanten sind dabei nicht vorgesehen. Die wären ein eigenes Thema.

4. Plugin-Daten im Projekt speichern – Voraussetzung für 1–3, sobald etwas gespeichert wird

Harter Befund: deserializeProject (src/core/store/serialize.ts) wirft beim Laden alles weg, was das Schema nicht kennt. Ein Plugin kann also nicht einfach eigene Keys in asset.props schreiben, nach dem Neuladen wären sie weg.

  • Vorschlag: ein eigener, deklarierter Platz pluginData: { [pluginId]: JSON } am Asset und/oder am Projekt. Beim Laden bleibt er unangetastet, auch wenn das Plugin gerade nicht aktiv ist (#114). Sonst löscht ein Deaktivieren und Speichern die Daten.
  • Geschrieben wird nur über ein Kommando, api.project.setData(assetId, value) mit Undo. Direkten Zugriff aufs Modell gibt es nicht.
  • Offen ist eine Größengrenze pro Plugin.

Schwerer, eigene Entscheidung nötig

5. Eigene Node-Typen im Behavior-Graph

registerNode gibt es, aber:

  • Plugins laden nach dem Projekt. normalizeGraph verwirft Nodes mit unbekanntem Typ (serialize.ts:337). Ein gespeicherter Plugin-Node wäre nach dem Neuladen still gelöscht, samt Kanten.
  • Nötig wäre entweder, dass Plugins vor dem Projekt laden, oder dass unbekannte Nodes als Platzhalter „(missing)“ erhalten bleiben. Letzteres braucht es ohnehin, sobald ein Plugin deaktiviert wird.
  • Ausgeführt würde der Node nur beim Play im Editor. Das deckt sich mit der Entscheidung „nur Editor“ aus #113, ein Export kennt das Plugin nicht.

6. Eigene Asset-Typen

ASSET_TYPES ist eine feste Union, an der vollständige Records hängen (Labels, Tabs …). Beim Laden wird ein unbekannter type verworfen (serialize.ts:85). Machbar wäre höchstens ein generischer Typ plugin mit pluginId und Daten nach Punkt 4. Nicht für die nächste Stufe.

Nicht möglich oder nicht vorgesehen

  • Eigenes Pixi, eigener Renderer, DOM-UI: Alles läuft in einem WebGL-Canvas. Ein zweites Pixi macht den Renderer kaputt, HTML-Overlays brechen das Konzept.
  • Eingebaute Editoren umbauen: Scene-View, Timeline, Behavior-Graph und Zeilen des Inspectors werden nur ergänzt, nicht verändert oder ersetzt.
  • Code am Server: Ein Plugin ist nur JS im Browser, es gibt keine eigenen PHP-Routen.
  • Wirkung im exportierten Spiel: In #113 wurde „nur Editor“ entschieden.
  • Isolation: Echte Isolation gibt es nicht, siehe #115.

Offen

  1. Reihenfolge der Folge-Issues. Vorschlag: 4 (Daten) → 2 (Felder im Tab) → 1 (Inspector) → 3a (Auswahl im Board). 3b, 5 und 6 bleiben zurückgestellt.
  2. Wo liegen Plugin-Daten? Am Asset, am Projekt oder beides? Oder zusätzlich serverweit (Einstellungen eines Plugins, z. B. data/plugins/<id>.json, dafür bräuchte es eine Route)?
  3. Plugins vor dem Projekt laden? Das wäre nötig für 5 und bequemer für 1. Dagegen spricht, dass ein langsames Plugin den Editorstart bremst.
  4. Demo-Plugin erweitern? Es könnte als Beispiel ein Notizfeld pro Asset bekommen, im Tab und im Inspector, gespeichert über Punkt 4. Das deckt 1, 2 und 4 mit einem Stück ab.
Folge von #113. Ein Konzept-Issue: Was kann ein Plugin erreichen und was nicht? Und welche API-Erweiterungen braucht es für den **Inspector** und für **eigene Felder** (Text, Number …) in einem Plugin-Tab, mit und ohne Graph? Hier wird nichts gebaut. Ergebnis sind Entscheidungen und daraus geschnittene Folge-Issues. ## Stand nach #113 (API v1) - `api.project.assets()` liefert nur Kopien von `id`, `name` und `type`, `api.project.on('change')` meldet nur Änderungen der Asset-Liste. - `api.tabs.add` fügt einen Tab hinzu, `api.ui.NodeBoard` zeigt Nodes nur an. - Plugins werden **nach** dem Projekt geladen und wirken nur im Editor. - Technisch läuft ein Plugin im selben JS-Kontext und *könnte* über Umwege überall hineingreifen (#115). Unterstützt ist aber nur die `api`. Alles andere bricht beim nächsten Umbau. ## Machbar mit API-Erweiterung ### 1. Inspector erweitern – ja Der Inspector (`src/editor/inspector/Inspector.ts`) baut je Auswahlart eine Section (Asset, Object, Node, Frame, Track …). Wann neu gebaut und wann nur aufgefrischt wird, entscheidet eine Signatur. - Vorschlag: `api.inspector.addSection({ id, title, appliesTo(selection), build(section), refresh?(section) })`. Die Section kommt **unter** die eingebauten Sections. - Die Plugin-Section bekommt dieselbe Aufteilung in Neuaufbau und Auffrischen. Sonst würde bei jedem Drag-Schritt alles neu gebaut, und ein Feld, in dem gerade getippt wird, verlöre den Fokus. Der Inspector hält das heute schon zurück (`rebuildWhenLeft`). - Die eingebauten Zeilen **ändern** oder ausblenden kann ein Plugin nicht. Es kann nur ergänzen. ### 2. Eigene Felder im Tab **ohne** Graph – ja Die Widgets gibt es alle schon in `src/ui/widgets` (`TextField`, `NumberField`, `Checkbox`, `Dropdown`, `ColorField`, `Slider`, `Button`, `Label`, dazu Layouts und `ScrollContainer`). - Vorschlag: `api.ui` bietet sie als Fabriken an, etwa `api.ui.numberField({ value, min, max, onChange })` und `api.ui.vstack([...])`. Das Plugin importiert nichts, weil Pixi nur einmal existieren darf. - Hält das Plugin die Werte selbst (z. B. Filter oder Anzeige-Optionen), reicht das. - Sollen die Werte **im Projekt** landen, braucht es Punkt 4. ### 3. Eigene Felder **mit** Graph – ja, in zwei Stufen - **a) Auswahl im Board:** Das `NodeBoard` meldet `on('select', nodeId)`. Das Plugin zeigt die Felder dann in einer eigenen Spalte im Tab oder über Punkt 1 im Inspector. Das ist wenig Aufwand, und die Nodes bleiben einfach. - **b) Felder direkt am Node:** Nodes bekommen deklarierte Felder wie die Inline-Ports im Behavior-Graph (`NodeView`), z. B. `{ id, title, fields: [{ key, kind: 'number', value }] }`. Dafür muss `NodeBoard` Eingaben, Fokus und Tastatur auf dem Board sauber lösen. Das ist deutlich mehr Aufwand. - Verbindungen und Kanten sind dabei nicht vorgesehen. Die wären ein eigenes Thema. ### 4. Plugin-Daten im Projekt speichern – Voraussetzung für 1–3, sobald etwas gespeichert wird **Harter Befund:** `deserializeProject` (`src/core/store/serialize.ts`) wirft beim Laden alles weg, was das Schema nicht kennt. Ein Plugin kann also nicht einfach eigene Keys in `asset.props` schreiben, nach dem Neuladen wären sie weg. - Vorschlag: ein eigener, deklarierter Platz `pluginData: { [pluginId]: JSON }` am Asset und/oder am Projekt. Beim Laden bleibt er **unangetastet**, auch wenn das Plugin gerade nicht aktiv ist (#114). Sonst löscht ein Deaktivieren und Speichern die Daten. - Geschrieben wird nur über ein Kommando, `api.project.setData(assetId, value)` mit Undo. Direkten Zugriff aufs Modell gibt es nicht. - Offen ist eine Größengrenze pro Plugin. ## Schwerer, eigene Entscheidung nötig ### 5. Eigene Node-Typen im Behavior-Graph `registerNode` gibt es, aber: - Plugins laden **nach** dem Projekt. `normalizeGraph` verwirft Nodes mit unbekanntem Typ (`serialize.ts:337`). Ein gespeicherter Plugin-Node wäre nach dem Neuladen still gelöscht, samt Kanten. - Nötig wäre entweder, dass Plugins **vor** dem Projekt laden, oder dass unbekannte Nodes als Platzhalter „(missing)“ erhalten bleiben. Letzteres braucht es ohnehin, sobald ein Plugin deaktiviert wird. - Ausgeführt würde der Node nur beim Play im Editor. Das deckt sich mit der Entscheidung „nur Editor“ aus #113, ein Export kennt das Plugin nicht. ### 6. Eigene Asset-Typen `ASSET_TYPES` ist eine feste Union, an der vollständige `Record`s hängen (Labels, Tabs …). Beim Laden wird ein unbekannter `type` verworfen (`serialize.ts:85`). Machbar wäre höchstens ein generischer Typ `plugin` mit `pluginId` und Daten nach Punkt 4. Nicht für die nächste Stufe. ## Nicht möglich oder nicht vorgesehen - **Eigenes Pixi, eigener Renderer, DOM-UI:** Alles läuft in einem WebGL-Canvas. Ein zweites Pixi macht den Renderer kaputt, HTML-Overlays brechen das Konzept. - **Eingebaute Editoren umbauen:** Scene-View, Timeline, Behavior-Graph und Zeilen des Inspectors werden nur ergänzt, nicht verändert oder ersetzt. - **Code am Server:** Ein Plugin ist nur JS im Browser, es gibt keine eigenen PHP-Routen. - **Wirkung im exportierten Spiel:** In #113 wurde „nur Editor“ entschieden. - **Isolation:** Echte Isolation gibt es nicht, siehe #115. ## Offen 1. **Reihenfolge der Folge-Issues.** Vorschlag: 4 (Daten) → 2 (Felder im Tab) → 1 (Inspector) → 3a (Auswahl im Board). 3b, 5 und 6 bleiben zurückgestellt. 2. **Wo liegen Plugin-Daten?** Am Asset, am Projekt oder beides? Oder zusätzlich serverweit (Einstellungen eines Plugins, z. B. `data/plugins/<id>.json`, dafür bräuchte es eine Route)? 3. **Plugins vor dem Projekt laden?** Das wäre nötig für 5 und bequemer für 1. Dagegen spricht, dass ein langsames Plugin den Editorstart bremst. 4. **Demo-Plugin erweitern?** Es könnte als Beispiel ein Notizfeld pro Asset bekommen, im Tab und im Inspector, gespeichert über Punkt 4. Das deckt 1, 2 und 4 mit einem Stück ab.
admin added this to the Development project 2026-09-17 19:16:52 +02:00
admin moved this to Next in Development on 2026-09-17 19:32:47 +02:00

zu den offenen fragen:

  1. reihenfolge der folge issues, dein vorschlag
  2. data/plugins/<id>.json passt, serverweit, kann man pro projekt aktivieren/deaktivieren, upload lassen wir noch weg, da gibt es dann vielleicht noch eine umentscheidung - es gibt noch keine user verwaltung deshalb absolut irrelevant momentan
  3. plugins vor dem projekt laden!
  4. demo plugin nach belieben erweitern, soll ein aussagekräftiges test plugin werden
zu den offenen fragen: 1. reihenfolge der folge issues, dein vorschlag 2. `data/plugins/<id>.json` passt, serverweit, kann man pro projekt aktivieren/deaktivieren, upload lassen wir noch weg, da gibt es dann vielleicht noch eine umentscheidung - es gibt noch keine user verwaltung deshalb absolut irrelevant momentan 3. plugins vor dem projekt laden! 4. demo plugin nach belieben erweitern, soll ein aussagekräftiges test plugin werden
Author
Owner

Entschieden, Folge-Issues angelegt

Deine Antworten aus dem Kommentar oben, dazu zwei Rückfragen, die ich gestellt habe:

  1. Reihenfolge: mein Vorschlag, also Daten → Felder im Tab → Inspector → Auswahl im Board. Davor kommt der Umbau des Startwegs, weil alles andere darauf sitzt.
  2. Plugin-Daten: nur im Projekt (pluginData). Eine serverweite Datei data/plugins/<id>.json gibt es nicht — auch die Einstellungen eines Plugins liegen im Projekt und gelten nur dort.
  3. Aktivierung: künftig pro Projekt. Der serverweite Schalter aus #114 wird dadurch ersetzt, nicht ergänzt.
  4. Plugins laden vor dem Projekt.
  5. Demo-Plugin wird zu einem aussagekräftigen Test-Plugin ausgebaut.

Wie 3 und 4 zusammengehen

Welche Plugins an sind, steht dann im Projekt — das Projekt muss also gelesen sein, bevor ein Plugin lädt. Gelöst wird das so: die Projektdatei wird als rohes JSON geholt, daraus die Plugin-Liste gelesen, dann laden und activate, und erst danach deserializeProject. Weggeworfen wird nämlich erst beim Deserialisieren (normalizeGraph, serialize.ts:337). Deshalb sind Ladeweg und Aktivierung ein gemeinsames Issue.

Eine Folge, die festzuhalten ist

In #113/#114 wurde ein ausgeschaltetes Plugin nie ausgeliefert (403). Mit der Aktivierung im Projekt kann der Server das nicht mehr wissen: jede Plugin-Datei ist abrufbar, ausgeführt wird sie nur, wenn das offene Projekt sie nennt. Ohne Benutzerverwaltung ist das ohne Belang; für #115 und #116 ist es dort zu berücksichtigen.

Die Folge-Issues, in der Reihenfolge der Umsetzung

# Inhalt
#119 Plugins vor dem Projekt laden, Aktivierung pro Projekt (baut #114 um)
#120 pluginData am Asset und am Projekt, Schreiben nur über ein Kommando mit Undo
#121 api.ui-Fabriken: eigene Felder im Plugin-Tab
#122 api.inspector.addSection
#123 NodeBoard meldet die Auswahl
#124 Demo-Plugin als Test-Plugin, deckt alles davon ab (zuletzt)

Zurückgestellt (held-back): #125 (Felder direkt am Node), #126 (eigene Node-Typen), #127 (eigene Asset-Typen).

In jedem Issue stehen Befund, „Zu bauen" und die noch offenen Punkte samt Vorschlag. Gebaut ist nichts.

## Entschieden, Folge-Issues angelegt Deine Antworten aus dem Kommentar oben, dazu zwei Rückfragen, die ich gestellt habe: 1. **Reihenfolge:** mein Vorschlag, also Daten → Felder im Tab → Inspector → Auswahl im Board. Davor kommt der Umbau des Startwegs, weil alles andere darauf sitzt. 2. **Plugin-Daten:** **nur im Projekt** (`pluginData`). Eine serverweite Datei `data/plugins/<id>.json` gibt es nicht — auch die Einstellungen eines Plugins liegen im Projekt und gelten nur dort. 3. **Aktivierung:** künftig **pro Projekt**. Der serverweite Schalter aus #114 wird dadurch ersetzt, nicht ergänzt. 4. **Plugins laden vor dem Projekt.** 5. **Demo-Plugin** wird zu einem aussagekräftigen Test-Plugin ausgebaut. ### Wie 3 und 4 zusammengehen Welche Plugins an sind, steht dann im Projekt — das Projekt muss also gelesen sein, bevor ein Plugin lädt. Gelöst wird das so: die Projektdatei wird als **rohes JSON** geholt, daraus die Plugin-Liste gelesen, dann laden und `activate`, und **erst danach** `deserializeProject`. Weggeworfen wird nämlich erst beim Deserialisieren (`normalizeGraph`, `serialize.ts:337`). Deshalb sind Ladeweg und Aktivierung ein gemeinsames Issue. ### Eine Folge, die festzuhalten ist In #113/#114 wurde ein ausgeschaltetes Plugin **nie ausgeliefert** (403). Mit der Aktivierung im Projekt kann der Server das nicht mehr wissen: jede Plugin-Datei ist abrufbar, ausgeführt wird sie nur, wenn das offene Projekt sie nennt. Ohne Benutzerverwaltung ist das ohne Belang; für #115 und #116 ist es dort zu berücksichtigen. ### Die Folge-Issues, in der Reihenfolge der Umsetzung | # | Inhalt | |---|---| | #119 | Plugins vor dem Projekt laden, Aktivierung pro Projekt (baut #114 um) | | #120 | `pluginData` am Asset und am Projekt, Schreiben nur über ein Kommando mit Undo | | #121 | `api.ui`-Fabriken: eigene Felder im Plugin-Tab | | #122 | `api.inspector.addSection` | | #123 | `NodeBoard` meldet die Auswahl | | #124 | Demo-Plugin als Test-Plugin, deckt alles davon ab (zuletzt) | Zurückgestellt (`held-back`): #125 (Felder direkt am Node), #126 (eigene Node-Typen), #127 (eigene Asset-Typen). In jedem Issue stehen Befund, „Zu bauen" und die noch offenen Punkte samt Vorschlag. Gebaut ist nichts.
admin moved this to Queue in Development on 2026-09-17 20:37:40 +02:00
meleki referenced this issue from a commit 2026-09-20 14:24:29 +02:00
Plugins belong to the project, and they load before it: the project file gained the field plugins: ["demo-plugin.js"] (default empty, no formatVersion bump), the Plugins tab's switch writes into it as an ordinary undo step (SetPluginActiveCommand) instead of into a server-wide plugins.json, and the start order is turned around - startPlugins only fetches the list of files, then editor.boot() runs and ProjectStore.load(id, prepare) hands the raw JSON through before it is deserialized, where pluginsOf(raw) reads the list and PluginManager.syncTo() activates exactly those; that is the point of the issue, because what a plugin registers has to stand there before normalizeGraph would throw it away as unknown (#126, #127), a project switch first stops what the new project does not name and then starts what it does, a file the server does not have is passed over silently, and after signing out the manager is dead so a late event cannot start anything in a torn-down session; on the server plugins.json, PluginStore::setActive, isActive, the route PUT ?r=plugins/{file} and the 403 for inactive modules are gone and GET ?r=plugins only answers {file,bytes,updatedAt}, which makes every plugin file fetchable - the consequence accepted in #117, written down in the wiki for #115 and #116; publishPlugins(root) only copies files now, the seeder switches the demo plugin on in the fixture project, and the Plugins tab carries a puzzle piece as its icon while only a plugin's own tab still goes without one (the knob and the socket are cubic curves, not arcs, which Pixi's parser draws as a spike after an L); 3 new e2e checks in module 38 and 5 new unit tests cover the project as the owner of the switch, its undo, the order, pluginsOf against crude input and an unknown file name (#119)
admin added the low-priority label 2026-09-21 08:08:35 +02:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: meleki/feuerwerk#117