Excalidraw ve Fugu: Temel Kullanıcı Yolculuklarını İyileştirme

Yeterince gelişmiş bir teknoloji, sihirle ayırt edilemez. Anlamadığınız sürece. Ben Thomas Steiner. Google'da geliştirici ilişkileri alanında çalışıyorum. Google I/O'daki konuşmamın bu yazılı versiyonunda, yeni Fugu API'lerinden bazılarına ve bunların Excalidraw PWA'daki temel kullanıcı yolculuklarını nasıl iyileştirdiğine bakacağız. Böylece bu fikirlerden ilham alarak kendi uygulamalarınıza uygulayabilirsiniz.

Excalidraw'a nasıl başladım?

Bir hikayeyle başlamak istiyorum. 1 Ocak 2020'de Facebook'ta yazılım mühendisi olarak çalışan Christopher Chedeau, üzerinde çalışmaya başladığı küçük bir çizim uygulaması hakkında tweet attı. Bu araçla, karikatür ve elle çizilmiş gibi görünen kutular ve oklar çizebilirsiniz. Ertesi gün, elips ve metin çizebilir, nesneleri seçip taşıyabilirsiniz. 3 Ocak'ta Excalidraw adını alan uygulama, her iyi yan projede olduğu gibi alan adının satın alınmasıyla başladı. Artık renkleri kullanabilir ve çizimin tamamını PNG olarak dışa aktarabilirsiniz.

Excalidraw prototip uygulamasının dikdörtgenleri, okları, elipsleri ve metinleri desteklediğini gösteren ekran görüntüsü.

Christopher, 15 Ocak'ta blog yayını yayınladı. Bu yayın, Twitter'da (benim de dahil olduğum) birçok kullanıcının dikkatini çekti. Gönderi, bazı etkileyici istatistiklerle başlıyordu:

  • 12 bin tekil etkin kullanıcı
  • GitHub'da 1.500 yıldız
  • 26 katkıda bulunan

Sadece iki hafta önce başlayan bir proje için bu hiç de fena değil. Ancak asıl ilgimi çeken şey, gönderinin daha aşağısında yer alıyordu. Christopher, bu sefer yeni bir şey denediğini yazdı: Çekme isteği gönderen herkese koşulsuz commit erişimi verme. Blog yayınını okuduğum gün, bir kullanıcının gönderdiği özellik isteğini düzelterek Excalidraw'a File System Access API desteği ekleyen bir çekme isteği gönderdim.

Kendi PR'ımı duyurduğum tweet'in ekran görüntüsü.

Çekme isteğim bir gün sonra birleştirildi ve o andan itibaren tam commit erişimine sahip oldum. Gücümü kötüye kullanmadığımı söylememe gerek yok. Şu ana kadar 149 katkıda bulunan başka kimse de yapmadı.

Excalidraw, bugün çevrimdışı destek, çarpıcı bir koyu mod ve evet, File System Access API sayesinde dosyaları açma ve kaydetme özelliği sunan, tam teşekküllü bir yüklenebilir ilerleyici web uygulamasıdır.

Excalidraw PWA'nın bugünkü durumunun ekran görüntüsü.

Lipis, zamanının büyük bir kısmını neden Excalidraw'a ayırdığını açıklıyor

Böylece "Excalidraw'a nasıl geldim?" hikayemin sonuna gelmiş oluyorum. Ancak Excalidraw'ın bazı harika özelliklerine geçmeden önce Panayiotis'i tanıtmak istiyorum. İnternette lipis olarak bilinen Panayiotis Lipiridis, Excalidraw'a en çok katkıda bulunan kişidir. lipis'e zamanının büyük bir kısmını Excalidraw'a ayırmasına neden olan motivasyon kaynağını sordum:

