Excalidraw und Fugu: Kaufprozesse bei Hauptdiensten verbessern

Jede ausreichend fortschrittliche Technologie ist nicht von Magie zu unterscheiden. Sofern Sie es nicht verstehen. Mein Name ist Thomas Steiner und ich arbeite bei Google im Bereich Developer Relations. In dieser Zusammenfassung meines Google I/O-Vortrags stelle ich einige der neuen Fugu-APIs vor und erläutere, wie sie die wichtigsten User Journeys 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, ein Softwareentwickler bei Facebook, über eine kleine Zeichen-App, an der er mit der Arbeit begonnen hatte. Mit diesem Tool können Sie Felder und Pfeile zeichnen, die sich mit Cartoons und von Hand gezeichnet fühlen. Am nächsten Tag können Sie auch Ellipsen und Text zeichnen sowie Objekte auswählen und verschieben. Am 3. Januar hatte die App ihren Namen, Excalidraw, und der Kauf des Domainnamens war wie bei jedem guten Nebenprojekt einer seiner ersten Handlungen. Inzwischen konnten Sie Farben verwenden und die gesamte Zeichnung als PNG exportieren.

Screenshot der Prototypanwendung von Excalidraw, die 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 bei mir. Der Beitrag begann mit einigen beeindruckenden Statistiken:

  • 12.000 einzelne aktive Nutzer
  • 1.500 Sterne auf GitHub
  • 26 Beitragende

Bei einem Projekt, das erst vor zwei Wochen begonnen hat, ist das gar nicht schlecht. Aber was mein Interesse tatsächlich geweckt hat, war weiter unten im Beitrag. Christopher schrieb, dass er diesmal etwas Neues ausprobiert hat: allen, die eine Pull-Anfrage erhalten haben, unbedingt einen bedingungslosen Commit-Zugriff zu erhalten. Am selben Tag, an dem ich den Blogpost gelesen habe, erhielt ich eine Pull-Anfrage, bei der Excalidraw Unterstützung für die File System Access API hinzugefügt hatte. Damit wurde eine Funktionsanfrage korrigiert, die jemand eingereicht hatte.

Screenshot des Tweets, in dem ich meinen PR angebe.

Meine Pull-Anfrage wurde einen Tag später zusammengeführt und hatte von nun an vollen Commit-Zugriff. Natürlich habe ich meine Macht nicht missbraucht. Und auch sonst niemand von den 149 Beitragenden, die bisher aktiv waren.

Heute ist Excalidraw eine vollwertige, installierbare progressive Web-App mit Offline-Unterstützung, einem atemberaubenden dunklen Modus und der Möglichkeit, Dateien mithilfe der File System Access API zu öffnen und zu speichern.

Screenshot der Excalidraw-PWA im heutigen Zustand

Lipis, warum er sich so viel Zeit für Excalidraw einsetzt,

Damit sind wir am Ende meiner Geschichte zu Excalidraw angekommen, aber bevor ich mich mit den faszinierenden Funktionen von Excalidraw beschäftige, möchte ich Ihnen gerne Panayiotis vorstellen. Panayiotis Lipiridis, im Internet einfach als Lipis bezeichnet, leistet am aktivsten einen Beitrag zu Excalidraw. Ich fragte Lipis, was ihn motiviert, so viel Zeit in Excalidraw zu investieren:

Wie alle anderen habe ich durch Christophers Tweet von diesem Projekt erfahren. Mein erster Beitrag bestand darin, die Open Color Library (Farbbibliothek öffnen) hinzuzufügen – die Farben, die auch heute noch zu Excalidraw gehören. Als das Projekt wuchs und wir sehr viele Anfragen erhielten, bestand mein nächster großer Beitrag darin, ein Back-End zum Speichern von Zeichnungen zu entwickeln, damit Nutzer sie teilen konnten. Was mich aber wirklich motiviert, ist, dass jeder, der Excalidraw ausprobiert hat, nach Ausreden sucht, um es wieder zu nutzen.

Ich stimme Lipis voll und ganz zu. Wer auch immer Excalidraw ausprobiert hat, sucht nach Ausreden, um es wieder zu nutzen.

Excalidraw in Aktion

Ich möchte Ihnen jetzt zeigen, wie Sie Excalidraw in der Praxis einsetzen können. Ich bin zwar kein großartiger Künstler, aber das Google I/O-Logo ist so einfach, wie es ist. Ein Kasten ist das "i", eine Linie kann der Schrägstrich sein und das "O" ist ein Kreis. Wenn ich die Umschalttaste gedrückt halte, erhalte ich einen perfekten Kreis. Ich verschiebe den Schrägstrich ein wenig, damit er besser aussieht. Jetzt etwas Farbe für das „i“ und das „o“. Blau ist gut. Vielleicht einen anderen Füllstil? Alles durchgehend oder schraffiert? Nein, hachure sieht toll aus. Es ist nicht perfekt, aber das ist die Idee von Excalidraw. Ich möchte es speichern.

