Zum Inhalt springen

Apps

Apps programmieren

Die Referenz für Entwickler, die eine App selbst schreiben: das .nxa-Paket und sein Manifest, das .nxd-Dokumentformat, was eine App in der Sandbox darf, das Nachrichtenprotokoll zwischen App und nexxus, wie der KI-Assistent und Mitbearbeitende Ihre Dokumente ändern, und wie Sie testen und veröffentlichen.

Für
Entwickler
Dauer
25 Min.

Eine App besteht aus einer HTML-Datei und einer Beschreibungsdatei. Das HTML zeichnet ein Dokument und meldet, was die Person darin ändert. Alles andere erledigt nexxus: Dokument laden und speichern, Versionen, Freigaben, gemeinsames Bearbeiten in Echtzeit, der KI-Assistent, die Rechte. Ihre App berührt nie einen Speicher und sieht nie das Netz. Das hält Apps klein, und deshalb lässt nexxus jeden Apps installieren.

Diese Seite ist der Vertrag. Sie beschreibt, was heute in nexxus ausgeliefert wird.

nexxus

  • DateienLädt und speichert das .nxd-Dokument, führt seine Versionen
  • MitbearbeitendeIhre Änderungen kommen an, während die App offen ist
  • KI-AssistentSchreibt gegen das Schema geprüfte Änderungen in die Daten

Ihre App

app.html in einem abgeschotteten Rahmen, ohne Netz, ohne Speicher

  • nxd:init
  • nxd:apply
  • nxd:change
  • nxd:export

Zurück an nexxus

  • ÄnderungenDie vollständigen neuen Daten bei jeder abgeschlossenen Änderung
  • Dateien zum SpeichernEin Bild, eine HTML-Seite, ein JSON-Export
  • Fragen an den KI-AssistentenDas Element, auf das die Person zeigt
Die App zeichnet nur Daten und meldet Änderungen. Alle Nachrichten laufen über einen privaten Kanal zwischen nexxus und dem Rahmen.

Eine App wird als ZIP-Datei mit der Endung .nxa ausgeliefert. nexxus liest daraus zwei Einträge, gefunden über ihren Dateinamen, egal wo im Archiv:

  • Ordnerhello.nxa ein ZIP-Archiv
    • manifest.json ID, Version, Name, Symbol, Inhaltstyp, Datenschema
    • app.html die Oberfläche, alles eingebettet

Andere Einträge ignoriert nexxus. Bauen Sie das Paket mit einem beliebigen ZIP-Werkzeug, zum Beispiel:

Terminal-Fenster
zip -j hello.nxa manifest.json app.html

Die Hilfe in der App enthält ein funktionierendes Beispiel: Unter StartApps öffnet Hilfe eine Erklärung, und Beispiel-App herunterladen speichert die Datei hello.nxa. Jeder Download trägt eine frische ID wie hello-ab12c, so lässt sich das Beispiel mehrmals in einem Mandanten installieren.

Der Hilfe-Dialog mit dem Paketaufbau und dem Beispiel zum Herunterladen
Die Hilfe erklärt das Paket und bietet eine funktionierende Beispiel-App an.

manifest.json beschreibt die App. nexxus prüft es vollständig, bevor etwas gespeichert wird: beim Hochladen, bei einer neuen Version und wenn der KI-Assistent eine App registriert.

So sieht das Manifest der Beispiel-App aus, wie nexxus es erzeugt:

