Excalidraw und Fugu: Kaufprozesse bei Hauptdiensten verbessern

Jede hinreichend fortschrittliche Technologie ist nicht von Magie zu unterscheiden. Es sei denn, Sie verstehen es. Mein Name ist Thomas Steiner und ich arbeite bei Google für Developer Relations. In diesem Beitrag zu meinem Google I/O-Vortrag werde ich auf einige der neuen Fugu-APIs eingehen und wie sie die wichtigsten Nutzerpfade in der Excalidraw-PWA verbessern. So können Sie 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ästen und Pfeile zeichnen, die comicartig und von Hand gezeichnet werden. 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. Inzwischen konnten Sie Farben verwenden und die gesamte Zeichnung als PNG exportieren.

Screenshot der Prototypanwendung von Excalidraw, der zeigt, dass Rechtecke, Pfeile, Ellipsen und Text unterstützt werden

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 begonnen hat, ist das gar nicht so schlecht. Aber was mein Interesse wirklich weckte, war weiter unten im Beitrag. Christopher schrieb, dass er diesmal etwas Neues ausprobiert hat: allen, die eine Pull-Anfrage erhalten haben, bedingungslosen Commit-Zugriff gewähren. 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, der von einem Nutzer geäußert wurde.

Screenshot des Tweets, in dem ich meine PR ankündige

Meine Pull-Anfrage wurde einen Tag später zusammengeführt und von da an hatte ich vollen Commit-Zugriff. Natürlich habe ich meine Macht nicht ausgenutzt. Das gilt auch für die anderen 149 Mitwirkenden.

Heute ist Excalidraw eine vollwertige installierbare progressive Webanwendung mit Offlineunterstützung, einem beeindruckenden dunklen Modus und ja, dank der File System Access API können Dateien geöffnet und gespeichert werden.

Screenshot der Excalidraw-PWA im heutigen Status.

Lipis erklärt, warum er so viel Zeit in Excalidraw investiert

Damit ist meine Geschichte "Wie ich zu Excalidraw kam" beendet. Bevor ich mich jedoch mit einigen der fantastischen Funktionen von Excalidraw beschäftigt, möchte ich Ihnen Panayiotis vorstellen. Panayiotis Lipiridis, im Internet einfach als Lipi bezeichnet, trägt am meisten zu Excalidraw bei. Ich habe lipis gefragt, was ihn dazu motiviert, so viel Zeit in Excalidraw zu investieren:

Wie alle anderen habe ich in Christophers Tweet von diesem Projekt 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 zahlreiche Anfragen erhielten, bestand mein nächster großer Beitrag darin, ein Back-End zum Speichern von Zeichnungen zu erstellen, damit Nutzer sie teilen können. Was mich aber wirklich antreibt, ist, dass alle, die Excalidraw ausprobiert haben, nach Ausreden suchen, es noch einmal zu verwenden.

Ich stimme Lilis voll und ganz zu. Wer Excalidraw ausprobiert hat, sucht nach Ausreden, um es noch einmal zu verwenden.

Excalidraw in Aktion

Ich möchte Ihnen jetzt zeigen, wie Sie Excalidraw in der Praxis einsetzen können. Ich bin kein großer Künstler, aber das Google I/O-Logo ist einfach genug, also versuche ich es. Ein Feld ist das „i“, eine Linie der Schrägstrich und das „o“ ein Kreis. Ich halte die Umschalttaste gedrückt, bis der Kreis perfekt ist. 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? Alles ausgefüllt oder schraffiert? Nein, die Schraffierung sieht gut aus. Sie ist nicht perfekt, aber das ist die Idee von Excalidraw, also lassen Sie es mich speichern.

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 jetzt das Logo und mache das "i" rot. Wenn ich jetzt erneut auf „Speichern“ klicke, wird meine Änderung wie zuvor in derselben Datei gespeichert. Als Nachweis werde ich den Canvas löschen und die Datei wieder öffnen. Das modifizierte rot-blaue Logo ist 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 werden geöffnet

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 eine Funktion namens 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 Funktion fileOpen(), die aus einer von mir geschriebenen kleinen Bibliothek namens browser-fs-access stammt, die wir in Excalidraw verwenden. Diese Bibliothek bietet Zugriff auf das Dateisystem über die File System Access API mit einem Legacy-Fallback und kann daher in jedem Browser verwendet werden.