Ich klicke auf das Symbol zum Speichern und gebe einen Dateinamen in das Dialogfeld zum Speichern der Datei ein. In Chrome, einem Browser, der die File System Access API unterstützt, handelt es sich nicht um einen Download, sondern um einen echten Speichervorgang, bei dem ich den Speicherort und den Namen der Datei auswählen und wo, wenn ich Änderungen vornehme, sie einfach in derselben Datei speichern kann.

Lassen Sie mich das Logo ändern und das "i" rot machen. Wenn ich jetzt noch einmal auf „Speichern“ klicke, wird meine Änderung in derselben Datei gespeichert. Zur Sicherheit möchte ich den Canvas leeren und die Datei noch einmal öffnen. Wie Sie sehen, ist wieder das modifizierte rot-blaue Logo zu sehen.

Mit Dateien arbeiten

In Browsern, die die File System Access API derzeit nicht unterstützen, handelt es sich bei jedem Speichervorgang um einen Download. Wenn ich also Änderungen vornehme, habe ich mehrere Dateien mit einer steigenden Zahl im Dateinamen, die meinen Downloadordner ausfüllen. Aber trotz des Nachteils kann ich die Datei 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 in einer Funktion namens 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() aus einer kleinen, von mir geschriebenen Bibliothek namens browser-fs-access, die wir in Excalidraw verwenden. Diese Bibliothek bietet über die File System Access API mit einem Legacy-Fallback Zugriff auf das Dateisystem, sodass sie in jedem Browser verwendet werden kann.

Zuerst zeige ich Ihnen die Implementierung, wenn die API unterstützt wird. Nachdem Sie die zulässigen MIME-Typen und Dateiendungen verhandelt haben, wird im zentralen Schritt 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. Nun müssen nur noch das Datei-Handle auf das Dateiobjekt festgelegt werden, 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". Nachdem Sie die zu akzeptierenden MIME-Typen und -Erweiterungen ausgehandelt haben, müssen Sie im nächsten Schritt programmatisch auf das Eingabeelement klicken, damit das Dialogfeld zum Öffnen von Dateien 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 werden gespeichert

Jetzt zum Speichern. In Excalidraw erfolgt das Speichern in einer Funktion namens saveAsJSON(). Zuerst wird das Excalidraw-Element-Array in JSON initialisiert, 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 };
};

Ich möchte noch einmal einen Blick auf die Implementierung für Browser werfen, die das File System Access API unterstützen. Die ersten Zeilen sehen etwas kompliziert aus, aber sie müssen nur die MIME-Typen und Dateierweiterungen aushandeln. Wenn ich bereits gespeichert habe und bereits einen Datei-Handle habe, muss kein Dialogfeld zum Speichern angezeigt werden. Wenn dies jedoch das erste Speichern ist, wird ein Dateidialogfeld angezeigt und die Anwendung erhält ein Datei-Handle für die zukünftige Verwendung. Der Rest wird dann nur in die Datei geschrieben, was über einen beschreibbaren Stream erfolgt.

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 einen bereits vorhandenen Datei-Handle ignoriere, kann ich die Funktion „Speichern unter“ implementieren, um eine neue Datei basierend auf einer vorhandenen Datei zu erstellen. Dazu öffne ich eine vorhandene Datei und ändere sie. Die vorhandene Datei wird dann nicht überschrieben, sondern mithilfe der Funktion „Speichern unter“ eine neue Datei erstellt. Die Originaldatei bleibt intakt.

Die Implementierung für Browser, die die File System Access API nicht unterstützen, ist kurz, da lediglich 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 landen im Standardordner Downloads.

Drag-and-Drop

Eine meiner Lieblingssystemintegrationen für 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. Sie müssen kein Dialogfeld zum Speichern der Datei aufrufen, da der erforderliche Datei-Handle per Drag-and-drop abgerufen wurde.