{
"id": "hello-ab12c",
"version": 1,
"label": "Hallo-App",
"icon": "waving_hand",
"contentType": "application/json",
"dataKind": "json",
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"text": { "type": "string", "description": "Der angezeigte Text." }
},
"required": ["text"]
}
}
FeldTypPflichtBedeutung
idStringjaDie ID der App und der Wert $app ihrer Dokumente. 2 bis 40 Zeichen, Kleinbuchstaben, Ziffern und Bindestriche, am Anfang ein Buchstabe ([a-z][a-z0-9-]{1,39}). Eindeutig je Mandant. board und markdown sind für die eingebauten Apps reserviert.
labelStringjaDer Name auf der Karte, im Menü Neu der Dateien und auf der Dokumentseite.
contentTypeStringjaWie die Daten beschrieben sind, etwa application/json, text/markdown, text/plain oder application/xml.
dataKind"json" oder "text"jajson: data ist ein JSON-Wert, geprüft gegen schema. text: data ist ein String.
schemaObjektbei json-AppsEin JSON Schema (Draft 2020-12) für data. nexxus kompiliert es beim Hochladen und prüft jedes Dokument dagegen, das der KI-Assistent anlegt oder ändert. Text-Apps bekommen {"type": "string"}.
descriptionStringneinSteht auf der Karte der App und geht an den KI-Assistenten.
iconStringneinEin Material-Icon-Name für die Karte. Standard apps.
colorStringneinDie Farbe der Karte als Hex-Wert. Standard #1565C0.
versionGanzzahlneinDie Version der App, Standard 1. Jeder neue Upload muss eine höhere Zahl tragen.
defaultDatabeliebigneinDer Inhalt eines neuen Dokuments. Ohne das Feld beginnt ein neues Dokument mit {} (json) oder einem leeren String (text), und Ihre App muss das darstellen.
aiInstructionsStringneinHinweise für den KI-Assistenten, wie ein gutes Dokument aussieht, höchstens 8000 Zeichen.
changelogArrayneinWas jede Version geändert hat, zu sehen vor einer Veröffentlichung und in der Versionshistorie.
capabilitiesObjektneinWünsche an den Rahmen um die App: tabs und assistant.
contributesObjektneinMenüeinträge, die die App in die Leiste über sich legt. Höchstens 256.000 Bytes.

app.html darf nicht leer sein und höchstens 2.000.000 Bytes (UTF-8) groß.

Das Schema ist der Vertrag zwischen Ihrer App, nexxus und dem KI-Assistenten. Daraus folgt dreierlei:

  • nexxus lehnt jedes Dokument vom KI-Assistenten ab, das nicht passt. Der KI-Assistent bekommt die Fehlermeldung und kann seine Änderung korrigieren.
  • Der KI-Assistent lernt Ihr Datenmodell aus dem Schema. Geben Sie jeder Eigenschaft eine description. Dort liest der KI-Assistent, was ein Feld bedeutet, welche Einheit es hat und welche Werte sinnvoll sind.
  • Dokumente überleben App-Versionen. Fügen Sie neue Eigenschaften als optional hinzu, damit Dokumente aus einer älteren Version gültig bleiben.

Halten Sie defaultData leer oder knapp. Soll der KI-Assistent in einem neuen Dokument etwas bauen, wird ihm gesagt, unveränderte Startinhalte zu ersetzen. Ein Demo-Board, das nie verschwindet, ist trotzdem die häufigste Klage über Startinhalte. Gehört eine Einführung dazu, zeigen Sie sie im leeren Zustand Ihrer App. Beim Hochladen prüft nexxus defaultData nicht gegen das Schema, das Werkzeug des KI-Assistenten zum Anlegen aber schon: Passt defaultData nicht zum Schema, scheitert er an neuen Dokumenten.

Das Schema sagt, was gültig ist. aiInstructions sagt, was gut ist: Layoutregeln, Größen, Konventionen, die Fehler, die Modelle mit Ihren Daten gern machen. nexxus gibt den Text zusammen mit dem Schema an den KI-Assistenten, sobald jemand an einem Ihrer Dokumente arbeitet, und liefert ihn mit dem Werkzeug, das die Apps auflistet. Auf der Seite der App kann der Besitzer für seinen Mandanten eine Ergänzung hinzufügen; der KI-Assistent bekommt zuerst Ihren Text, dann die Ergänzung.

Ein Eintrag je Version, der neueste zuerst. version ist eine Ganzzahl ab 1, changes eine nicht leere Liste nicht leerer Strings, date optional im Format JJJJ-MM-TT. Weitere Schlüssel ignoriert nexxus. Das sind die beiden neuesten Einträge der Whiteboard-App:

"changelog": [
{
"version": 7,
"date": "2026-10-04",
"changes": [
"Die App bringt einen Changelog mit. Er erscheint beim Hochladen einer neuen Version und in der Versionshistorie.",
"Die Hinweise für den KI-Assistenten erklären jetzt auch Standardgrößen, wie das Board überlappende Elemente nachbessert, Frames als Spalten und typische Fehler wie doppelte ids."
]
},
{
"version": 6,
"date": "2026-10-03",
"changes": [
"Hell und Dunkel folgen dem Modus der Anwendung, in der das Board läuft.",
"Wer in der App selbst umschaltet, behält diese Wahl für das Whiteboard."
]
}
]