Sehen wir uns zuerst die Implementierung an, wenn die API unterstützt wird. Nach der Verhandlung der zulässigen MIME-Typen und Dateiendungen wird als Erstes 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 das Datei-Handle 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 des Typs "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 in einer Funktion namens saveAsJSON(). Zuerst wird das Excalidraw-Element-Array in JSON serialisiert, das JSON-Format in ein Blob konvertiert und dann eine Funktion namens 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 lediglich 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 der erste Speichervorgang ist, wird ein Dateidialogfeld angezeigt und die App erhält ein Datei-Handle zur späteren Verwendung. Der Rest wird dann einfach in die Datei geschrieben, was über einen beschreibbaren 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 „Speichern unter“

Wenn ich ein bereits vorhandenes Datei-Handle ignorieren möchte, kann ich eine „Speichern unter“-Funktion implementieren, um eine neue Datei auf der 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 intakt.

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 hierbei nur um einen Download handelt, wird kein Dialogfeld zum Speichern von Dateien angezeigt und alle Dateien landen im Standardordner Downloads.

Drag-and-Drop

Eine meiner Lieblings-Systemintegrationen auf dem Desktop 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. Dateien öffnen, speichern, zu sehr speichern, per Drag-and-drop verschieben. Mein Kollege Pete und ich haben alle diese Tricks und mehr in unserem Artikel dokumentiert, damit Sie es auf den neuesten Stand bringen können, falls das alles zu schnell verlaufen sollte.

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 unter Android, ChromeOS und Windows läuft, erfolgt über die Web Share Target API. Hier bin ich in der App „Dateien“ in meinem Ordner „Downloads“. Ich kann zwei Dateien sehen, eine davon mit dem nicht beschreibenden Namen untitled und einem Zeitstempel. Um zu prüfen, was sie enthält, klicke ich auf die drei Punkte, dann auf „Share“. 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 auf der veralteten Electron-Version

Dateien, über die ich noch nicht gesprochen habe, können Sie beispielsweise mit doubleclick. Wenn Sie normalerweise auf eine Datei doppelklicken, wird die App geöffnet, die mit dem MIME-Typ der Datei verknüpft ist. Für .docx wäre das beispielsweise Microsoft Word.

Excalidraw hat zuvor eine Electron-Version der App verwendet, die solche Dateitypverknüpfungen unterstützt. Wenn du also auf eine .excalidraw-Datei doppelklickst, wird die Excalidraw Electron-App geöffnet. Lipis, den Sie bereits kennen, war sowohl der Erfinder als auch der Abschaffung von Excalidraw Electron. Ich fragte ihn, warum er es für möglich hält, die Electron-Version einzustellen:

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, eine PWA zu erstellen, also haben wir einfach 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 Desktop oder Mobilgerät installieren, ohne das zusätzliche Gewicht von Electron. Die Entscheidung, die Electron-Version einzustellen, sich nur auf die Web-App zu konzentrieren und sie zur bestmöglichen PWA zu machen, fiel uns leicht. Außerdem können wir jetzt PWAs im Play Store und im Microsoft Store veröffentlichen. Das ist großartig!

Man könnte sagen, Excalidraw für Electron wurde nicht eingestellt, weil Electron schlecht ist – nicht, weil das Web mittlerweile gut genug ist. Das gefällt mir.

Dateiverwaltung

Wenn ich sage, „das Web ist gut genug geworden“, liegt das an Funktionen wie der baldigen Dateiverarbeitung.

Dies ist eine reguläre macOS Big Sur-Installation. Schauen Sie sich nun an, was passiert, wenn ich mit der rechten Maustaste auf eine Excalidraw-Datei klicke. Ich kann sie mit Excalidraw, der installierten PWA, öffnen. Natürlich würde auch ein Doppelklick funktionieren. Die Demonstration in einem Screencast ist nur weniger dramatisch.