Dazu rufen Sie getAsFileSystemHandle() für das Datenübertragungselement auf, wenn die File System Access API unterstützt wird. Anschließend übergebe ich diesen Datei-Handle an loadFromBlob(), den Sie sich vielleicht noch aus den obigen Absätzen erinnern. Es gibt so viele Dinge, die Sie mit Dateien tun können: Öffnen, Speichern, Überspeichern, Ziehen und Ablegen. Mein Kollege Pete und ich haben all diese Tricks und mehr in unserem Artikel dokumentiert, damit Sie sich auf den neuesten Stand bringen können, falls alles zu schnell gelaufen 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 unter Android, ChromeOS und Windows verwendet wird, ist die Web Share Target API. Hier bin ich in der App „Dateien“ in meinem Ordner Downloads. Ich sehe zwei Dateien, eine davon mit dem nicht aussagekräftigen Namen untitled und einem Zeitstempel. Um zu überprüfen, was sie enthält, klicke ich auf die drei Punkte, dann auf „Teilen“. Eine der angezeigten Optionen ist Excalidraw. Wenn ich auf das Symbol tippe, sehe ich, dass die Datei wieder nur das E/A-Logo enthält.

Lipis auf der eingestellten Elektron-Version

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

Zuvor hatte Excalidraw eine Elektron-Version der App, die solche Dateitypverknüpfungen unterstützte. Wenn Sie auf eine .excalidraw-Datei doppelklicken, wird die Excalidraw Elektron-Anwendung geöffnet. Lipis, den Sie bereits kennen, war sowohl der Erfinder als auch der Verleger von Excalidraw Elektron. Ich fragte ihn, warum er es für möglich hielt, die Elektronen-Version einzustellen:

Von Anfang an wurde nach einer Elektron-App gefragt, weil Dateien per Doppelklick geöffnet werden wollten. Außerdem war geplant, die App in App-Shops anzubieten. Parallel dazu schlug jemand vor, eine PWA zu erstellen. Wir haben also beides gemacht. Zum Glück haben wir Project Fugu-APIs kennengelernt, wie z. B. Zugriff auf Dateisystem und Zwischenablage. Mit einem einzigen Klick können Sie die App auf Ihrem Desktop oder Mobilgerät installieren, ohne das zusätzliche Gewicht von Elektron zu verschwenden. Es fiel uns leicht, die Elektron-Version einzustellen, sich ausschließlich auf die Web-App 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 Elektron nicht eingestellt wurde, weil das Elektron nicht schlecht ist, sondern weil das Web gut genug geworden ist. Das gefällt mir!

Dateihandhabung

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

Dies ist eine reguläre macOS Big Sur-Installation. Schauen wir uns nun an, was passiert, wenn ich mit der rechten Maustaste auf eine Excalidraw-Datei klicke. Ich kann sie mit Excalidraw öffnen, der installierten PWA. Natürlich würde auch Doppelklicks funktionieren, es ist nur weniger dramatisch, dies in einem Screencast zu demonstrieren.

Wie funktioniert das? Der erste Schritt besteht darin, dem Betriebssystem die Dateitypen mitzuteilen, die meine Anwendung verarbeiten kann. Dazu verwende ich im Manifest der Web-App das neue Feld file_handlers. Sein Wert ist ein Array von Objekten mit einer Aktion und einem accept-Attribut. Die Aktion bestimmt den URL-Pfad, über den das Betriebssystem Ihre Anwendung startet. Das Annahmeobjekt sind Schlüssel/Wert-Paare von MIME-Typen und die zugehörigen Dateierweiterungen.