Bu proje hakkında herkes gibi ben de Christopher'ın tweet'inden bilgi edindim. İlk katkım, bugün hâlâ Excalidraw'da kullanılan renkleri içeren Open Color kitaplığını eklemek oldu. Proje büyüdükçe ve istekler arttıkça bir sonraki büyük katkım, kullanıcıların çizimlerini paylaşabilmesi için çizimleri depolayacak bir arka uç oluşturmak oldu. Ancak beni gerçekten katkıda bulunmaya iten şey, Excalidraw'ı deneyen herkesin onu tekrar kullanmak için bahane arıyor olması.

lipis'e tamamen katılıyorum. Excalidraw'ı deneyen herkes, tekrar kullanmak için bahane arıyor.

Excalidraw'ın kullanılma şekli

Şimdi size Excalidraw'ı pratikte nasıl kullanabileceğinizi göstermek istiyorum. Çok iyi bir sanatçı değilim ama Google I/O logosu yeterince basit. O yüzden denemek istiyorum. Kutu "i", çizgi eğik çizgi, "o" ise daire olabilir. Mükemmel bir daire elde etmek için Shift tuşunu basılı tutuyorum. Daha iyi görünmesi için eğik çizgiyi biraz kaydırayım. Şimdi "i" ve "o" harflerine biraz renk katın. Mavi iyidir. Belki de farklı bir dolgu stili? Tamamı dolu mu yoksa çapraz çizgili mi? Hayır, tarama harika görünüyor. Mükemmel olmasa da Excalidraw'ın amacı bu olduğu için kaydediyorum.

Kaydet simgesini tıklayıp dosya kaydetme iletişim kutusuna bir dosya adı giriyorum. File System Access API'yi destekleyen bir tarayıcı olan Chrome'da bu işlem indirme değil, gerçek bir kaydetme işlemidir. Bu işlemde dosyanın konumunu ve adını seçebilirim. Ayrıca, düzenleme yaparsam bunları aynı dosyaya kaydedebilirim.

Logoyu değiştirebilir ve "i" harfini kırmızı yapabilir miyim? Şimdi tekrar kaydet'i tıklarsam değişikliğim daha önce olduğu gibi aynı dosyaya kaydediliyor. Kanıt olarak tuvali temizleyip dosyayı yeniden açacağım. Gördüğünüz gibi, değiştirilmiş kırmızı-mavi logo tekrar görünüyor.

Dosyalarla çalışma

Şu anda File System Access API'yi desteklemeyen tarayıcılarda her kaydetme işlemi bir indirme işlemidir. Bu nedenle, değişiklik yaptığımda dosya adında artan bir sayı bulunan birden fazla dosya oluşur ve bu dosyalar İndirilenler klasörümü doldurur. Ancak bu dezavantaja rağmen dosyayı yine de kaydedebiliyorum.

Dosyaları açma

Peki bu işin sırrı ne? Dosya Sistemi Erişimi API'sini destekleyebilen veya desteklemeyen farklı tarayıcılarda açma ve kaydetme nasıl çalışır? Excalidraw'da bir dosya açma işlemi, loadFromJSON)( adlı bir işlevde gerçekleşir. Bu işlev de fileOpen() adlı bir işlevi çağırır.

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);
};

Excalidraw'da kullandığımız browser-fs-access adlı küçük bir kitaplıktan gelen fileOpen() işlevi. Bu kitaplık, File System Access API aracılığıyla dosya sistemi erişimi sağlar ve eski bir geri dönüşüm özelliği içerir. Bu nedenle, herhangi bir tarayıcıda kullanılabilir.

Öncelikle API'nin desteklendiği durumdaki uygulamayı göstereyim. Kabul edilen MIME türleri ve dosya uzantıları üzerinde anlaşmaya varıldıktan sonra, File System Access API'nin showOpenFilePicker() işlevini çağırmak merkezi bir adım olur. Bu işlev, birden fazla dosya seçilip seçilmediğine bağlı olarak bir dosya dizisi veya tek bir dosya döndürür. Geriye kalan tek şey, dosya nesnesine dosya tutamacını yerleştirmektir. Böylece dosya tekrar alınabilir.

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;
  };
};