Lädt jemand eine neue Version hoch, zeigt der Dialog jeden Eintrag, der neuer ist als die installierte Version, bis zur Version des Pakets. Die Versionshistorie auf der Seite der App nimmt den Eintrag jeder Version aus dem neuesten hochgeladenen Manifest, das einen hat. Ein vollständiger Changelog in einer neuen Version füllt also auch ältere Versionen auf. Schreiben Sie die Punkte für die Menschen, die die App benutzen: eine konkrete Änderung je Punkt.

SchlüsselTypStandardWirkung
tabsBooleanfalsenexxus zeigt über der App eine Reihe von Tabs. Das geöffnete Dokument ist der erste Tab, + öffnet eine Auswahl mit den übrigen Dokumenten derselben App und Leeres Dokument. Ein Tabwechsel lädt die App mit dem anderen Dokument neu.
assistantBooleantruefalse erklärt, dass die Dokumente der App nicht für den KI-Assistenten gedacht sind. Der Schalter Assistent für diese App auf der Seite der App bleibt dann aus und lässt sich erst mit einer Version einschalten, die es erlaubt.

Unbekannte Schlüssel ignoriert nexxus. Was fehlt, ist aus, außer bei assistant.

Eine App kann Befehle in die Leiste über sich legen, entweder in das stehende Menü Datei oder in eigene Menüs. Ein Klick geht als nxd:command mit der ID des Befehls an die App. Das ist der Block des BPMN-Prozess-Editors:

"contributes": {
"commands": [
{ "id": "process.validate", "title": "BPMN prüfen", "category": "Prozess" },
{ "id": "process.export.svg", "title": "Als SVG exportieren", "category": "Datei" }
],
"menus": {
"datei": [ { "command": "process.export.svg", "group": "export" } ],
"menubar": [
{ "id": "prozess", "title": "Prozess", "items": [ { "command": "process.validate" } ] }
]
}
}
  • commands legt Befehle mit id und title fest. Die erste Angabe einer ID gilt. Die IDs __proto__, constructor und prototype lehnt nexxus ab.
  • menus.datei hängt Einträge an das Menü Datei an, gruppiert nach group und sortiert nach order. datei ist das einzige stehende Menü, das eine App erweitern kann.
  • menus.menubar fügt eigene Menüs hinzu, jedes mit id, title, items und optional order. Angezeigt werden höchstens 8 Menüs mit je 24 Einträgen.
  • An einem fehlerhaften Menüeintrag scheitert kein Manifest. nexxus lässt weg, was es nicht auflösen kann (einen unbekannten Befehl, ein nicht erlaubtes Menü), und warnt in der Browser-Konsole. Weitergereicht werden nur Befehls-IDs, die diese Prüfung bestanden haben.

Ein Dokument ist eine JSON-Datei in den Dateien, verpackt in einen kleinen Umschlag:

{
"$app": "board",
"$v": 1,
"contentType": "application/json",
"meta": { "title": "Messe 2026" },
"data": {
"columns": [ { "id": "todo", "title": "To-do" } ],
"cards": []
}
}
FeldTypBedeutung
$appStringDie ID der App, die das Dokument öffnet. Pflicht: Ohne das Feld zeigt nexxus Ungültige .nxd-Datei: Feld “$app” fehlt.
$vZahlVersion des Umschlagformats, derzeit 1.
contentTypeStringAus dem Manifest übernommen.
meta.titleStringDer Titel des Dokuments. Er ist auch Teil des Dateinamens.
dataObjekt oder StringDer Inhalt Ihrer App: was schema bei json-Apps beschreibt, ein String bei Text-Apps.

Der Dateiname lautet <Titel>.<App-ID>.nxd, zum Beispiel Messe 2026.board.nxd. Beim Anlegen liest nexxus die App-ID aus dem Dateinamen und findet darüber die Dokumente einer App. Welche App ein Dokument öffnet, entscheidet $app, nicht der Name. Eine Datei, die kein gültiges JSON ist, zeigt Ungültige .nxd-Datei: kein gültiges JSON.

Ihre App sieht immer nur data (und den Titel in meta). Der Umschlag ist Sache von nexxus.

nexxus lädt app.html in einen iframe mit sandbox="allow-scripts": Ihr Code läuft, aber ohne die Origin von nexxus, also ohne Cookies, ohne Sitzung und ohne Zugriff auf die Seite drumherum. Vor Ihr HTML setzt nexxus diese Content-Security-Policy:

