Jede hinreichend fortgeschrittene Technologie ist von Magie nicht mehr zu unterscheiden. Es sei denn, Sie verstehen es. Mein Name ist Thomas Steiner. Ich arbeite im Bereich Developer Relations bei Google. In diesem Artikel über meinen Vortrag auf der Google I/O werde ich einige der neuen Fugu APIs vorstellen und erläutern, wie sie die wichtigsten User Journeys in der Excalidraw-PWA verbessern. Sie können sich von diesen Ideen inspirieren lassen und sie auf Ihre eigenen Apps anwenden.
Wie ich zu Excalidraw kam
Ich möchte mit einer Geschichte beginnen. Am 1. Januar 2020 twitterte Christopher Chedeau, Softwareentwickler bei Facebook, über eine kleine Zeichen-App, an der er gerade arbeitete. Mit diesem Tool können Sie Kästchen und Pfeile zeichnen, die wie Comicfiguren und handgezeichnet wirken. Am nächsten Tag können Sie auch Ellipsen und Text zeichnen sowie Objekte auswählen und verschieben. Am 3. Januar erhielt die App den Namen „Excalidraw“ und wie bei jedem guten Nebenprojekt war der Kauf des Domainnamens eine der ersten Handlungen von Christopher. Sie können jetzt Farben verwenden und die gesamte Zeichnung als PNG exportieren.
Am 15. Januar veröffentlichte Christopher einen Blogpost, der auf Twitter viel Aufmerksamkeit erregte, auch meine. Der Beitrag begann mit einigen beeindruckenden Statistiken:
- 12.000 einzelne aktive Nutzer
- 1.500 Sterne auf GitHub
- 26 Beitragende
Für ein Projekt, das erst vor zwei Wochen gestartet wurde, ist das gar nicht schlecht. Aber was mein Interesse wirklich weckte, war weiter unten im Beitrag. Christopher schrieb, dass er dieses Mal etwas Neues ausprobiert hat: Er hat allen, die einen Pull-Request eingereicht haben, uneingeschränkten Commit-Zugriff gewährt. Am selben Tag, an dem ich den Blogpost gelesen habe, hatte ich einen Pull-Request geöffnet, mit dem Excalidraw die Unterstützung der File System Access API hinzugefügt wurde. Damit wurde ein Funktionswunsch erfüllt, den jemand geäußert hatte.
Mein Pull-Request wurde einen Tag später zusammengeführt und ab diesem Zeitpunkt hatte ich vollen Commit-Zugriff. Selbstverständlich habe ich meine Macht nicht missbraucht. Das gilt auch für alle anderen der bisher 149 Mitwirkenden.
Heute ist Excalidraw eine vollwertige installierbare progressive Webanwendung mit Offlineunterstützung, einem beeindruckenden dunklen Modus und dank der File System Access API die Möglichkeit, Dateien zu öffnen und zu speichern.
Lipis erklärt, warum er so viel Zeit in Excalidraw investiert
Damit ist meine Geschichte zu Excalidraw zu Ende. Bevor ich jedoch auf einige der tollen Funktionen von Excalidraw eingehe, möchte ich Ihnen Panayiotis vorstellen. Panayiotis Lipiridis, im Internet einfach als lipis bekannt, ist der produktivste Mitwirkende an Excalibur. Ich habe lipis gefragt, was ihn dazu motiviert, so viel Zeit in Excalidraw zu investieren:
Wie alle anderen habe ich von diesem Projekt durch Christophers Tweet erfahren. Mein erster Beitrag war die Open Color Library, die Farben, die auch heute noch in Excalidraw enthalten sind. Als das Projekt wuchs und wir viele Anfragen erhielten, war mein nächster großer Beitrag die Entwicklung eines Backends zum Speichern von Zeichnungen, damit Nutzer sie teilen konnten. Was mich aber wirklich antreibt, ist, dass alle, die Excalidraw ausprobiert haben, nach Ausreden suchen, es noch einmal zu verwenden.
Ich stimme lipis voll und ganz zu. Wer Excalidraw schon einmal ausprobiert hat, sucht nach Ausreden, es noch einmal zu verwenden.
Excalidraw in Aktion
Ich möchte Ihnen jetzt zeigen, wie Sie Excalidraw in der Praxis verwenden können. Ich bin kein großer Künstler, aber das Google I/O-Logo ist einfach genug, also versuche ich es. Ein Rechteck steht für das „i“, eine Linie kann der Schrägstrich sein und das „o“ ist ein Kreis. Ich halte die Umschalttaste gedrückt, damit ein perfekter Kreis entsteht. Ich verschiebe den Schrägstrich ein wenig, damit er besser aussieht. Jetzt noch etwas Farbe für das „i“ und das „o“. Blau ist gut. Vielleicht ein anderer Füllungsstil? Vollständig durchgezogen oder Schraffuren? Nein, die Schraffierung sieht gut aus. Es ist nicht perfekt, aber das ist die Idee von Excalidraw. Ich speichere es jetzt.
Ich klicke auf das Symbol zum Speichern und gebe im Dialogfeld zum Speichern der Datei einen Dateinamen ein. In Chrome, einem Browser, der die File System Access API unterstützt, ist dies kein Download, sondern ein echter Speichervorgang, bei dem ich den Speicherort und den Namen der Datei auswählen kann. Wenn ich Änderungen vornehme, kann ich sie einfach in derselben Datei speichern.
Ich ändere das Logo und mache das „i“ rot. Wenn ich jetzt noch einmal auf „Speichern“ klicke, wird meine Änderung in derselben Datei wie zuvor gespeichert. Als Nachweis werde ich den Canvas löschen und die Datei wieder öffnen. Wie Sie sehen, ist das geänderte rot-blaue Logo wieder da.
Mit Dateien arbeiten
In Browsern, die die File System Access API derzeit nicht unterstützen, ist jeder Speichervorgang ein Download. Wenn ich also Änderungen vornehme, habe ich mehrere Dateien mit einer inkrementellen Zahl im Dateinamen, die meinen Downloadordner füllen. Trotz dieses Nachteils kann ich die Datei aber trotzdem speichern.
Dateien öffnen
Was ist also das Geheimnis? Wie kann das Öffnen und Speichern in verschiedenen Browsern funktionieren, die die File System Access API unterstützen oder nicht? Das Öffnen einer Datei in Excalidraw erfolgt über die Funktion loadFromJSON)(
, die wiederum die Funktion fileOpen()
aufruft.
export const loadFromJSON = async (localAppState: AppState) => {
const blob = await fileOpen({
description: 'Excalidraw files',
extensions: ['.json', '.excalidraw', '.png', '.svg'],
mimeTypes: ['application/json', 'image/png', 'image/svg+xml'],
});
return loadFromBlob(blob, localAppState);
};
Die fileOpen()
-Funktion stammt aus einer kleinen Bibliothek namens browser-fs-access, die ich geschrieben habe und die wir in Excalidraw verwenden. Diese Bibliothek bietet Dateisystemzugriff über die File System Access API mit einem Legacy-Fallback, sodass sie in jedem Browser verwendet werden kann.
Sehen wir uns zuerst die Implementierung an, wenn die API unterstützt wird. Nachdem die zulässigen MIME-Typen und Dateiendungen ausgehandelt wurden, wird die Funktion showOpenFilePicker()
der File System Access API aufgerufen. Diese Funktion gibt ein Array von Dateien oder eine einzelne Datei zurück, je nachdem, ob mehrere Dateien ausgewählt sind. Jetzt müssen Sie nur noch den Dateihandle auf das Dateiobjekt setzen, damit es wieder abgerufen werden kann.
export default async (options = {}) => {
const accept = {};
// Not shown: deal with extensions and MIME types.
const handleOrHandles = await window.showOpenFilePicker({
types: [
{
description: options.description || '',
accept: accept,
},
],
multiple: options.multiple || false,
});
const files = await Promise.all(handleOrHandles.map(getFileWithHandle));
if (options.multiple) return files;
return files[0];
const getFileWithHandle = async (handle) => {
const file = await handle.getFile();
file.handle = handle;
return file;
};
};
Die Fallback-Implementierung basiert auf einem input
-Element vom Typ "file"
. Nach der Verhandlung der zulässigen MIME-Typen und Erweiterungen besteht der nächste Schritt darin, programmatisch auf das Eingabeelement zu klicken, damit das Dialogfeld zum Öffnen der Datei angezeigt wird. Bei einer Änderung, d. h. wenn der Nutzer eine oder mehrere Dateien ausgewählt hat, wird das Promise aufgelöst.
export default async (options = {}) => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
const accept = [
...(options.mimeTypes ? options.mimeTypes : []),
options.extensions ? options.extensions : [],
].join();
input.multiple = options.multiple || false;
input.accept = accept || '*/*';
input.addEventListener('change', () => {
resolve(input.multiple ? Array.from(input.files) : input.files[0]);
});
input.click();
});
};
Dateien speichern
Jetzt zum Speichern. In Excalidraw erfolgt das Speichern über eine Funktion namens saveAsJSON()
. Zuerst wird das Excalidraw-Element-Array in JSON serialisiert, das JSON in einen Blob konvertiert und dann die Funktion fileSave()
aufgerufen. Diese Funktion wird ebenfalls von der Bibliothek browser-fs-access bereitgestellt.
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: 'application/vnd.excalidraw+json',
});
const fileHandle = await fileSave(
blob,
{
fileName: appState.name,
description: 'Excalidraw file',
extensions: ['.excalidraw'],
},
appState.fileHandle,
);
return { fileHandle };
};
Sehen wir uns zuerst die Implementierung für Browser mit Unterstützung der File System Access API an. Die ersten Zeilen sehen etwas kompliziert aus, aber sie dienen nur dazu, die MIME-Typen und Dateiendungen auszuhandeln. Wenn ich schon einmal gespeichert habe und bereits einen Dateihandle habe, muss kein Speicherdialogfeld angezeigt werden. Wenn dies jedoch das erste Speichern ist, wird ein Dateidialogfeld angezeigt und die App erhält einen Dateihandle für die zukünftige Verwendung zurück. Der Rest wird dann einfach in die Datei geschrieben, was über einen schreibbaren Stream geschieht.
export default async (blob, options = {}, handle = null) => {
options.fileName = options.fileName || 'Untitled';
const accept = {};
// Not shown: deal with extensions and MIME types.
handle =
handle ||
(await window.showSaveFilePicker({
suggestedName: options.fileName,
types: [
{
description: options.description || '',
accept: accept,
},
],
}));
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
};
Die Funktion „Als Datei speichern“
Wenn ich einen bereits vorhandenen Dateihandle ignorieren möchte, kann ich die Funktion „Als Datei speichern“ implementieren, um eine neue Datei auf Grundlage einer vorhandenen Datei zu erstellen. Ich öffne dazu eine vorhandene Datei, führe einige Änderungen durch und überschreibe die Datei nicht, sondern erstelle mit der Funktion „Als Datei speichern“ eine neue Datei. Die Originaldatei bleibt dabei erhalten.
Die Implementierung für Browser, die die File System Access API nicht unterstützen, ist kurz, da nur ein Ankerelement mit einem download
-Attribut erstellt wird, dessen Wert der gewünschte Dateiname und eine Blob-URL als href
-Attributwert ist.
export default async (blob, options = {}) => {
const a = document.createElement('a');
a.download = options.fileName || 'Untitled';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', () => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Das Ankerelement wird dann programmatisch angeklickt. Um Speicherlecks zu vermeiden, muss die Blob-URL nach der Verwendung widerrufen werden. Da es sich nur um einen Download handelt, wird nie ein Dialogfeld zum Speichern von Dateien angezeigt und alle Dateien werden im Standardordner Downloads
abgelegt.
Drag-and-Drop
Eine meiner Lieblingssystemintegrationen auf dem Computer ist Drag-and-drop. Wenn ich in Excalidraw eine .excalidraw
-Datei in die Anwendung ziehe, wird sie sofort geöffnet und ich kann mit der Bearbeitung beginnen. In Browsern, die die File System Access API unterstützen, kann ich meine Änderungen sogar sofort speichern. Es ist kein Dialogfeld zum Speichern von Dateien erforderlich, da der erforderliche Dateihandle durch das Drag-and-drop-Verfahren abgerufen wurde.
Rufen Sie dazu getAsFileSystemHandle()
für das Element Datenübertragung auf, wenn die File System Access API unterstützt wird. Diesen Dateihandle übergebe ich dann an loadFromBlob()
, an das Sie sich vielleicht aus den vorherigen Absätzen erinnern. Mit Dateien können Sie so viele Dinge tun: Öffnen, Speichern, Überschreiben, Ziehen und Ablegen. Mein Kollege Pete und ich haben all diese und weitere Tricks in diesem Artikel dokumentiert, damit Sie sich auf den neuesten Stand bringen können, falls Ihnen etwas zu schnell vorgegangen ist.
const file = event.dataTransfer?.files[0];
if (file?.type === 'application/json' || file?.name.endsWith('.excalidraw')) {
this.setState({ isLoading: true });
// Provided by browser-fs-access.
if (supported) {
try {
const item = event.dataTransfer.items[0];
file as any.handle = await item as any
.getAsFileSystemHandle();
} catch (error) {
console.warn(error.name, error.message);
}
}
loadFromBlob(file, this.state).then(({ elements, appState }) =>
// Load from blob
).catch((error) => {
this.setState({ isLoading: false, errorMessage: error.message });
});
}
Dateien teilen
Eine weitere Systemintegration, die derzeit auf Android, ChromeOS und Windows verfügbar ist, ist die Web Share Target API. Hier bin ich in der Dateien App in meinem Ordner Downloads
. Ich sehe zwei Dateien, eine davon mit dem unspezifischen Namen untitled
und einem Zeitstempel. Um zu sehen, was es enthält, klicke ich auf das Dreipunkt-Menü und dann auf „Teilen“. Eine der angezeigten Optionen ist „Excalidraw“. Wenn ich auf das Symbol tippe, sehe ich, dass die Datei nur noch das I/O-Logo enthält.
Lipis zur eingestellten Electron-Version
Eine Sache, die Sie mit Dateien tun können, die ich noch nicht erwähnt habe, ist das Doppelklicken. Wenn Sie normalerweise auf eine Datei doppelklicken, wird die Anwendung geöffnet, die mit dem MIME-Typ der Datei verknüpft ist. Für .docx
wäre das beispielsweise Microsoft Word.
Excalidraw hatte früher eine Electron-Version der App, die solche Dateitypzuordnungen unterstützte. Wenn Sie also auf eine .excalidraw
-Datei doppelklickten, wurde die Excalidraw Electron-App geöffnet. Lipis, den Sie bereits kennen, war sowohl der Erfinder als auch derjenige, der Excalidraw Electron eingestellt hat. Ich fragte ihn, warum er der Meinung war, dass die Electron-Version eingestellt werden könnte:
Seit Beginn haben Nutzer nach einer Electron-App gefragt, vor allem weil sie Dateien per Doppelklick öffnen wollten. Wir wollten die App auch in App-Shops anbieten. Parallel dazu schlug jemand vor, stattdessen eine PWA zu erstellen. Wir haben also beides gemacht. Glücklicherweise wurden wir mit den APIs von Project Fugu vertraut gemacht, darunter APIs für den Zugriff auf das Dateisystem, die Zwischenablage und die Dateiverwaltung. Mit nur einem Klick können Sie die App auf Ihrem Computer oder Mobilgerät installieren, ohne das zusätzliche Gewicht von Electron. Es war eine einfache Entscheidung, die Electron-Version einzustellen, sich nur auf die Webanwendung zu konzentrieren und sie zur bestmöglichen PWA zu machen. Außerdem können wir jetzt PWAs im Play Store und im Microsoft Store veröffentlichen. Das ist großartig!
Man könnte sagen, dass Excalidraw für Electron nicht eingestellt wurde, weil Electron schlecht ist, sondern weil das Web gut genug geworden ist. Das gefällt mir.
Dateiverwaltung
Wenn ich sage, dass das Web gut genug ist, dann liegt das an Funktionen wie der anstehenden Dateiverwaltung.
Dies ist eine reguläre macOS Big Sur-Installation. Sehen wir uns an, was passiert, wenn ich mit der rechten Maustaste auf eine Exaclidraw-Datei klicke. Ich kann sie mit Excalidraw, der installierten PWA, öffnen. Natürlich würde auch ein Doppelklick funktionieren, es ist nur weniger spektakulär, in einem Screencast zu demonstrieren.
Wie funktioniert das? Im ersten Schritt müssen die Dateitypen, die meine Anwendung verarbeiten kann, dem Betriebssystem bekannt gemacht werden. Ich mache das in einem neuen Feld namens file_handlers
im Manifest der Webanwendung. Sein Wert ist ein Array von Objekten mit einer Aktion und einer accept
-Property. Die Aktion bestimmt den URL-Pfad, unter dem das Betriebssystem Ihre App startet. Das Accept-Objekt besteht aus Schlüssel/Wert-Paaren von MIME-Typen und den zugehörigen Dateiendungen.
{
"name": "Excalidraw",
"description": "Excalidraw is a whiteboard tool...",
"start_url": "/",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff",
"file_handlers": [
{
"action": "/",
"accept": {
"application/vnd.excalidraw+json": [".excalidraw"]
}
}
]
}
Im nächsten Schritt wird die Datei beim Starten der Anwendung verarbeitet. Das passiert auf der launchQueue
-Benutzeroberfläche, wo ich einen Verbraucher durch Aufrufen von setConsumer()
festlegen muss. Der Parameter dieser Funktion ist eine asynchrone Funktion, die launchParams
empfängt. Dieses launchParams
-Objekt hat ein Feld namens „files“, über das ich ein Array von Datei-Handles abrufen kann. Ich benötige nur den ersten und erhalte über diesen Dateihandle ein Blob, das ich dann an unseren alten Freund loadFromBlob()
weitergebe.
if ('launchQueue' in window && 'LaunchParams' in window) {
window as any.launchQueue
.setConsumer(async (launchParams: { files: any[] }) => {
if (!launchParams.files.length) return;
const fileHandle = launchParams.files[0];
const blob: Blob = await fileHandle.getFile();
blob.handle = fileHandle;
loadFromBlob(blob, this.state).then(({ elements, appState }) =>
// Initialize app state.
).catch((error) => {
this.setState({ isLoading: false, errorMessage: error.message });
});
});
}
Falls das zu schnell ging, können Sie in meinem Artikel mehr über die File Handling API erfahren. Sie können die Dateiverwaltung aktivieren, indem Sie das Flag für experimentelle Funktionen der Webplattform festlegen. Die Funktion wird voraussichtlich im Laufe des Jahres in Chrome eingeführt.
Zwischenablage-Integration
Eine weitere coole Funktion von Excalidraw ist die Zwischenablage-Integration. Ich kann meine gesamte Zeichnung oder nur Teile davon in die Zwischenablage kopieren, nach Belieben ein Wasserzeichen hinzufügen und sie dann in eine andere App einfügen. Dies ist übrigens eine Webversion der Paint-App von Windows 95.
Das funktioniert überraschend einfach. Ich benötige nur den Canvas als Blob, den ich dann in die Zwischenablage schreibe. Dazu übergebe ich der Funktion navigator.clipboard.write()
ein Array mit einem Element, das ein ClipboardItem
mit dem Blob enthält. Weitere Informationen zu den Möglichkeiten der Zwischenablage-API finden Sie in den Artikeln von Jason und meinem Artikel.
export const copyCanvasToClipboardAsPng = async (canvas: HTMLCanvasElement) => {
const blob = await canvasToBlob(canvas);
await navigator.clipboard.write([
new window.ClipboardItem({
'image/png': blob,
}),
]);
};
export const canvasToBlob = async (canvas: HTMLCanvasElement): Promise<Blob> => {
return new Promise((resolve, reject) => {
try {
canvas.toBlob((blob) => {
if (!blob) {
return reject(new CanvasError(t('canvasError.canvasTooBig'), 'CANVAS_POSSIBLY_TOO_BIG'));
}
resolve(blob);
});
} catch (error) {
reject(error);
}
});
};
Zusammenarbeit mit Anderen
Sitzungs-URL teilen
Wussten Sie, dass Excalidraw auch einen Modus für die Gruppenarbeit hat? Mehrere Personen können gemeinsam an einem Dokument arbeiten. Wenn ich eine neue Sitzung starten möchte, klicke ich auf die Schaltfläche für die Live-Zusammenarbeit und starte dann eine Sitzung. Dank der Web Share API, die in Excalidraw integriert ist, kann ich die Sitzungs-URL ganz einfach mit meinen Mitbearbeitern teilen.
Live-Zusammenarbeit
Ich habe eine Gruppenarbeit lokal simuliert, indem ich an meinem Pixelbook, meinem Google Pixel 3a und meinem iPad Pro am Google I/O-Logo gearbeitet habe. Sie sehen, dass Änderungen, die ich auf einem Gerät vornehme, auf allen anderen Geräten übernommen werden.
Ich sehe sogar, wie sich alle Cursor bewegen. Der Cursor von Pixelbook bewegt sich gleichmäßig, da er über ein Touchpad gesteuert wird. Der Cursor von Pixel 3a und das iPad Pro-Tablet springen jedoch herum, da ich diese Geräte durch Tippen mit dem Finger steuere.
Status von Mitbearbeitern ansehen
Um die Echtzeit-Zusammenarbeit zu verbessern, gibt es sogar ein Inaktivitätserkennungssystem. Der Cursor meines iPad Pro zeigt einen grünen Punkt, wenn ich es verwende. Der Punkt wird schwarz, wenn ich zu einem anderen Browsertab oder einer anderen App wechsle. Wenn ich in der Excalidraw-App bin, aber nichts tue, wird der Cursor als inaktiv angezeigt, was durch die drei „zZZ“ symbolisiert wird.
Leser unserer Publikationen könnten annehmen, dass die Inaktivitätserkennung über die Idle Detection API erfolgt, einen Vorschlag in der Anfangsphase, der im Rahmen von Project Fugu entwickelt wird. Spoileralarm: Das ist nicht der Fall. Wir hatten zwar eine auf dieser API basierende Implementierung in Excalidraw, haben uns aber letztendlich für einen traditionelleren Ansatz entschieden, der auf der Messung der Mausbewegung und der Sichtbarkeit der Seite basiert.
Wir haben Feedback dazu gegeben, warum die API zur Inaktivitätserkennung unseren Anwendungsfall nicht löste. Alle APIs von Project Fugu werden offen entwickelt, sodass jeder seine Meinung äußern und gehört werden kann.
Lipis zu den Problemen mit Excalidraw
Apropos: Ich habe Lipis noch eine letzte Frage gestellt, was seiner Meinung nach auf der Webanwendung fehlt und Excalidraw zurückhält:
Die File System Access API ist großartig, aber wissen Sie was? Die meisten Dateien, die mir wichtig sind, befinden sich heutzutage in meiner Dropbox oder in Google Drive, nicht auf meiner Festplatte. Ich würde mir wünschen, dass die File System Access API eine Abstraktionsschicht für Anbieter von Remote-Dateisystemen wie Dropbox oder Google enthalten würde, die sie integrieren und für die Entwickler Code schreiben könnten. Nutzer können dann beruhigt sein, da sie wissen, dass ihre Dateien bei einem vertrauenswürdigen Cloud-Anbieter sicher sind.
Ich stimme lipis voll und ganz zu. Ich lebe auch in der Cloud. Wir hoffen, dass diese Funktion bald eingeführt wird.
Modus für anklickbare Tabs
Wow! Wir haben viele wirklich tolle API-Integrationen in Excalidraw gesehen. Dateisystem, Dateiverwaltung, Zwischenablage, Webfreigabe und Ziel der Webfreigabe. Aber es gibt noch etwas anderes. Bisher konnte ich immer nur ein Dokument gleichzeitig bearbeiten. Zum Glück nicht. Wir stellen Ihnen heute eine erste Version des Tab-Modus in Excalidraw vor. So sieht das aus.
Ich habe eine vorhandene Datei in der installierten Excalidraw-PWA geöffnet, die im eigenständigen Modus ausgeführt wird. Jetzt öffne ich einen neuen Tab im eigenständigen Fenster. Dies ist kein normaler Browsertab, sondern ein PWA-Tab. Auf diesem neuen Tab kann ich dann eine sekundäre Datei öffnen und unabhängig voneinander im selben App-Fenster daran arbeiten.
Der Tab-Modus befindet sich noch in der Entwicklungsphase und nicht alles ist in Stein gemeißelt. Weitere Informationen zum aktuellen Status dieser Funktion findest du in diesem Artikel.
Abschluss
Wenn du über diese und andere Funktionen auf dem Laufenden bleiben möchtest, behalte unseren Fugu API-Tracker im Auge. Wir freuen uns sehr, das Web voranzubringen und Ihnen mehr Möglichkeiten auf der Plattform zu bieten. Auf ein immer besser werdendes Excalidraw und auf all die tollen Anwendungen, die Sie damit erstellen werden. Jetzt unter excalidraw.com loslegen
Ich bin schon gespannt, einige der heute vorgestellten APIs in Ihren Apps zu sehen. Ich heiße Tom und du findest mich auf Twitter und im Internet unter @tomayac. Vielen Dank fürs Zusehen. Ich wünsche Ihnen noch einen schönen Tag.