Yedek uygulama, "file" türünde bir input öğesine dayanır. Kabul edilecek MIME türleri ve uzantılarla ilgili görüşmelerin ardından, dosya açma iletişim kutusunun gösterilmesi için giriş öğesinin programatik olarak tıklanması gerekir. Değişiklik olduğunda, yani kullanıcı bir veya daha fazla dosya seçtiğinde söz yerine getirilir.

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();
  });
};

Dosyaları kaydetme

Şimdi de kaydetme işlemine geçelim. Excalidraw'da kaydetme işlemi saveAsJSON() adlı bir işlevde gerçekleşir. Öncelikle Excalidraw öğeleri dizisini JSON'a serileştirir, JSON'ı blob'a dönüştürür ve ardından fileSave() adlı bir işlevi çağırır. Bu işlev de browser-fs-access kitaplığı tarafından sağlanır.

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 };
};

Öncelikle File System Access API desteği olan tarayıcılardaki uygulamaya bakalım. İlk birkaç satır biraz karmaşık görünüyor ancak bu satırlarda yalnızca MIME türleri ve dosya uzantıları üzerinde anlaşma yapılıyor. Daha önce kaydettiğim ve zaten bir dosya tutma noktasına sahip olduğum durumlarda kaydetme iletişim kutusunun gösterilmesi gerekmez. Ancak bu ilk kaydetme işlemidirse bir dosya iletişim kutusu gösterilir ve uygulama, gelecekte kullanılmak üzere bir dosya tutacağı alır. Geriye kalan kısım ise yazılabilir akış aracılığıyla dosyaya yazma işlemidir.

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;
};

"Farklı kaydet" özelliği

Mevcut bir dosya tutamacını yoksaymaya karar verirsem mevcut bir dosyaya dayalı yeni bir dosya oluşturmak için "Farklı kaydet" özelliğini uygulayabilirim. Bunu göstermek için mevcut bir dosyayı açıp bazı değişiklikler yapacağım ve ardından mevcut dosyanın üzerine yazmayıp farklı kaydet özelliğini kullanarak yeni bir dosya oluşturacağım. Bu işlem, orijinal dosyayı olduğu gibi bırakır.

Dosya Sistemi Erişim API'sini desteklemeyen tarayıcılar için uygulama kısadır. Çünkü bu uygulama, değeri istenen dosya adı olan bir download özelliği ve href özellik değeri olarak bir blob URL'si içeren bir bağlantı öğesi oluşturmaktan başka bir şey yapmaz.

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();
};

Bağlayıcı öğe daha sonra programatik olarak tıklanır. Bellek sızıntılarını önlemek için blob URL'sinin kullanımdan sonra iptal edilmesi gerekir. Bu işlem yalnızca bir indirme olduğundan hiçbir zaman dosya kaydetme iletişim kutusu gösterilmez ve tüm dosyalar varsayılan Downloads klasörüne indirilir.

Sürükle ve bırak

Masaüstünde en sevdiğim sistem entegrasyonlarından biri sürükle ve bırak özelliğidir. Excalidraw'da bir .excalidraw dosyasını uygulamaya bıraktığımda dosya hemen açılıyor ve düzenlemeye başlayabiliyorum. File System Access API'yi destekleyen tarayıcılarda değişikliklerimi anında kaydedebilirim. Gerekli dosya işleyicisi sürükle ve bırak işleminden alındığından dosya kaydetme iletişim kutusuna gitmenize gerek yoktur.