default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:;
font-src data:; media-src data: blob:; form-action 'none'; base-uri 'none'

Was das für Ihren Code bedeutet:

GehtGeht nicht
Eingebettetes <script> und <style>, alles in der einen HTML-DateiJeder Netzwerkzugriff: fetch, WebSocket, externe Skripte, Stylesheets, Webfonts, Bilder von einer URL
Bilder und Schriften als data:- oder blob:-URLlocalStorage, sessionStorage, Cookies (der Rahmen hat keine Origin)
<input type="file">, Einfügen und Drag and Dropalert, confirm, prompt (in der Sandbox gesperrt): Bauen Sie eigene Dialoge
Canvas, SVG, alles, was der Browser zeichnetEine Datei direkt herunterladen: Geben Sie sie mit nxd:export an nexxus

Die Daten des Dokuments sind Ihr einziger Zustand. Bilder, die eine Person hinzufügt, gehören komprimiert in data. Große Mengen Rasterdaten gehören als eigene Dokumente in die Dateien.

App und nexxus sprechen über einen privaten MessageChannel. Der Handshake hat drei Schritte, danach laufen Nachrichten über den Port in beide Richtungen.

Ihre App
nexxus-Seite
nexxus-Server
Mitbearbeiterin
  1. 1nxd:ready (an das Elternfenster)Ihre App → nexxus-Seite
  2. 2nxd:connect mit einem MessagePortnexxus-Seite → Ihre App
  3. 3nxd:init: Daten, Titel, readonlynexxus-Seite → Ihre App
  4. 4Die Person ändert etwasIhre App
  5. 5nxd:change mit den vollständigen DatenIhre App → nexxus-Seite
  6. 6Live an die anderen, gespeichert nach etwa 1,5 snexxus-Seite → nexxus-Server
  7. 7Die Änderung kommt bei der Mitbearbeiterin annexxus-Server → Mitbearbeiterin
  8. 8Die Mitbearbeiterin ändert etwasMitbearbeiterin → nexxus-Server
  9. 9Weitergereicht an diese Seitenexxus-Server → nexxus-Seite
  10. 10nxd:apply mit origin 'peer'nexxus-Seite → Ihre App
Nach dem Handshake geht jede Nachricht über den Port. Ihre App spricht nie selbst mit dem Server.
RichtungNachrichtBedeutung
App an nexxus (Fenster){type: 'nxd:ready'}Die App ist gestartet und möchte ihren Kanal.
nexxus an App (Fenster){type: 'nxd:connect'} mit einem MessagePort in ports[0]Behalten Sie den Port, über ihn läuft alles Weitere.
nexxus an App{type: 'nxd:init', doc, meta, readonly, hostCapabilities}Die data des Dokuments als doc, meta mit dem Titel, readonly und was dieser Host kann, heute {assist: true} oder {assist: false}.
nexxus an App{type: 'nxd:apply', doc, origin}Jemand anderes hat das Dokument geändert. origin ist 'ai' für den KI-Assistenten und 'peer' für Mitbearbeitende. Behandeln Sie ein fehlendes origin wie 'peer'.
App an nexxus{type: 'nxd:change', doc}Die Person hat etwas geändert. Schicken Sie die vollständigen neuen Daten. Bei schreibgeschützten Dokumenten verworfen.
nexxus an App{type: 'nxd:command', id}Die Person hat einen Ihrer Menüeinträge angeklickt.
App an nexxus{type: 'nxd:export', filename, mime, data}Eine Datei für die Person speichern.
App an nexxus{type: 'nxd:assist', context, message}Das Element, auf das die Person zeigt, an den KI-Assistenten geben.
App an nexxus{type: 'nxd:presenting', on}Eine Vollbild-Vorführung beginnt (true) oder endet (false).
nexxus an App{type: 'nxd:theme', mode, pref}Hell oder dunkel: mode ist der Modus von nexxus, pref die Wahl dieser Person für Ihre App.
App an nexxus{type: 'nxd:theme-pref', pref}Die Person hat den Hell-Dunkel-Schalter Ihrer App benutzt.

Das ist die app.html der Beispiel-App. Sie zeigt ein Textfeld, meldet jeden Tastendruck und übernimmt Änderungen von anderen:

