Gemeinsame Bearbeitung
getSharedStore() macht einen Editor zu einem Knoten in einer gemeinsamen Sitzung.
Der Editor gibt dann jede Änderung als Stream von Aktionen aus und wendet die Aktionen an, die andere Editoren zurücksenden – den Transport zwischen ihnen (WebSocket, WebRTC oder alles andere, was JSON übertragen kann) stellen Sie selbst bereit.
getSharedStore() ist eine der Methoden von ErdEditorElement und lässt sich mit Remote-Speicherung kombinieren, wenn die weitergeleiteten Änderungen auch gespeichert werden müssen.
interface ErdEditorElement extends HTMLElement {
// ...
getSharedStore: (
config?: SharedStoreConfig & {
mouseTracker?: boolean;
focusTracker?: boolean;
}
) => SharedStore;
}
type SharedStoreConfig = {
getNickname?: () => string;
};
type SharedStore = {
connection: () => void;
disconnect: () => void;
dispatch: (actions: Array<AnyAction> | AnyAction) => void;
dispatchSync: (actions: Array<AnyAction> | AnyAction) => void;
subscribe: (fn: (value: AnyAction[]) => void) => Unsubscribe;
flushStreamBuffers: () => void;
destroy: () => void;
};
// example
const sharedStore = editor.getSharedStore();
Eine Aktion ist ein einfaches serialisierbares Objekt, das eine Änderung am Dokument beschreibt, sodass sie JSON.stringify auf dem Weg zum Transport übersteht.
Aktionen kommen in Stapeln an: subscribe übergibt Ihnen ein Array, und dieses Array ist eine Einheit – leiten Sie es als Ganzes weiter und übergeben Sie es auf der anderen Seite als Ganzes an dispatch.
Behandeln Sie eine Aktion als Blackbox und reichen Sie sie unverändert durch, statt sie zu lesen oder umzuschreiben, denn die Verwaltungsdaten, die sie mitführt, halten die Editoren synchron.
Einiges von dem, was ein Editor tut, bleibt in diesem Editor.
Wenn eine Tabelle, Notiz oder Tabellengruppe in den Vordergrund geholt wird, wie es beim Auswählen geschieht, wird das nicht gesendet.
Auch die Auswahl bei Statements, Header und Tables auf der Registerkarte Schema SQL wird nicht gesendet, denn sie gehört zu dem Fenster, in dem sie getroffen wird.
Seit 4.1.0 bleibt auch der Explorer in seinem Editor: Ob er geöffnet ist, seine Breite, seine eingeklappten Zeilen, die Zeile, auf der der Tastaturfokus liegt, wohin sein Baum gescrollt ist sowie seine Suche und Optionen werden nicht gesendet.
Auch die Hervorhebung, die beim Wechseln zu einem Fremdschlüssel auf der Verbindungslinie seiner Beziehung erscheint, wird nicht gesendet.
Scrollen, Zoomen oder ein Wechsel der Registerkarte wird gesendet, verschiebt aber nie die Ansicht eines anderen Benutzers und wechselt nie dessen Registerkarte. Das gilt auch für das Scrollen bei einem Sprung aus dem Explorer und für den Wechsel zur Registerkarte ERD, wenn der Explorer von einer anderen Registerkarte aus geöffnet wird.
Sperren werden gemeinsam genutzt: Das letzte Sperren oder Entsperren einer Einstellung setzt sich in jedem Editor durch, und eine Sperre trägt die Werte des Editors mit, der sie eingeschaltet hat, sodass jeder Editor und jedes Replikat denselben Wert speichert.
Eine Sperre Viewport speichert daher die Ansicht des Editors, der sie eingeschaltet hat, und eine Sperre Canvas Type die letzte Registerkarte, auf der dieser Editor vor der Registerkarte Einstellungen war, auf der Sperren eingeschaltet werden.
Das Ausschalten der Sperre Language, Table Name Case, Column Name Case oder Bracket Type setzt diese Einstellung außerdem in jedem Editor auf den Wert, den der Editor anzeigt, der sie ausgeschaltet hat, sodass alle weiterhin einen einzigen Wert speichern. Das Ausschalten von Viewport oder Canvas Type verändert bei niemandem die Ansicht oder die Registerkarte.
Eine Änderung am Skript Before tables oder After tables wird als Ganzes gesendet, wenn sein Feld den Fokus verliert. Die beiden Skripte werden getrennt abgeglichen, sodass die Änderung eines Mitbearbeiters am einen nie das andere überschreibt.
Nichts, was im Flow-Modus der Registerkarte Visualisierung getan wird, ist eine Änderung am Dokument, deshalb sendet ein Editor in Flow den anderen keine Änderungen, und die Änderungen, die sie senden, werden weiter angewendet, solange er dort ist.
Die einzige Ausnahme ist der Wechsel von Flow zu einer anderen Registerkarte: Dieser Wechsel der Registerkarte wird wie jeder andere gesendet.
Die Kartenschaltfläche für den externen Link, ein nach #, @ oder : gewähltes Ergebnis der Schnellsuche und das Öffnen des Explorers wechseln alle von Flow zur Registerkarte ERD. Ein Scrollen, das die Kartenschaltfläche oder ein Ergebnis der Schnellsuche dort auslöst, wird ebenfalls gesendet.
Zwei Editoren auf einer Seite
Die Verdrahtung von subscribe zu dispatch steht hier für das Netzwerk, sodass Sie den Aufbau einer Sitzung sehen können, ohne dass ein Transport dazwischensteht.
const editor1 = document.createElement('erd-editor');
const editor2 = document.createElement('erd-editor');
const sharedStore1 = editor1.getSharedStore({
getNickname: () => 'editor1',
});
const sharedStore2 = editor2.getSharedStore({
getNickname: () => 'editor2',
});
sharedStore1.subscribe(actions => {
sharedStore2.dispatch(actions);
});
sharedStore2.subscribe(actions => {
sharedStore1.dispatch(actions);
});
Beide Editoren beginnen hier mit demselben neuen, leeren Dokument, sodass keiner von beiden initialisiert werden muss.
Bei drei oder mehr Editoren senden Sie die Stapel jedes Editors an alle anderen: Ein Editor gibt nie weiter, was er empfangen hat.
Einen Mitbearbeiter initialisieren
Ein gemeinsamer Store leitet Änderungen weiter, sobald sie vorgenommen werden, und übergibt einem beitretenden Editor nie das Dokument in seinem aktuellen Zustand, sodass ein Editor, der einer bereits laufenden Sitzung beitritt, zuerst das Dokument erhalten muss.
Seit 4.0.0 initialisieren Sie ihn mit runtimeValue eines Editors, der sich bereits in der Sitzung befindet, nicht mit value oder der gespeicherten Datei.
editor2.setInitialValue(editor1.runtimeValue);
const sharedStore2 = editor2.getSharedStore();
Der Laufzeitwert enthält noch, was seit dem Öffnen des Dokuments entfernt wurde, sodass der beitretende Editor, wenn ein anderer Editor später ein Entfernen rückgängig macht, die Tabelle mit ihrem Namen und ihren Spalten zurückholt.
value ist die Form, die eine Datei enthält, ohne entfernte Entitäten, sodass ein beitretender Editor, der daraus oder aus der Datei initialisiert wurde, stattdessen eine leere, namenlose Tabelle zurückholen würde.
Schreiben Sie weiterhin value in den Speicher: runtimeValue dient nur dazu, einen anderen Editor, eine zweite Ansicht oder einen replicationStore zu initialisieren.
Die Editoren in VS Code, Obsidian, einer JetBrains-IDE und der Web-App initialisieren auf diese Weise eine zweite Ansicht eines geöffneten Dokuments.
setInitialValue wird nicht weitergeleitet, sodass das Initialisieren des beitretenden Editors bei den anderen nichts ändert.
Seit 4.0.0 verwirft die Methode außerdem die Kollaborationsregister des Editors, die gleichzeitige Änderungen abgleichen. Hat der gemeinsame Store des Editors bereits einen Abonnenten gehabt, fragt der Store die anderen Editoren erneut nach deren Registern, wie bei seinem ersten subscribe, sodass ein mitten in einer Sitzung erneut geladener Editor Änderungen so abgleicht wie die anderen.
Eine Zuweisung an value behält die Register bei, wird aber anders als setInitialValue weitergeleitet: Sobald der Editor einen gemeinsamen Store hat, lädt jeder Editor in der Sitzung das zugewiesene Dokument. Initialisieren Sie einen beitretenden Editor daher nie auf diese Weise.
Gemischte Versionen
Ein Editor wendet nur die Aktionstypen an, die seine eigene Version kennt, sodass in einer Sitzung mit gemischten Versionen den älteren Editoren alles entgeht, was über einen neueren Typ übertragen wird.
Seit 3.10.0 wird eine Änderung an ON DELETE oder ON UPDATE einer Beziehung über einen von zwei neuen Aktionstypen übertragen, relationship.changeOnDelete und relationship.changeOnUpdate, und relationship.add kann beide mitführen. Eine Weiterleitung, die Aktionen unverändert durchreicht, braucht dafür nichts Neues.
Ein Editor einer Version vor 3.10.0 ignoriert diese Aktionstypen und Felder: Er erfährt nie ON DELETE oder ON UPDATE einer Beziehung, und ein mit ihm gespeichertes Dokument enthält keines von beiden. Eine Beziehung, die ein älterer Editor hinzufügt, kommt ohne sie an, sodass beide mit Not set beginnen.
Jede andere Änderung, die ein Editor der Version 3.10.0 sendet, wird über Aktionstypen übertragen, die ältere Editoren bereits kennen.
Replace All in Suchen und Ersetzen sendet die Änderungen als einen einzigen Stapel derselben Umbenennungs-, Kommentar- und Notizaktionen, die auch eine Änderung von Hand sendet. Die Ansichtsoptionen Alternate Key und Referential Actions werden über die bestehende Aktion settings.changeShow übertragen.
Seit 4.0.0 werden diese Änderungen über neue Aktionstypen übertragen, und eine Weiterleitung, die Aktionen unverändert durchreicht, braucht dafür nichts Neues:
- eine Tabellengruppe, die hinzugefügt, verschoben, in der Größe geändert, umbenannt, neu eingefärbt oder entfernt wird:
tableGroup.add,tableGroup.move,tableGroup.moveTo,tableGroup.resize,tableGroup.changeName,tableGroup.changeColorundtableGroup.remove; eine Tabelle, die einer Gruppe beitritt oder sie verlässt:table.changeGroup; - das Speichern mit
Saveim Dialog Spalten zuordnen für eine bestehende Beziehung, das beide ihrer Spaltenlisten auf einmal setzt:relationship.changeColumns; - eine Sperre, die ein- oder ausgeschaltet wird:
settings.changeLockSettings; - eine Änderung am Skript
Before tablesoderAfter tablesauf der Registerkarte Schema SQL:settings.changeDDLScript.
Die Ansichtsoptionen Table Groups und Memos werden über die bestehende Aktion settings.changeShow übertragen.
Die Aktion settings.changeIgnoreSaveSettings, die die Schalter Save Scroll Information und Save Zoom Information sendeten, bevor die Sperren sie ersetzten, wird nicht mehr gesendet, und settings.changeRelationshipOptimization gibt es nicht mehr. Ein Editor der Version 4.0.0, der eine der beiden empfängt, ändert nichts.
Ein Editor einer Version vor 4.0.0 ignoriert die neuen Aktionstypen: Er sieht nie eine Tabellengruppe oder welche Tabellen zu ihr gehören, obwohl das Ziehen einer Gruppe die Tabellen der Gruppe dort trotzdem verschiebt, da es auch für sie table.move sendet.
Er behält nach dem Speichern mit Save im Dialog „Spalten zuordnen“ die alten Spalten einer Beziehung, und wenn in ihm eine Spalte dieser alten Zuordnung entfernt wird, verschwindet die Beziehung in jedem Editor. Er hat keine Sperren und keine Skripte.
Ein mit einem solchen Editor gespeichertes Dokument enthält nichts davon, und ein Editor der Version 4.0.0 öffnet es mit allen Sperren im eingeschalteten Zustand.
Halten Sie jeden Editor einer Sitzung und jeden replicationStore, der ihr Dokument speichert, auf 4.0.0 oder neuer.
Ein Editor der Version 4.1.0 sendet keinen neuen Aktionstyp, sodass Editoren und Replikate der Versionen 4.0.0 und 4.1.0 eine Sitzung teilen, ohne dass etwas verloren geht.
In ihm durchsucht Suchen und Ersetzen auch Indexnamen und Tabellengruppennamen, und Replace und Replace All benennen sie über die Aktionen index.changeName und tableGroup.changeName um, die ein Editor der Version 4.0.0 bereits anwendet.
SharedStoreConfig
getNickname
Legt den Nickname fest, der in den anderen Editoren neben dem Mauszeiger dieses Benutzers angezeigt wird.
Andere Benutzer sehen user, wenn der Nickname fehlt oder leer ist.
editor.getSharedStore({
getNickname: () => 'nickname...',
});
mouseTracker
Überträgt den Mauszeiger dieses Benutzers an die anderen Editoren. Der Standardwert ist true.
Von anderen Benutzern gesendete Mauszeiger werden auf der ERD-Zeichenfläche immer angezeigt.
editor.getSharedStore({ mouseTracker: false });
focusTracker
Überträgt, woran dieser Benutzer gerade arbeitet. Der Standardwert ist true.
Die Tabelle und die Zelle, die Sie fokussiert haben, die Tabellen, Notizen und Tabellengruppen, die Sie ausgewählt haben, und der Auswahlrahmen, den Sie auf der Zeichenfläche aufziehen, werden an die anderen Editoren gesendet und dort gezeichnet.
Jeder Benutzer erhält eine eigene Farbe, sodass Sie die Benutzer unterscheiden können: ein Umriss um die fokussierte Tabelle, eine Unterstreichung der fokussierten Zelle, ein Ring um die ausgewählten Tabellen, Notizen und Tabellengruppen und ein gestricheltes Rechteck für den Auswahlrahmen.
Präsenzinformationen, die von anderen Benutzern eintreffen, werden immer auf Ihrer Zeichenfläche gezeichnet, unabhängig davon, ob diese Option eingeschaltet ist.
Im Flow-Modus der Registerkarte Visualisierung werden Fokus und Auswahl anderer Benutzer auf den Tabellenkarten gezeichnet, ihre Auswahlrahmen jedoch nicht. Die Tabellen, die Sie dort auswählen, werden weiterhin gesendet; der Auswahlrahmen, den Sie dort aufziehen, nicht.
editor.getSharedStore({ focusTracker: false });
Schalten Sie beide Tracker aus, um den gemeinsamen Store als reine Weiterleitung zu nutzen, die keine eigenen Präsenzinformationen veröffentlicht.
Präsenzinformationen, die andere Editoren senden, werden weiterhin empfangen und gezeichnet.
So machen es die Editoren in VS Code, Obsidian, einer JetBrains-IDE und Google Drive.
editor.getSharedStore({ mouseTracker: false, focusTracker: false });
Mauszeiger, Fokus, Auswahl und Auswahlrahmen werden im selben Stream wie die Änderungen am Dokument übertragen, sind aber flüchtig.
Sie gelangen nie in das Dokument, den Verlauf oder editor.value, sodass ein Transport sie verwerfen darf und ein replicationStore sie ignoriert.
SharedStore
connection, disconnect
Diese Methoden legen den aktuellen Verbindungsstatus fest.
Solange die Verbindung getrennt ist, werden Änderungen intern gepuffert.
Die gepufferten Änderungen werden an die Abonnenten ausgegeben, sobald die Übertragung wieder möglich ist.
Der Standardzustand ist verbunden (connection).
sharedStore.connection();
sharedStore.disconnect();
dispatch, dispatchSync
Wendet Änderungen anderer Editorinstanzen auf die aktuelle Editorinstanz an.
sharedStore.dispatch(actions); // async
sharedStore.dispatchSync(actions); // sync
subscribe
Abonniert Änderungen im Editor.
Nichts verlässt den Editor, solange es nicht mindestens einen Abonnenten gibt, und vorher vorgenommene Änderungen werden gepuffert.
Beim ersten Abonnieren werden außerdem die anderen Editoren nach ihren Kollaborationsregistern gefragt, die gleichzeitige Änderungen abgleichen, sodass ein später beitretender Editor sie auf dieselbe Weise abgleicht.
Das Dokument wird dabei nicht übertragen. Initialisieren Sie einen später beitretenden Editor daher zuerst.
Seit 4.0.0 fragt der Store jedes Mal erneut, wenn setInitialValue nach diesem ersten Abonnieren ein Dokument in seinen Editor lädt.
const unsubscribe = sharedStore.subscribe(actions => {
// send...
});
flushStreamBuffers
Sendet die Stream-Stapel, die dieser Store noch zurückhält, sofort statt nach ihrer Ruhephase.
Seit 3.9.2 funktioniert die Methode bei einem Store aus getSharedStore; in 3.9.1 ist sie im Typ vorhanden, bewirkt aber nichts.
Das Verschieben einer Tabelle, Notiz oder Tabellengruppe, das Ändern ihrer Farbe, das Ändern der Größe einer Notiz sowie das Scrollen oder Zoomen der Ansicht kommen jeweils als Stream kleiner Änderungen an. Bei der Größenänderung einer Tabellengruppe ist das nicht so: Sie wird einmal gesendet, wenn das Ziehen endet.
Der Store sammelt jeden Stream in einem Stapel und hält ihn zurück, bis der Stream 200 ms lang ruhig war. Er tut dies in zwei Stufen, sodass der Stapel etwa 400 ms nach der letzten Änderung gesendet wird.
Ein Aufruf beendet diese Wartezeit in beiden Stufen. Solange der Store verbunden ist, erhält ein Abonnent den Stapel, bevor der Aufruf zurückkehrt. Ein Aufruf, bei dem nichts zurückgehalten wird, sendet nichts.
Die Methode destroy, ob die des Stores oder die des Editors, verwirft einen noch zurückgehaltenen Stapel. Rufen Sie daher zuerst diese Methode auf, wenn Sie einen Store aufgeben und die anderen die letzte Änderung behalten sollen, etwa wenn eine Registerkarte geschlossen wird.
sharedStore.flushStreamBuffers();
sharedStore.destroy();
destroy
Zerstört die sharedStore-Instanz vollständig.
Der Editor selbst bleibt unberührt, sodass das Dokument nach dem Verlassen einer Sitzung bearbeitbar bleibt.
Die Maus- und Fokus-Tracker werden beendet, sobald der letzte sharedStore, den der Editor ausgegeben hat, zerstört ist.
Ein Stream-Stapel, den der Store noch zurückhält, wird mit ihm verworfen; rufen Sie zuerst flushStreamBuffers auf, um ihn zu senden.
sharedStore.destroy();