Bunun gerçekleşmesini sağlayan gizli anahtar, File System Access API desteklendiğinde veri aktarımı öğesinde getAsFileSystemHandle() işlevini çağırmaktır. Ardından, bu dosya işleyicisini birkaç paragraf yukarıda bahsettiğimiz loadFromBlob()'ya iletiyorum. Dosyalarla yapabileceğiniz pek çok şey var: açma, kaydetme, üzerine kaydetme, sürükleme, bırakma. Meslektaşım Pete ile birlikte tüm bu ipuçlarını ve daha fazlasını makalemizde derledik. Böylece, tüm bu bilgiler biraz hızlı aktarıldıysa makalemizi okuyarak eksiklerinizi tamamlayabilirsiniz.

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 });
  });
}

Dosyaları paylaşma

Android, ChromeOS ve Windows'da şu anda kullanılan bir diğer sistem entegrasyonu ise Web Share Target API aracılığıyla sağlanır. Dosyalar uygulamasında Downloads klasöründeyim. Biri untitled gibi açıklayıcı olmayan bir ada ve zaman damgasına sahip iki dosya görüyorum. İçeriğini kontrol etmek için üç noktayı, ardından paylaş'ı tıklıyorum ve görünen seçeneklerden biri Excalidraw oluyor. Simgeye dokunduğumda dosyanın yalnızca I/O logosunu içerdiğini görüyorum.

Lipis, desteği sonlandırılan Electron sürümünde

Henüz bahsetmediğim bir şey daha var: Dosyalara çift tıklayabilirsiniz. Bir dosyayı çift tıkladığınızda genellikle dosyanın MIME türüyle ilişkili uygulama açılır. Örneğin, .docx için bu uygulama Microsoft Word olur.

Excalidraw'ın, bu tür dosya türü ilişkilendirmelerini destekleyen Electron sürümü vardı. Bu nedenle, bir .excalidraw dosyasını çift tıkladığınızda Excalidraw Electron uygulaması açılırdı. Daha önce tanıştığınız Lipis, Excalidraw Electron'un hem geliştiricisi hem de desteğini sonlandıran kişidir. Ona Electron sürümünün desteğini sonlandırmanın neden mümkün olduğunu düşündüğünü sordum:

Kullanıcılar, özellikle dosyaları çift tıklayarak açmak istedikleri için başından beri Electron uygulaması talep ediyor. Ayrıca uygulamayı uygulama mağazalarına koymayı da planlıyorduk. Aynı zamanda bir kullanıcı, bunun yerine PWA oluşturmamızı önerdi. Biz de ikisini birden yaptık. Neyse ki dosya sistemi erişimi, pano erişimi ve dosya işleme gibi Project Fugu API'leriyle tanıştık. Tek bir tıklamayla, Electron'un ek yükü olmadan uygulamayı masaüstünüze veya mobil cihazınıza yükleyebilirsiniz. Electron sürümünün desteğini sonlandırmak, yalnızca web uygulamasına odaklanmak ve onu mümkün olan en iyi PWA haline getirmek kolay bir karardı. Ayrıca, artık PWA'ları Play Store ve Microsoft Store'da yayınlayabiliyoruz. Bu çok büyük bir başarı.

Electron kötü olduğu için değil, web yeterince iyi hale geldiği için Excalidraw for Electron'un desteğinin sonlandırıldığını söyleyebiliriz. Bunu beğendim.

Dosya işleme

"Web yeterince iyi hale geldi" derken, yaklaşan File Handling gibi özelliklerden bahsediyorum.

Bu, normal bir macOS Big Sur yüklemesidir. Şimdi bir Excalidraw dosyasını sağ tıkladığımda ne olduğuna bakalım. Yüklü PWA olan Excalidraw ile açmayı seçebilirim. Elbette çift tıklama da işe yarar ancak ekran kaydında bu yöntemi göstermek daha az etkileyicidir.

Peki, bu nasıl çalışır? İlk adım, uygulamamın işleyebileceği dosya türlerini işletim sistemine bildirmektir. Bunu, web uygulaması manifest dosyasında file_handlers adlı yeni bir alanda yapıyorum. Değeri, bir işlem ve accept özelliği içeren nesneler dizisidir. İşlem, işletim sisteminin uygulamanızı başlattığı URL yolunu belirler. Kabul etme nesnesi, MIME türlerinin ve ilişkili dosya uzantılarının anahtar/değer çiftleridir.

