Einführung
Plugins sind eine Möglichkeit, den K3 durch Programmierung zu erweitern. Beachten Sie, derzeit sind Plugins als
Einige Plugins gibt es bereits im K3 Plugin Store – Gruppierung, Positionierung, Boolesche Operation, Hotspots und Veranda. Was sie können und wie sie eingerichtet werden, steht in der Plugin-Übersicht. Dieser Bereich hier beschreibt, wie Sie eigene Plugins bauen.
Plugin erzeugen
Ein neues Plugin lässt sich mit einem einzigen Befehl anlegen:
npm create k3-plugin
Der Scaffolder fragt nach einem Plugin-Namen und erzeugt ein fertig konfiguriertes Projekt mit allen nötigen Abhängigkeiten.
Abhängigkeiten installieren und starten:
cd <plugin-name>
cp .env.example .env # API_TOKEN und PLUGIN_ID eintragen
pnpm install
pnpm run dev
pnpm run dev baut das Plugin und startet einen lokalen Server. Das Plugin ist dann unter
http://localhost:5002/<FEDERATION_NAME>/remoteEntry.js
erreichbar — den FEDERATION_NAME legen Sie in der .env fest.
Vite Konfiguration
Die vite.config.ts des Sample Plugins ist bereits vollständig konfiguriert. Hier eine Übersicht, was Sie dort finden und warum:
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react";
import { federation } from "@module-federation/vite";
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), "");
const federationName = env.FEDERATION_NAME ?? "k3-plugin";
const federationModule = env.FEDERATION_MODULE ?? "Plugin";
return {
base: `/${federationName}/`,
plugins: [
react(),
federation({
name: federationName,
filename: "remoteEntry.js",
exposes: {
[`./${federationModule}`]: `./src/${federationModule}.tsx`,
},
shared: {
react: { singleton: true, requiredVersion: "19.1.1" },
"react-dom": { singleton: true, requiredVersion: "19.1.1" },
"@mui/material": { singleton: true, requiredVersion: "^7.1.1" },
"@mui/styled-engine": { singleton: true, requiredVersion: "^7.1.1" },
"@emotion/react": { singleton: true, requiredVersion: "^11.10.0" },
"@emotion/styled": { singleton: true, requiredVersion: "^11.10.5" },
"@emotion/cache": { singleton: true, requiredVersion: "^11.14.0" },
"@react-three/drei": { singleton: true, requiredVersion: "^10.1.2" },
"@react-three/fiber": { singleton: true, requiredVersion: "^9.1.2" },
three: { singleton: true, requiredVersion: "^0.177.0" },
"k3-plugin-api": { singleton: true, requiredVersion: "^2.0.0" },
},
}),
],
preview: {
cors: true,
port: 5002,
strictPort: true,
},
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
assetsInlineLimit: 0,
},
};
});
Hier wird folgendes festgelegt:
- Der name, der beim Anlagen des Plugins im K3 angegeben wird.
- Der filename ist immer
remoteEntry.js, die Datei wird beim Bauen des Plugins erzeugt. - exposes ist eine Liste von Modulen, die exportiert werden. Ein Modul besteht aus:
- dem Namen, der beim Anlagen des Plugins im K3 angegeben wird.
- dem Namen der Datei, die das Modul implementiert.
- Unter shared wird angegeben, welche Bibliotheken nicht im Plugin deployed, sondern vom K3 genutzt werden sollen.
Die MUI- und Emotion-Einträge im shared-Bereich sind nur erforderlich, wenn Sie Material-UI-Komponenten in Ihrem Plugin verwenden (z.B. für Custom Inputs oder Dialoge).
Falls Sie nur 3D-Modelle erstellen, können Sie diese Zeilen weglassen:
"@mui/material": { singleton: true, requiredVersion: "^7.1.1" },
"@mui/styled-engine": { singleton: true, requiredVersion: "^7.1.1" },
"@emotion/react": { singleton: true, requiredVersion: "^11.10.0" },
"@emotion/styled": { singleton: true, requiredVersion: "^11.10.5" },
"@emotion/cache": { singleton: true, requiredVersion: "^11.14.0" },
Wichtig: Wenn Sie diese Einträge im shared-Bereich belassen, müssen Sie die entsprechenden Pakete installieren, sonst schlägt der Build fehl.
TypeScript Konfiguration
Das Sample Plugin enthält bereits eine funktionierende tsconfig.json. Die wichtigsten Einstellungen im Überblick:
tsconfig.json des Sample Plugins:
{
"compilerOptions": {
"target": "ES2023",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2023", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
"jsx": "react-jsx",
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}
Wenn Sie JSX-Elemente wie <group>, <mesh>, oder <cylinderGeometry> verwenden möchten, müssen Sie in mindestens einer Datei etwas von @react-three/fiber importieren, damit die globalen JSX-Typdefinitionen geladen werden:
import type {} from "@react-three/fiber";
Dieser leere Type-Import lädt die JSX-Typen für alle .tsx Dateien im Projekt, ohne Code ins Bundle zu bringen.
Nach Änderungen an der tsconfig.json:
Starten Sie den TypeScript Language Server neu:
- Öffnen Sie die Befehlspalette (
Cmd+Shift+Pauf macOS /Ctrl+Shift+Pauf Windows/Linux) - Suchen Sie nach:
TypeScript: Restart TS Server - Bestätigen Sie mit Enter
Plugin Deklaration
Im Sample Plugin ist src/Plugin.tsx bereits vorhanden. Diese Datei ist der Einstiegspunkt Ihres Plugins und kann direkt angepasst werden.
Sie enthält einen default export mit einem K3PluginDescriptor:
import type { K3PluginDescriptor } from "k3-plugin-api";
export default {
id: "vendor.my-plugin",
version: "1.0.0",
// UI Erweiterungen: Layout, Inputs, Dialoge
ui: {
layout: { /* Header, Sidebar, Footer usw. */ },
inputs: { /* Custom Variable Visualisierungen */ },
dialogs: { /* Bestell-Dialog, Warnungen usw. */ }
},
// 3D/Viewer Erweiterungen
viewer: {
canvas: { /* Canvas HOC */ },
customLayoutComponents: { /* Custom React Komponenten */ },
models: [ /* Dynamische 3D-Modelle */ ],
labels: { /* Label/Hotspot Overlays */ }
},
// Logik & Callbacks
logic: {
config: { /* onUpdate, onSave, onSaveFiles, onSaveEvent */ },
camera: { /* Screenshot Kameras, Dimensionen */ },
core: { /* preprocessFullApp, onOpenPdf, onExportAR */ }
}
} satisfies K3PluginDescriptor;
Der Plugin Descriptor besteht aus:
- id: Eindeutige Kennung für das Plugin (z.B. "vendor.my-plugin")
- version: Semantic Versioning String (z.B. "1.0.0")
- settings: Konfigurationskomponente für Plugin-spezifische Einstellungen (optional)
- ui: UI Extension Points für Layout, Inputs und Dialoge (optional)
- viewer: 3D/Viewer Extension Points (optional)
- logic: Callbacks und Event Hooks (optional)
Alle Extension Points sind optional. Sie können nur die Bereiche implementieren, die Sie benötigen.
Plugin-Settings
Plugins können eigene Konfigurationseinstellungen speichern und verwalten. Dies ist nützlich für:
- API-Keys und Zugangsdaten
- Anpassbare Verhaltensmuster
- Benutzer-spezifische Präferenzen
- Plugin-Konfigurationen, die pro Konfigurator unterschiedlich sein können
Settings-Komponente definieren
Fügen Sie im Plugin Descriptor eine Settings-Komponente hinzu:
import type { K3PluginDescriptor } from "k3-plugin-api";
import { SettingsComponent } from "./SettingsComponent";
export default {
id: "vendor.my-plugin",
version: "1.0.0",
settings: SettingsComponent,
// ... weitere Extension Points
} satisfies K3PluginDescriptor;
Die Settings-Komponente erhält zwei Props:
interface SettingsProps {
settings: unknown; // Aktuelle Settings (oder undefined beim ersten Aufruf)
onSave: (settings: unknown) => void; // Callback zum Speichern
}
const SettingsComponent: React.FC<SettingsProps> = ({ settings, onSave }) => {
const [apiKey, setApiKey] = React.useState(settings?.apiKey || "");
return (
<div>
<h3>Plugin Konfiguration</h3>
<input
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="API Key"
/>
<button onClick={() => onSave({ apiKey })}>
Speichern
</button>
</div>
);
};
Settings-UI im K3
Wenn eine Settings-Komponente definiert ist:
- Erscheint in der Plugin-Tabelle ein Stift-Icon neben dem Plugin
- Ein Klick darauf öffnet ein Popup mit Ihrer Settings-Komponente
- Das Popup enthält automatisch Abbrechen und Speichern Buttons
- Beim Klick auf Speichern wird
onSave()aufgerufen
Settings abrufen
Um die gespeicherten Settings in Ihrem Plugin zu verwenden:
import { getSettings } from "k3-plugin-api";
// In Ihrer Komponente oder Logik:
const pluginSettings = getSettings("vendor.my-plugin");
console.log(pluginSettings?.apiKey);
Wichtig: Die Plugin-ID in getSettings() muss mit der id im Plugin Descriptor übereinstimmen.
Beispiel: Vollständige Settings-Implementierung
// SettingsComponent.tsx
import React, { useState } from "react";
interface MyPluginSettings {
apiKey: string;
maxRetries: number;
enableDebug: boolean;
}
interface SettingsProps {
settings: unknown;
onSave: (settings: unknown) => void;
}
export const SettingsComponent: React.FC<SettingsProps> = ({
settings,
onSave
}) => {
const currentSettings = settings as MyPluginSettings | undefined;
const [apiKey, setApiKey] = useState(currentSettings?.apiKey || "");
const [maxRetries, setMaxRetries] = useState(currentSettings?.maxRetries || 3);
const [enableDebug, setEnableDebug] = useState(currentSettings?.enableDebug || false);
const handleSave = () => {
onSave({
apiKey,
maxRetries,
enableDebug
} satisfies MyPluginSettings);
};
return (
<div style={{ padding: "20px" }}>
<h3>Plugin Einstellungen</h3>
<div>
<label>API Key:</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<div>
<label>Max Retries:</label>
<input
type="number"
value={maxRetries}
onChange={(e) => setMaxRetries(Number(e.target.value))}
/>
</div>
<div>
<label>
<input
type="checkbox"
checked={enableDebug}
onChange={(e) => setEnableDebug(e.target.checked)}
/>
Debug-Modus aktivieren
</label>
</div>
<button onClick={handleSave}>Speichern</button>
</div>
);
};
// Plugin.tsx
import type { K3PluginDescriptor } from "k3-plugin-api";
import { getSettings } from "k3-plugin-api";
import { SettingsComponent } from "./SettingsComponent";
import type { MyPluginSettings } from "./SettingsComponent";
export default {
id: "vendor.my-plugin",
version: "1.0.0",
settings: SettingsComponent,
logic: {
config: {
onUpdate: () => {
const settings = getSettings("vendor.my-plugin") as MyPluginSettings;
if (settings?.enableDebug) {
console.log("Debug-Modus aktiv");
}
// API-Key verwenden
if (settings?.apiKey) {
// API-Aufruf mit apiKey...
}
}
}
}
} satisfies K3PluginDescriptor;
Häufige Probleme und Lösungen
Nachdem Sie Ihr Plugin entwickelt und lokal bereitgestellt haben, können Sie es im K3 eintragen.
Ein Plugin tragen Sie in den Optionen eines Konfigurators unter Plugins ein. Es wird dann beim Start des Konfigurators geladen. Dazu können Sie 2 verschiedene URLs angeben:
- die eigentliche URL muss vom Nutzer des Konfigurators aus erreichbar sein, das Plugin wird von dort geladen.
- die Development URL können Sie nutzen, während Sie das Plugin entwickeln. Sie wird typischerweise auf
localhost zeigen (z.B.
http://localhost:5002/<FEDERATION_NAME>/remoteEntry.js), dann wird das Plugin aus Ihrer lokalen Entwicklungsumgebung geladen.
Ausserdem müssen Sie ausser Titel und Beschreibung folgende technische Felder befüllen:
- Name: Der Name aus der
vite.config.ts(z.B.k3-plugin-test) - Modul: Der Modul-Name aus der
exposes-Konfiguration (typischerweise./Plugin)
Die Werte für Name und Modul müssen exakt mit den Einträgen in Ihrer vite.config.ts übereinstimmen, sonst kann das Plugin nicht geladen werden.
TypeScript-Fehler: "Property 'group' does not exist on type 'JSX.IntrinsicElements'"
Dieses Problem tritt auf, wenn Sie React-Three-Fiber JSX-Elemente wie <group>, <mesh>, oder <cylinderGeometry> verwenden.
Ursache: TypeScript kennt die JSX-Typdefinitionen von @react-three/fiber nicht.
Lösung: Fügen Sie in der betreffenden .tsx Datei einen leeren Type-Import hinzu:
import type {} from "@react-three/fiber";
Alternativ können Sie in einer beliebigen Datei im Projekt etwas von @react-three/fiber importieren (z.B. useFrame, useThree), dann werden die JSX-Typen automatisch global verfügbar.
TypeScript-Fehler: "Cannot use JSX unless the '--jsx' flag is provided"
Ursache: Die tsconfig.json enthält keine JSX-Konfiguration.
Lösung: Fügen Sie in der tsconfig.json unter compilerOptions hinzu:
"jsx": "react-jsx"
Starten Sie danach den TypeScript Language Server neu (siehe TypeScript Konfiguration).
TypeScript-Fehler: "Could not find a declaration file for module 'react/jsx-runtime'"
Ursache: Die TypeScript-Typen für React sind nicht installiert.
Lösung:
pnpm install @types/react @types/react-dom --save-dev
Build-Fehler: Module not found oder Vite-Konfiguration wird nicht erkannt
Ursache: Die vite.config.ts existiert nicht oder ist fehlerhaft.
Lösung: Stellen Sie sicher, dass die vite.config.ts im Projekt-Root existiert mit korrekter Konfiguration (siehe Vite Konfiguration).
Bei neueren Vite-Versionen (8.x+) wird die Datei nicht immer automatisch erstellt und muss manuell angelegt werden.
Build-Fehler: "Cannot find module '@mui/material'" oder "@emotion/react"
Ursache: In der vite.config.ts sind MUI- oder Emotion-Pakete im shared-Bereich aufgeführt, aber nicht installiert.
Fehlermeldung:
[Module Federation] Error: Cannot find module '@mui/material/package.json'
[UNLOADABLE_DEPENDENCY] Error: Could not load @emotion/cache
Lösung 1 - MUI installieren (falls Sie es verwenden möchten):
pnpm install @mui/material @mui/styled-engine @emotion/react @emotion/styled @emotion/cache --save-dev
Lösung 2 - MUI aus shared entfernen (falls Sie es nicht benötigen):
Entfernen Sie die MUI- und Emotion-Zeilen aus dem shared-Bereich der vite.config.ts:
// Diese Zeilen entfernen:
"@mui/material": { singleton: true, requiredVersion: "^7.1.1" },
"@mui/styled-engine": { singleton: true, requiredVersion: "^7.1.1" },
"@emotion/react": { singleton: true, requiredVersion: "^11.10.0" },
"@emotion/styled": { singleton: true, requiredVersion: "^11.10.5" },
"@emotion/cache": { singleton: true, requiredVersion: "^11.14.0" },
Nach dem Entfernen sollten Sie den Build-Cache löschen:
rm -rf .__mf__temp dist node_modules/.vite
Änderungen in tsconfig.json werden nicht übernommen
Ursache: Der TypeScript Language Server muss nach Konfigurationsänderungen neu gestartet werden.
Lösung:
Cmd+Shift+P(macOS) oderCtrl+Shift+P(Windows/Linux)- Suchen:
TypeScript: Restart TS Server - Enter drücken
CORS-Probleme beim Laden
Während der lokalen Entwicklung können CORS-Fehler auftreten. Fügen Sie in Ihrer vite.config.ts folgende Konfiguration hinzu:
export default defineConfig({
// ... andere Einstellungen
server: {
cors: true,
},
preview: {
cors: true,
headers: {
"Access-Control-Allow-Origin": "*",
},
},
});
Starten Sie danach den Server neu.
Nächste Schritte
- Runtime Hooks – Konfigurationszustand lesen und schreiben, Merkmale rendern
- Layout-Komponenten – Headerleiste, Sidebar, Footer und mehr anpassen
- Input-Komponenten – Custom Variable Visualisierungen erstellen
- Dialoge – Bestell-Dialoge und Warnungen anpassen
- 3D/Viewer – Canvas, Custom Layout Components und Labels
- Logik & Events – Config, Camera und Core Callbacks
- Dynamische 3D-Modelle – Eigene 3D-Modelle mit React-Three-Fiber erstellen