<!doctype html>
<html lang="de"><head><meta charset="utf-8"><style>
body{font:15px/1.5 -apple-system,system-ui,sans-serif;margin:0;display:grid;place-items:center;height:100vh}
input{font-size:20px;padding:10px 14px;border:1px solid #cbd5e1;border-radius:10px}
</style></head><body>
<input id="t" placeholder="Text eingeben">
<script>
var port=null, state={text:""};
var t=document.getElementById("t");
function render(){ if(t.value!==(state.text||"")) t.value=state.text||""; }
function emit(){ if(port) port.postMessage({type:"nxd:change", doc: state}); }
t.addEventListener("input", function(){ state.text=t.value; emit(); });
function handle(m){ if(!m) return;
if(m.type==="nxd:init"){ state=m.doc||{text:""}; t.readOnly=!!m.readonly; render(); }
if(m.type==="nxd:apply"){ state=m.doc||{text:""}; render(); } }
window.addEventListener("message", function(e){
if(e.data && e.data.type==="nxd:connect" && e.ports[0]){ port=e.ports[0]; port.onmessage=function(ev){handle(ev.data);}; } });
(window.parent||window).postMessage({type:"nxd:ready"}, "*");
</script>
</body></html>
  • readonly respektieren. Blenden Sie bei true alle Bearbeitungselemente aus: Paletten, Griffe, Löschknöpfe. Eine CSS-Klasse am <body> lässt sich leichter vollständig halten als eine Abfrage je Knopf. Lesen, Suchen, Zoomen und Exportieren bleiben. nexxus erzwingt die Schreibrechte auf dem Server und verwirft nxd:change aus einem schreibgeschützten Dokument. Bei readonly geht es darum, nichts anzubieten, was scheitern würde.
  • Jedes nxd:init als Neuanfang behandeln. Setzen Sie Ihren Zustand daraus neu. Hosts können es mehr als einmal schicken.
  • Bei nxd:apply keine laufende Eingabe überschreiben. Kommt die Änderung einer Mitbearbeiterin, während die Person in einem Feld tippt, behalten Sie deren Text in diesem Feld. render() im Beispiel schreibt das Feld nur, wenn sich der Wert unterscheidet.
  • nxd:change bei jeder abgeschlossenen Änderung schicken, mit den vollständigen Daten. Fassen Sie schnelle Tastenfolgen zusammen.
  • hostCapabilities großzügig lesen. Fehlt ein Eintrag, kann der Host es nicht. Bieten Sie eine Funktion nur an, wenn der Host sie ankündigt.

Die Sandbox kann keine Dateien herunterladen, also gibt Ihre App sie an nexxus:

port.postMessage({ type: 'nxd:export', filename: 'board.svg', mime: 'image/svg+xml', data: svgString });

data darf ein String, ein ArrayBuffer, ein typisiertes Array oder ein Blob sein, übergeben als Bytes und nie als Base64. nexxus schaut nicht auf das Format. Es prüft:

  • Der Export muss auf einen echten Klick in Ihrer App folgen. Ein Export ohne vorherigen Klick wird verworfen.
  • Höchstens 50 MB.
  • Mindestens eine Sekunde zwischen zwei Exporten, höchstens 20 je Minute. Sonst sieht die Person Zu viele Exporte in kurzer Zeit. Bitte einen Moment warten.
  • mime muss die Form typ/untertyp haben. Den Dateinamen kürzt nexxus auf einen einfachen Namen mit höchstens 200 Zeichen (keine Pfade, keine Steuerzeichen, kein Punkt am Anfang). Eine ungültige Nachricht zeigt Export fehlgeschlagen: Die App hat ungültige Daten geliefert.
  • Auf einem öffentlichen Link, der Downloads verbietet, sieht die Person Für diesen Link sind Downloads nicht erlaubt.

Den KI-Assistenten zu einem Element fragen: nxd:assist

Abschnitt betitelt „Den KI-Assistenten zu einem Element fragen: nxd:assist“

Ihre App erreicht kein Modell. Sie kann aber sagen, worauf die Person zeigt:

port.postMessage({
type: 'nxd:assist',
context: {
focus: { kind: 'block', id: 'b-7', label: 'Tabelle',
path: 'Abschnitt „Liste“ › Tabelle', data: node },
summary: 'Auftragstabelle mit 4 Spalten',
excerpt: '<table class="orders">...</table>'
},
message: 'Diese Tabelle ist zu breit.'
});

nexxus zeigt das Element als Fokus des Gesprächs, öffnet den KI-Assistenten und legt message ins Eingabefeld, ohne es abzuschicken: Modell und Formulierung wählt weiterhin die Person. Der Fokus reist mit jeder folgenden Nachricht mit, bis die Person weiterzieht. kind legen Sie fest (block, slide, entity). Schicken Sie in excerpt Markup, statt ein Bild zu beschreiben: nexxus kann Ihren Rahmen nicht abfotografieren, und Markup sagt einem Modell mehr.

Alles, was die Sandbox-Grenze überquert, gilt als ungeprüft und wird gekürzt: label auf 120 Zeichen, kind auf 40, path und summary auf 300, excerpt auf 2000 und data auf 4000 Zeichen JSON.

Bieten Sie die Übergabe nur an, wenn nxd:init hostCapabilities.assist === true mitgebracht hat. Der Wert ist false, wo kein KI-Assistent bereitsteht, etwa wenn er für Ihre App abgeschaltet ist. Zeigen Sie dann den Kontext zum Kopieren an, statt eines Knopfs, der nichts tut.

Die Bedienelemente Ihrer App (Leisten, Seitenpanels, Dialoge) sollten nexxus folgen, bis die Person in Ihrer App selbst umschaltet. Der Inhalt behält sein eigenes Aussehen. nexxus schickt nxd:theme nach jedem nxd:init und immer, wenn sich einer der beiden Werte ändert:

const look = { host: 'light', pref: 'auto' };
const dark = () => (look.pref === 'auto' ? look.host : look.pref) === 'dark';
// bei nxd:theme: look.host = m.mode; look.pref = m.pref; apply()
// eigener Schalter: const next = dark() ? 'light' : 'dark';
// look.pref = next === look.host ? 'auto' : next;
// port.postMessage({ type: 'nxd:theme-pref', pref: look.pref }); apply()

nexxus bewahrt die Wahl je Person und App in den Einstellungen der Person auf und schickt sie mit dem nächsten nxd:theme zurück. Speichern Sie sie nie in data: Sie gehört der Person, nicht dem Dokument. Zurück zum Modus von nexxus heißt pref: 'auto'.

Schicken Sie {type: 'nxd:presenting', on: true}, wenn Ihre App eine Vollbild-Vorführung beginnt, und on: false, wenn sie endet. nexxus blendet solange die Zeiger der anderen aus und meldet den Zeiger der vorführenden Person nicht weiter. Die Live-Zeiger zeichnet nexxus selbst über Ihre App; dafür müssen Sie sonst nichts tun.

Öffnet jemand den KI-Assistenten an einem Ihrer Dokumente, sagt nexxus ihm einmal zu Beginn des Gesprächs:

  • dass es ein App-Dokument ist, welche App es ist und dass die aktuellen Daten mit jeder Nachricht mitkommen;
  • Ihr Schema (direkt, wenn es höchstens 20.000 Zeichen hat, sonst holt der KI-Assistent es mit dem Werkzeug, das die Apps auflistet);
  • Ihre aiInstructions, gefolgt von der Ergänzung des Mandanten;
  • dass er das Dokument mit nx_dmsapp_update ändern soll, statt die Änderung zu beschreiben, unveränderte Startinhalte ersetzen soll und nie Nebendateien anlegen darf.

Mit jeder Nachricht bekommt der KI-Assistent die Daten so, wie die Person sie gerade sieht, samt ungespeicherten Änderungen und denen der Mitbearbeitenden. Über 100.000 Zeichen werden die Daten gekürzt, und der KI-Assistent soll statt der vollständigen Daten einen JSON Patch (RFC 6902) mit nur den geänderten Pfaden schicken:

[
{ "op": "replace", "path": "/slides/0/boxes/1/text", "value": "Neu" }
]

Auf dem Server wendet nexxus die Änderung an, prüft das Ergebnis gegen Ihr Schema, prüft das Bearbeitungsrecht der Person, speichert eine neue Version und schickt die Daten mit nxd:apply und origin: 'ai' an jede offene Ansicht des Dokuments.

Ihre App darf nachbessern, was ein Modell nicht sehen kann, etwa Text, der aus einem Kasten läuft, oder neue Elemente, die alte überdecken. Tun Sie das nur bei origin: 'ai' und schicken Sie das Ergebnis als eigenes nxd:change. Die Änderung einer Mitbearbeiterin ('peer') wurde schon so gezeichnet, wie sie es wollte: Lassen Sie sie, wie sie ist.

  • Jedes nxd:change wird etwa anderthalb Sekunden später gespeichert und sofort an alle anderen geschickt, die das Dokument offen haben. Dort kommt es als nxd:apply mit origin: 'peer' an. Jede Nachricht trägt die vollständigen Daten; eine Nachricht darf höchstens 4 MB groß sein.
  • Wer das Dokument öffnet, während andere daran arbeiten, bekommt zuerst deren aktuellen Stand.
  • Jedes Speichern nennt den Stand, auf dem es beruht. Hat dazwischen jemand anderes gespeichert, holt nexxus dessen Stand, führt ihn mit den Änderungen dieser Person zusammen und speichert erneut. Das Zusammenführen arbeitet auf Ihrem JSON: Objekte Schlüssel für Schlüssel, Arrays, deren Einträge alle eine eindeutige id tragen, Eintrag für Eintrag. Legen zwei Personen je eine Haftnotiz an, bleiben beide. Wo beide denselben Wert unterschiedlich geändert haben, gilt der Wert dieser Person, und der andere Stand wird als eigene Version gesichert. Steht eine Löschung auf der einen Seite gegen eine Änderung auf der anderen, bleibt der geänderte Eintrag.
  • Eine Version für den Verlauf entsteht, wenn jemand das Dokument verlässt, oder nach zehn ruhigen Minuten.

Für Ihre App heißt das: Rechnen Sie jederzeit mit nxd:apply, auch mitten in einem Ziehvorgang oder während die Person tippt, und geben Sie jedem Eintrag einer Liste eine stabile, eindeutige id. Listen ohne IDs lassen sich nur als Ganzes zusammenführen.

Das ganze Protokoll lässt sich ohne nexxus testen. Diese Seite spielt den Host: Sie setzt dieselbe Content-Security-Policy vor Ihre App, führt den Handshake aus, schickt nxd:init und protokolliert alles, was die App zurückschickt.

  • Ordnermeine-app
    • manifest.json
    • app.html Ihre App
    • host.html der Test-Host unten
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>DMS-App test host</title>
<iframe id="app" sandbox="allow-scripts" style="width:100%;height:60vh;border:1px solid #ccc"></iframe>
<pre id="log"></pre>
<script>
// The same policy nexxus puts in front of every app: no network at all.
const CSP = "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; " +
"font-src data:; media-src data: blob:; form-action 'none'; base-uri 'none'";
const seal = (html) => {
const meta = `<meta http-equiv="Content-Security-Policy" content="${CSP}">`;
const doctype = /^\s*(?:<!--[\s\S]*?-->\s*)*<!doctype[^>]*>/i.exec(html);
return doctype ? doctype[0] + meta + html.slice(doctype[0].length) : meta + html;
};
const frame = document.getElementById('app');
const log = (text) => { document.getElementById('log').textContent += text + '\n'; };
let port = null;
window.addEventListener('message', (e) => {
if (e.source !== frame.contentWindow || e.data?.type !== 'nxd:ready') return;
const channel = new MessageChannel();
port = channel.port1;
port.onmessage = (ev) => log('from app: ' + JSON.stringify(ev.data));
frame.contentWindow.postMessage({type: 'nxd:connect'}, '*', [channel.port2]);
port.postMessage({type: 'nxd:init', doc: {text: 'Hallo'}, meta: {title: 'Test'}, readonly: false, hostCapabilities: {assist: false}});
});
fetch('app.html').then((r) => r.text()).then((html) => { frame.srcdoc = seal(html); });
</script>
</html>
  1. Stellen Sie den Ordner über einen beliebigen statischen Webserver bereit, zum Beispiel python3 -m http.server 8000, und öffnen Sie http://localhost:8000/host.html. Direkt von der Festplatte geöffnet geht es nicht, weil die Seite app.html mit fetch lädt.

  2. Ändern Sie etwas in der App. Jede Änderung erscheint im Protokoll als from app: {"type":"nxd:change",...}.

  3. Spielen Sie in der Browser-Konsole eine Mitbearbeiterin oder den KI-Assistenten:

    port.postMessage({type: 'nxd:apply', doc: {text: 'Von außen'}, origin: 'ai'})
  4. Setzen Sie in host.html readonly: false auf true, laden Sie neu und prüfen Sie, dass alle Bearbeitungselemente verschwunden sind.

  5. Öffnen Sie die Netzwerkansicht des Browsers. Sie muss leer bleiben, solange die App läuft: Was die App zu laden versucht, wird hier genauso gesperrt wie in nexxus.

Danach probieren Sie die App in nexxus aus. Sind Sie kein Administrator, wird Ihr Import zu Ihrer eigenen App, die nur Sie und die Administratoren sehen. Das ist ein sicherer Ort zum Testen. Prüfen Sie, dass eine zweite Person an einem freigegebenen Dokument Ihre Änderungen live sieht, dass jemand mit Leserecht eine schreibgeschützte App bekommt und dass der KI-Assistent aus Ihrem Schema ein Dokument bauen kann.

Ihre App ist fertig, wenn sie mit dem Handshake startet, bei jeder Änderung nxd:change schickt, ein nxd:apply mitten in einer Änderung übersteht, im Nur-Lesen-Modus ihre Bearbeitungselemente ausblendet und nichts aus dem Netz lädt.

  1. Erhöhen Sie version in manifest.json um eins für jedes Paket, das Sie herausgeben. nexxus lehnt ein Paket ab, dessen Version nicht höher ist als die installierte, und an der Nummer erkennen Sie, welchen Stand ein Mandant hat.

  2. Ergänzen Sie einen changelog-Eintrag für die neue Version, geschrieben für die Menschen, die die App benutzen.

  3. Bauen Sie die .nxa: zip -j meine-app.nxa manifest.json app.html.

  4. Erste Installation: App importieren unter StartApps. Jede spätere Version: Neue Version hochladen auf der Seite der App. nexxus prüft das Paket und zeigt Was sich ändert, bevor jemand bestätigt. Siehe Apps ohne Code erstellen.

Neue Versionen lassen jedes vorhandene Dokument unangetastet. Halten Sie neue Schema-Eigenschaften optional, und wenn sich die Bedeutung vorhandener Daten ändern muss, lassen Sie Ihre App auch die alte Form lesen.

Für automatische Veröffentlichungen gibt es dieselben Schritte als HTTP-Endpunkte für einen angemeldeten Benutzer. Ein Paket, das die Prüfung nicht besteht, beantwortet nexxus mit Status 400 und dem Grund als Meldung.

Methode und PfadWas es tut
POST /api/dms-app/importInstalliert ein Paket (Multipart-Feld file).
POST /api/dms-app/{appId}/version/previewPrüft ein Paket als nächste Version und liefert valid, error, installedVersion, packageVersion und die changelog-Einträge, die es mitbringt. Speichert nichts.
POST /api/dms-app/{appId}/versionVeröffentlicht ein Paket als nächste Version (Multipart-Feld file).
GET /api/dms-app/Listet die Apps, die der Benutzer sehen darf, ohne ihr HTML.
GET /api/dms-app/{appId}Eine App mit HTML, Schema und den wirksamen KI-Anweisungen.
GET /api/dms-app/{appId}/detailDie App mit Dokumentanzahl, Versionshistorie und Einstellungen.
PATCH /api/dms-app/{appId}/settingsSetzt aiInstructionsAddendum (String mit höchstens 8000 Zeichen oder null) und assistantDisabled (Boolean).
DELETE /api/dms-app/{appId}Entfernt die App. Ihre Dokumente bleiben.

Wer was darf, ist dasselbe wie in der Oberfläche: Jeder Angemeldete darf importieren, Administratoren installieren für den Mandanten, und nur der Besitzer der App und Administratoren dürfen Versionen veröffentlichen, Einstellungen ändern oder eine App entfernen.

WasGrenze
app.html2.000.000 Bytes
contributes256.000 Bytes, 8 Menüs, 24 Einträge je Menü
aiInstructions, Ergänzungje 8000 Zeichen
App-ID[a-z][a-z0-9-]{1,39}, nicht board oder markdown
Eine Nachricht beim gemeinsamen Bearbeiten4 MB
Daten, die der KI-Assistent vollständig sieht100.000 Zeichen, darüber ändert der KI-Assistent per JSON Patch
Schema, das der KI-Assistent direkt bekommt20.000 Zeichen, darüber holt der KI-Assistent es selbst
Export50 MB, 1 je Sekunde, 20 je Minute, nur nach einem Klick in der App