{
  "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"]
      }
    }
  ]
}

Bir sonraki adım, uygulama başlatıldığında dosyayı işlemektir. Bu, launchQueue arayüzünde, setConsumer()'ı arayarak bir tüketici ayarlamam gerektiğinde gerçekleşir. Bu işlevin parametresi, launchParams alanını alan bir asenkron işlevdir. Bu launchParams nesnesinde üzerinde çalışabileceğim bir dosya işleyicileri dizisi döndüren dosyalar adlı bir alan var. Yalnızca ilkini önemsiyorum ve bu dosya işleyiciden bir blob alıp eski dostumuz loadFromBlob()'a iletiyorum.

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 });
      });
    });
}

Bu bölümü hızlı geçtiyseniz Dosya İşleme API'si hakkında daha fazla bilgiyi makalemde bulabilirsiniz. Deneysel web platformu özellikleri işaretini ayarlayarak dosya işlemeyi etkinleştirebilirsiniz. Bu özellik, bu yılın ilerleyen dönemlerinde Chrome'da kullanıma sunulacak.

Pano entegrasyonu

Excalidraw'ın bir diğer harika özelliği de pano entegrasyonudur. Çizimimin tamamını veya yalnızca bir kısmını panoya kopyalayabilir, istersem filigran ekleyebilir ve ardından başka bir uygulamaya yapıştırabilirim. Bu arada, bu uygulama Windows 95'teki Paint uygulamasının web sürümüdür.

Bu özelliğin çalışma şekli şaşırtıcı derecede basittir. Tek ihtiyacım olan, blob olarak tuval. Ardından, ClipboardItem ile blob içeren tek öğeli bir dizi ileterek navigator.clipboard.write() işlevine tuvali panoya yazıyorum. Pano API'si ile yapabilecekleriniz hakkında daha fazla bilgi için Jason'ın ve benim makaleme göz atın.

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);
    }
  });
};

Diğer kullanıcılarla ortak çalışma

Oturum URL'sini paylaşma

Excalidraw'da ortak çalışma modu olduğunu biliyor muydunuz? Farklı kişiler aynı doküman üzerinde birlikte çalışabilir. Yeni bir oturum başlatmak için canlı ortak çalışma düğmesini tıklayıp oturum başlatıyorum. Excalidraw'ın entegre ettiği Web Share API sayesinde oturum URL'sini ortak çalışanlarımla kolayca paylaşabiliyorum.

Canlı ortak çalışma

Pixelbook'umda, Pixel 3a telefonumda ve iPad Pro'mda Google I/O logosu üzerinde çalışarak yerel olarak bir ortak çalışma oturumu simüle ettim. Bir cihazda yaptığım değişikliklerin diğer tüm cihazlara yansıdığını görebiliyorum.

Hatta tüm imleçlerin hareket ettiğini görebiliyorum. Pixelbook'un imleci, izleme paneliyle kontrol edildiği için sabit bir şekilde hareket ediyor. Ancak Pixel 3a telefonun imleci ve iPad Pro'nun tablet imleci, bu cihazları parmağımla dokunarak kontrol ettiğim için zıplıyor.

Düzenleyen kişilerin durumlarını görme

Gerçek zamanlı ortak çalışma deneyimini iyileştirmek için bir boşta kalma algılama sistemi bile çalıştırılıyor. iPad Pro'nun imleci, kullanırken yeşil nokta gösteriyor. Farklı bir tarayıcı sekmesine veya uygulamaya geçtiğimde nokta siyah oluyor. Excalidraw uygulamasında olup hiçbir şey yapmadığımda ise imleç beni boşta olarak gösteriyor. Bu durum üç adet zZZ harfiyle sembolize ediliyor.