Wie funktioniert das? Im ersten Schritt muss ich dem Betriebssystem die Dateitypen mitteilen, die meine Anwendung verarbeiten kann. Dazu verwende ich im Web-App-Manifest ein neues Feld namens file_handlers. 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"]
      }
    }
  ]
}

Der nächste Schritt besteht darin, die Datei zu verarbeiten, wenn die Anwendung gestartet wird. Das passiert in der launchQueue-Benutzeroberfläche, wo ich einen Verbraucher durch Aufrufen von setConsumer() festlegen muss. Der Parameter für diese Funktion ist eine asynchrone Funktion, die das launchParams empfängt. Dieses launchParams-Objekt hat ein Feld namens „files“, durch das ich ein Array von Datei-Handles erhalte, mit denen ich arbeiten 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 dies zu schnell ging, erfahren Sie in meinem Artikel mehr über die File Handling API. Sie können die Dateiverwaltung aktivieren, indem Sie das Flag für experimentelle Funktionen der Webplattform festlegen. Es ist voraussichtlich noch in diesem Jahr in Chrome verfügbar.

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 erstaunlich einfach. Dazu benötige ich lediglich den Canvas als Blob, den ich dann in die Zwischenablage schreibe. Dazu übergebe ich ein Ein-Element-Array mit einem ClipboardItem mit dem Blob an die navigator.clipboard.write()-Funktion. 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 zusammen 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 von Excalidraw eingebundenen Web Share API kann ich die Sitzungs-URL ganz einfach für meine Mitbearbeiter freigeben.

Zusammenarbeit in Echtzeit

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. Wie Sie sehen, werden Änderungen, die ich auf einem Gerät vornehme, auch auf allen anderen Geräten übernommen.

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 Zusammenarbeit in Echtzeit zu verbessern, wird sogar ein System zur Erkennung inaktiver Daten ausgeführt. Wenn ich das iPad Pro verwende, wird der Cursor mit einem grünen Punkt angezeigt. 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.

Screenshot des Feedbacks zur Inaktivitätserkennung, das im WICG-Repository für die Inaktivitätserkennung gesendet wurde.

Wir haben Feedback dazu eingereicht, warum unser Anwendungsfall mit der Idle Detection API nicht gelöst werden konnte. Alle APIs von Project Fugu werden offen entwickelt, sodass jeder seine Meinung äußern und gehört werden kann.

Was hält Excalidraw zurück

Davon habe ich Lipi eine letzte Frage gestellt, was ihrer Meinung nach auf der Webplattform fehlt, die Excalidraw zurückhält:

Die File System Access API ist toll, aber weißt du 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 sich dann entspannen und wissen, dass ihre Dateien bei dem Cloud-Anbieter, dem sie vertrauen, sicher sind.

Ich stimme Lipi zu, ich lebe auch in der Cloud. Wir hoffen, dass dies bald umgesetzt wird.

Anwendungsmodus mit Tabs

Wow! Wir haben in Excalidraw viele tolle API-Integrationen gesehen. Dateisystem, Dateiverwaltung, Zwischenablage, Webfreigabe und Ziel der Webfreigabe. Aber hier noch eine Sache. Bisher konnte ich immer nur ein Dokument bearbeiten. Zum Glück nicht. Sehen Sie sich zum ersten Mal eine frühe Version des Anwendungsmodus mit Tabs in Excalidraw an. 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 regulärer 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-Anwendungsmodus befindet sich noch in der Anfangsphase und nicht alles ist in Stein gemeißelt. Wenn es Sie interessiert, sollten Sie sich in meinem Artikel über den aktuellen Status dieser Funktion informieren.

Abschluss

Sehen Sie sich unseren Fugu API-Tracker an, um über diese und andere Funktionen auf dem Laufenden zu bleiben. 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. Mein Name ist Tom. Du findest mich als @tomayac auf Twitter und allgemein im Internet. Vielen Dank fürs Zusehen. Viel Spaß beim Rest der Google I/O.