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.
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
Das Paket (.nxa)
Abschnitt betitelt „Das Paket (.nxa)“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:
zip -j hello.nxa manifest.json app.htmlDie 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.

Das Manifest
Abschnitt betitelt „Das Manifest“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"] }}| Feld | Typ | Pflicht | Bedeutung |
|---|---|---|---|
id | String | ja | Die 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. |
label | String | ja | Der Name auf der Karte, im Menü Neu der Dateien und auf der Dokumentseite. |
contentType | String | ja | Wie die Daten beschrieben sind, etwa application/json, text/markdown, text/plain oder application/xml. |
dataKind | "json" oder "text" | ja | json: data ist ein JSON-Wert, geprüft gegen schema. text: data ist ein String. |
schema | Objekt | bei json-Apps | Ein 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"}. |
description | String | nein | Steht auf der Karte der App und geht an den KI-Assistenten. |
icon | String | nein | Ein Material-Icon-Name für die Karte. Standard apps. |
color | String | nein | Die Farbe der Karte als Hex-Wert. Standard #1565C0. |
version | Ganzzahl | nein | Die Version der App, Standard 1. Jeder neue Upload muss eine höhere Zahl tragen. |
defaultData | beliebig | nein | Der Inhalt eines neuen Dokuments. Ohne das Feld beginnt ein neues Dokument mit {} (json) oder einem leeren String (text), und Ihre App muss das darstellen. |
aiInstructions | String | nein | Hinweise für den KI-Assistenten, wie ein gutes Dokument aussieht, höchstens 8000 Zeichen. |
changelog | Array | nein | Was jede Version geändert hat, zu sehen vor einer Veröffentlichung und in der Versionshistorie. |
capabilities | Objekt | nein | Wünsche an den Rahmen um die App: tabs und assistant. |
contributes | Objekt | nein | Menü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 Datenschema
Abschnitt betitelt „Das Datenschema“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.
aiInstructions
Abschnitt betitelt „aiInstructions“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.
changelog
Abschnitt betitelt „changelog“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.
capabilities
Abschnitt betitelt „capabilities“| Schlüssel | Typ | Standard | Wirkung |
|---|---|---|---|
tabs | Boolean | false | nexxus 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. |
assistant | Boolean | true | false 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.
contributes
Abschnitt betitelt „contributes“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" } ] } ] }}commandslegt Befehle mitidundtitlefest. Die erste Angabe einer ID gilt. Die IDs__proto__,constructorundprototypelehnt nexxus ab.menus.dateihängt Einträge an das Menü Datei an, gruppiert nachgroupund sortiert nachorder.dateiist das einzige stehende Menü, das eine App erweitern kann.menus.menubarfügt eigene Menüs hinzu, jedes mitid,title,itemsund optionalorder. 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.
Das Dokument (.nxd)
Abschnitt betitelt „Das Dokument (.nxd)“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": [] }}| Feld | Typ | Bedeutung |
|---|---|---|
$app | String | Die ID der App, die das Dokument öffnet. Pflicht: Ohne das Feld zeigt nexxus Ungültige .nxd-Datei: Feld “$app” fehlt. |
$v | Zahl | Version des Umschlagformats, derzeit 1. |
contentType | String | Aus dem Manifest übernommen. |
meta.title | String | Der Titel des Dokuments. Er ist auch Teil des Dateinamens. |
data | Objekt oder String | Der 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.
In der Sandbox
Abschnitt betitelt „In der Sandbox“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:
| Geht | Geht nicht |
|---|---|
Eingebettetes <script> und <style>, alles in der einen HTML-Datei | Jeder Netzwerkzugriff: fetch, WebSocket, externe Skripte, Stylesheets, Webfonts, Bilder von einer URL |
Bilder und Schriften als data:- oder blob:-URL | localStorage, sessionStorage, Cookies (der Rahmen hat keine Origin) |
<input type="file">, Einfügen und Drag and Drop | alert, confirm, prompt (in der Sandbox gesperrt): Bauen Sie eigene Dialoge |
| Canvas, SVG, alles, was der Browser zeichnet | Eine 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.
Das Protokoll
Abschnitt betitelt „Das Protokoll“App und nexxus sprechen über einen privaten MessageChannel. Der Handshake hat drei Schritte, danach laufen Nachrichten über den Port in beide Richtungen.
- 1nxd:ready (an das Elternfenster)Ihre App → nexxus-Seite
- 2nxd:connect mit einem MessagePortnexxus-Seite → Ihre App
- 3nxd:init: Daten, Titel, readonlynexxus-Seite → Ihre App
- 4Die Person ändert etwasIhre App
- 5nxd:change mit den vollständigen DatenIhre App → nexxus-Seite
- 6Live an die anderen, gespeichert nach etwa 1,5 snexxus-Seite → nexxus-Server
- 7Die Änderung kommt bei der Mitbearbeiterin annexxus-Server → Mitbearbeiterin
- 8Die Mitbearbeiterin ändert etwasMitbearbeiterin → nexxus-Server
- 9Weitergereicht an diese Seitenexxus-Server → nexxus-Seite
- 10nxd:apply mit origin 'peer'nexxus-Seite → Ihre App
Nachrichten
Abschnitt betitelt „Nachrichten“| Richtung | Nachricht | Bedeutung |
|---|---|---|
| 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. |
Eine vollständige Minimal-App
Abschnitt betitelt „Eine vollständige Minimal-App“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>Regeln, an die sich jede App hält
Abschnitt betitelt „Regeln, an die sich jede App hält“readonlyrespektieren. Blenden Sie beitruealle 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 verwirftnxd:changeaus einem schreibgeschützten Dokument. Beireadonlygeht es darum, nichts anzubieten, was scheitern würde.- Jedes
nxd:initals Neuanfang behandeln. Setzen Sie Ihren Zustand daraus neu. Hosts können es mehr als einmal schicken. - Bei
nxd:applykeine 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:changebei jeder abgeschlossenen Änderung schicken, mit den vollständigen Daten. Fassen Sie schnelle Tastenfolgen zusammen.hostCapabilitiesgroßzügig lesen. Fehlt ein Eintrag, kann der Host es nicht. Bieten Sie eine Funktion nur an, wenn der Host sie ankündigt.
Dateien speichern: nxd:export
Abschnitt betitelt „Dateien speichern: nxd:export“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.
mimemuss die Formtyp/untertyphaben. 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.
Hell und dunkel: nxd:theme
Abschnitt betitelt „Hell und dunkel: nxd:theme“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'.
Vorführen: nxd:presenting
Abschnitt betitelt „Vorführen: nxd:presenting“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.
Wie der KI-Assistent Ihre Dokumente ändert
Abschnitt betitelt „Wie der KI-Assistent Ihre Dokumente ändert“Ö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.
Wie gemeinsames Bearbeiten funktioniert
Abschnitt betitelt „Wie gemeinsames Bearbeiten funktioniert“- Jedes
nxd:changewird etwa anderthalb Sekunden später gespeichert und sofort an alle anderen geschickt, die das Dokument offen haben. Dort kommt es alsnxd:applymitorigin: '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
idtragen, 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.
Die App testen
Abschnitt betitelt „Die App testen“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>-
Stellen Sie den Ordner über einen beliebigen statischen Webserver bereit, zum Beispiel
python3 -m http.server 8000, und öffnen Siehttp://localhost:8000/host.html. Direkt von der Festplatte geöffnet geht es nicht, weil die Seiteapp.htmlmitfetchlädt. -
Ändern Sie etwas in der App. Jede Änderung erscheint im Protokoll als
from app: {"type":"nxd:change",...}. -
Spielen Sie in der Browser-Konsole eine Mitbearbeiterin oder den KI-Assistenten:
port.postMessage({type: 'nxd:apply', doc: {text: 'Von außen'}, origin: 'ai'}) -
Setzen Sie in
host.htmlreadonly: falseauftrue, laden Sie neu und prüfen Sie, dass alle Bearbeitungselemente verschwunden sind. -
Ö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.
Veröffentlichen
Abschnitt betitelt „Veröffentlichen“-
Erhöhen Sie
versioninmanifest.jsonum 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. -
Ergänzen Sie einen
changelog-Eintrag für die neue Version, geschrieben für die Menschen, die die App benutzen. -
Bauen Sie die
.nxa:zip -j meine-app.nxa manifest.json app.html. -
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.
Hochladen per API
Abschnitt betitelt „Hochladen per API“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 Pfad | Was es tut |
|---|---|
POST /api/dms-app/import | Installiert ein Paket (Multipart-Feld file). |
POST /api/dms-app/{appId}/version/preview | Prü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}/version | Verö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}/detail | Die App mit Dokumentanzahl, Versionshistorie und Einstellungen. |
PATCH /api/dms-app/{appId}/settings | Setzt 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.
Grenzen
Abschnitt betitelt „Grenzen“| Was | Grenze |
|---|---|
app.html | 2.000.000 Bytes |
contributes | 256.000 Bytes, 8 Menüs, 24 Einträge je Menü |
aiInstructions, Ergänzung | je 8000 Zeichen |
| App-ID | [a-z][a-z0-9-]{1,39}, nicht board oder markdown |
| Eine Nachricht beim gemeinsamen Bearbeiten | 4 MB |
| Daten, die der KI-Assistent vollständig sieht | 100.000 Zeichen, darüber ändert der KI-Assistent per JSON Patch |
| Schema, das der KI-Assistent direkt bekommt | 20.000 Zeichen, darüber holt der KI-Assistent es selbst |
| Export | 50 MB, 1 je Sekunde, 20 je Minute, nur nach einem Klick in der App |