Yayınlarımızı yakından takip edenler, boşta kalma algılamanın Project Fugu kapsamında üzerinde çalışılan erken aşama bir teklif olan Idle Detection API aracılığıyla gerçekleştirildiğini düşünebilir. Spoiler uyarısı: Bu doğru değil. Excalidraw'da bu API'ye dayalı bir uygulama olsa da sonunda işaretçi hareketini ve sayfa görünürlüğünü ölçmeye dayalı daha geleneksel bir yaklaşımı tercih ettik.

WICG Idle Detection deposunda gönderilen boşta algılama geri bildiriminin ekran görüntüsü.

Boşta Kalma Algılama API'sinin, sahip olduğumuz kullanım alanını neden çözmediğiyle ilgili geri bildirimde bulunduk. Tüm Project Fugu API'leri açık bir şekilde geliştirilmektedir. Bu nedenle herkes görüşlerini paylaşabilir ve sesini duyurabilir.

Lipis, Excalidraw'ı neyin engellediği hakkında

Bu bağlamda, lipis'e web platformunda Excalidraw'ın gelişimini engelleyen eksiklikler hakkında son bir soru sordum:

File System Access API harika, ama biliyor musunuz? Günümüzde önemsediğim çoğu dosya sabit diskimde değil, Dropbox veya Google Drive'ımda bulunuyor. Dosya Sistemi Erişimi API'sinin, Dropbox veya Google gibi uzak dosya sistemi sağlayıcılarının entegre olabileceği ve geliştiricilerin kod yazabileceği bir soyutlama katmanı içermesini istiyorum. Kullanıcılar, güvendikleri bulut sağlayıcı ile dosyalarının güvende olduğunu bilerek rahatlayabilir.

lipis'e tamamen katılıyorum, ben de bulutta yaşıyorum. Bu özelliğin kısa süre içinde kullanıma sunulmasını umuyoruz.

Sekmeli uygulama modu

İnanılmaz! Excalidraw'da birçok harika API entegrasyonu gördük. Dosya sistemi, dosya işleme, pano, web paylaşımı ve web paylaşımı hedefi. Ancak bir şey daha var. Şimdiye kadar tek seferde yalnızca bir dokümanı düzenleyebiliyordum. Artık sorun değil. Excalidraw'da sekmeli uygulama modunun erken sürümünü ilk kez kullanmanın keyfini çıkarın. Bu şekilde görünür.

Yüklü Excalidraw PWA'da bağımsız modda çalışan mevcut bir dosya açık. Şimdi bağımsız pencerede yeni bir sekme açıyorum. Bu, normal bir tarayıcı sekmesi değil, PWA sekmesidir. Bu yeni sekmede ikincil bir dosya açabilir ve aynı uygulama penceresinde bu dosyalar üzerinde bağımsız olarak çalışabilirim.

Sekmeli uygulama modu erken aşamadadır ve her şey kesinleşmemiştir. İlgileniyorsanız makalemi okuyarak bu özelliğin mevcut durumu hakkında bilgi edinebilirsiniz.

Kapanış

Bu ve diğer özelliklerle ilgili güncel bilgilerden haberdar olmak için Fugu API izleyicimizi takip edin. Web'i ileriye taşımak ve platformda daha fazlasını yapmanızı sağlamak için çok heyecanlıyız. Excalidraw'ın sürekli gelişmesi ve oluşturacağınız tüm harika uygulamalar için. excalidraw.com adresine giderek oluşturmaya başlayın.

Bugün gösterdiğim API'lerden bazılarının uygulamalarınızda görünmesini dört gözle bekliyorum. Adım Tom. Beni Twitter'da @tomayac kullanıcı adıyla ve genel olarak internette bulabilirsiniz. İzlediğiniz için çok teşekkür ederiz. Google I/O'nun geri kalanını keyifle izlemenizi dileriz.