{
  "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 Start der Anwendung verarbeitet. Das passiert in der launchQueue-Oberfläche, in der ich durch Aufrufen von setConsumer() einen Nutzer festlegen muss. Der Parameter dieser 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 kümmere mich nur um das erste und aus diesem Datei-Handle erhalte ich ein Blob, das ich dann an unseren alten Freund loadFromBlob() übergebe.

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 ebenfalls zu schnell ging, lesen Sie in meinem Artikel mehr über die File Handling API. Sie können die Dateiverarbeitung aktivieren, indem Sie das Flag für experimentelle Webplattformfunktionen festlegen. Sie soll im Laufe des Jahres in Chrome eingeführt werden.

Integration der Zwischenablage

Eine weitere coole Funktion von Excalidraw ist die Integration der Zwischenablage. Ich kann meine gesamte Zeichnung oder Teile davon in die Zwischenablage kopieren, ich kann ein Wasserzeichen hinzufügen und es dann in eine andere App einfügen. Das ist übrigens eine Webversion der Windows 95 Paint App.

Das funktioniert überraschend einfach. Alles, was ich brauche, ist das Canvas als Blob, das 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 dazu, was du mit der Clipboard API tun kannst, findest du in Jasons 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 über einen Modus für Zusammenarbeit verfügt? Verschiedene Personen können zusammen an einem Dokument arbeiten. Um eine neue Sitzung zu starten, klicke ich auf die Schaltfläche „Live Collaboration“ und starte dann eine Sitzung. Dank der in Excalidraw integrierten Web Share API kann ich die Sitzungs-URL ganz einfach mit meinen Mitbearbeitern teilen.

Zusammenarbeit in Echtzeit

Ich habe lokal am Google I/O-Logo auf meinem Pixelbook, meinem Pixel 3a-Smartphone und meinem iPad Pro simuliert. Sie können sehen, dass Änderungen, die ich auf einem Gerät vor, auf allen anderen Geräten übernommen werden.

Ich kann sogar sehen, wie sich alle Cursor bewegen. Der Cursor des Pixelbook bewegt sich gleichmäßig, da er über ein Touchpad gesteuert wird, aber der Cursor des Pixel 3a-Smartphones und des Tablet-Cursors des iPad Pro springen herum, da ich diese Geräte durch Tippen mit dem Finger steuere.

Status von Mitbearbeitern abrufen

Um die Zusammenarbeit in Echtzeit zu verbessern, wird sogar ein System zur Erkennung von Inaktivität ausgeführt. Der Cursor auf dem 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 mich in der Excalidraw-App bin, aber einfach nichts, wird mir der Cursor als inaktiv angezeigt, symbolisiert durch drei ZZZs.

Begeisterte Leser unserer Publikationen neigen möglicherweise zu der Annahme, dass die Erkennung von Inaktivität über die Idle Detection API realisiert wird. Dies ist ein Vorschlag in der Frühphase, an dem im Rahmen des Projekts Fugu gearbeitet wurde. Achtung, Spoiler: Das stimmt nicht. Während wir in Excalidraw eine Implementierung hatten, die auf dieser API basiert, haben wir uns schließlich für einen traditionelleren Ansatz entschieden, der auf der Messung der Zeigerbewegung und der Seitensichtbarkeit basiert.

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

Wir haben Feedback dazu eingereicht, warum die Idle Detection API unseren Anwendungsfall nicht gelöst hat. Alle APIs des Project Fugu werden offen entwickelt, sodass jeder seine Meinung hören und sich einbringen kann.

Lipis auf das, was Excalidraw zurückhält

Apropos, ich habe Lipis eine letzte Frage zu dem gestellt, was seiner Meinung nach auf der Webplattform fehlt, die 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 in meiner Dropbox oder in Google Drive und nicht auf der Festplatte. Ich wünschte, die File System Access API würde eine Abstraktionsschicht für Remote-Dateisystemanbieter wie Dropbox oder Google enthalten, in die sie integriert werden können und für die Entwickler Code erstellen können. Nutzer können sich dann entspannt zurücklehnen und sich darauf verlassen, dass ihre Dateien bei dem Cloud-Anbieter, dem sie vertrauen, sicher sind.

Ich stimme lipis voll und ganz zu. Ich lebe auch in der Cloud. Wir hoffen, dass dies bald umgesetzt wird.

Anwendungsmodus mit Tabs

Wow! Wir haben viele tolle API-Integrationen in Excalidraw gesehen. Dateisystem, Dateiverarbeitung, Zwischenablage, Webfreigabe und Webfreigabeziel. Aber hier ist noch eine Sache. Bisher konnte ich immer nur ein Dokument auf einmal bearbeiten. Doch das ist nicht mehr der Fall. Sehen Sie sich zum ersten Mal eine frühe Version des Anwendungsmodus mit Tabs in Excalidraw an. So sieht es 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 gewöhnlicher Browsertab, sondern ein PWA-Tab. Auf diesem neuen Tab kann ich dann eine sekundäre Datei öffnen und unabhängig vom selben App-Fenster daran arbeiten.

Der Anwendungsmodus mit Tabs befindet sich noch in der Anfangsphase und nicht alles ist in Stein gemeißelt. Wenn Sie daran interessiert sind, finden Sie in meinem Artikel Informationen zum aktuellen Status dieser Funktion.

Closing

Sehen Sie sich auch unseren Fugu API-Tracker an, um über diese und andere Funktionen auf dem Laufenden zu bleiben. Wir freuen uns darauf, das Web voranzubringen und Ihnen auf der Plattform mehr Möglichkeiten zu bieten. Auf ein ständig verbessertes Excalidraw und auf all die wundervollen Anwendungen, die Sie entwickeln werden. Erstellen Sie auf excalidraw.com eigene Inhalte.

Ich kann es kaum erwarten, einige der APIs, die ich heute vorgestellt habe, in deinen Apps zu sehen. Mein Name ist Tom. Sie finden mich als @tomayac auf Twitter und im Internet allgemein. Vielen Dank fürs Zusehen. Viel Spaß beim Rest der Google I/O.