Exalidraw và Fugu: Cải thiện hành trình cốt lõi của người dùng

Bất kỳ công nghệ tiên tiến nào cũng không thể phân biệt được với phép thuật. Trừ phi bạn hiểu rõ. Tôi là Thomas Steiner, làm việc tại bộ phận Quan hệ nhà phát triển tại Google và trong bản viết bài cho buổi trò chuyện tại Google I/O, tôi sẽ xem xét một số API Fugu mới cũng như cách chúng cải thiện hành trình cốt lõi của người dùng trong PWA Excalidraw, để bạn có thể lấy cảm hứng từ các ý tưởng này và áp dụng vào ứng dụng của riêng mình.

Cách tôi đến với Excalidraw

Tôi muốn bắt đầu bằng một câu chuyện. Vào ngày 1 tháng 1 năm 2020, Christopher Chedeau, một kỹ sư phần mềm tại Facebook, đã nhắc nhở về một ứng dụng vẽ nhỏ mà anh đã bắt tay phát triển. Với công cụ này, bạn có thể vẽ các hộp và mũi tên trông như hoạt hình và vẽ tay. Ngày tiếp theo, bạn cũng có thể vẽ hình elip và văn bản, cũng như chọn các đối tượng và di chuyển chúng xung quanh. Vào ngày 3 tháng 1, ứng dụng có tên là Excalidraw và giống như mọi dự án phụ tốt, việc mua tên miền là một trong những hành động đầu tiên của Christopher. Hiện tại, bạn có thể sử dụng màu và xuất toàn bộ bản vẽ dưới dạng tệp PNG.

Ảnh chụp màn hình ứng dụng nguyên mẫu Excalidraw cho thấy ứng dụng có hỗ trợ hình chữ nhật, mũi tên, dấu ba chấm và văn bản.

Vào ngày 15 tháng 1, Christopher đăng một bài đăng trên blog thu hút được nhiều sự chú ý trên Twitter, bao gồm cả của tôi. Bài đăng mở đầu với một số số liệu thống kê ấn tượng:

  • 12 nghìn người dùng đang hoạt động riêng biệt
  • 1,5 nghìn sao trên GitHub
  • 26 cộng tác viên

Đối với một dự án mới bắt đầu cách đây chưa đầy 2 tuần, thì quả thực chẳng kém. Nhưng điều thực sự làm tôi quan tâm nằm sâu hơn ở bài đăng đó. Christopher viết rằng lần này anh đã thử một cách mới: cung cấp cho những người đã đưa ra yêu cầu lấy dữ liệu quyền truy cập vô điều kiện. Cùng ngày đọc bài đăng trên blog này, tôi nhận được yêu cầu kéo lên và thêm tính năng hỗ trợ API Truy cập hệ thống tệp vào Excalidraw, khắc phục yêu cầu về tính năng mà ai đó đã gửi.

Ảnh chụp màn hình bài đăng trên Twitter trong đó tôi công bố hoạt động PR của mình.

Yêu cầu lấy dữ liệu của tôi được hợp nhất một ngày sau đó và kể từ đó, tôi có toàn quyền truy cập cam kết. Không cần phải nói, tôi không lạm dụng sức mạnh của mình. Ngoài ra, không có ai khác trong số 149 người đóng góp cho đến thời điểm này.

Hiện nay, Excalidraw là một ứng dụng web tiến bộ có thể cài đặt hoàn chỉnh, có hỗ trợ ngoại tuyến, chế độ tối tuyệt đẹp và khả năng mở và lưu tệp nhờ API Truy cập hệ thống tệp.

Ảnh chụp màn hình của PWA Excalidraw ở trạng thái hiện tại.

Lipis chia sẻ lý do anh dành nhiều thời gian cho trò chơi Excalidraw

Đây là phần cuối trong câu chuyện "cách tôi đến với Excalidraw", nhưng trước khi tìm hiểu kỹ hơn về một số tính năng thú vị của Excalidraw, tôi rất hân hạnh được giới thiệu Panayiotis. Panayiotis Lipiridis, trên Internet chỉ được gọi là lipis, là người đóng góp nhiều nhất vào Excalidraw. Tôi hỏi lipis điều gì thúc đẩy anh dành nhiều thời gian như vậy cho Excalidraw:

Giống như mọi người khác mà tôi đã biết về dự án này qua tweet của Christopher. Nội dung đóng góp đầu tiên của tôi là thêm thư viện Màu mở, màu sắc vẫn là một phần của Excalidraw hiện nay. Khi dự án phát triển và chúng tôi nhận được khá nhiều yêu cầu, đóng góp lớn tiếp theo của tôi là xây dựng một phần phụ trợ dùng để lưu trữ các bản vẽ để người dùng có thể chia sẻ. Nhưng điều thực sự thôi thúc tôi đóng góp là những ai đã thử Excalidraw đều muốn tìm lý do để sử dụng lại công cụ này.

Tôi hoàn toàn đồng ý với lipis. Những người đã thử Excalidraw đều đang tìm cách bào chữa để sử dụng lại nó.

Hoạt động của exalidraw trong thực tế

Bây giờ, tôi muốn cho bạn thấy cách bạn có thể sử dụng Excalidraw trong thực tế. Tôi không phải là một nghệ sĩ tài năng nhưng biểu trưng Google I/O khá đơn giản, vì vậy hãy để tôi dùng thử. Một ô là chữ "i", một đường kẻ có thể là dấu gạch chéo và "o" là một hình tròn. Tôi nhấn giữ phím shift để có được một vòng tròn hoàn hảo. Hãy để tôi di chuyển dấu gạch chéo một chút để nó trông đẹp hơn. Bây giờ là một số màu cho chữ "i" và "o". Màu xanh dương rất tốt. Có thể là một kiểu tô màu khác? Tất cả đều ổn định hay nở chéo? Không, hachure trông tuyệt đấy. Nó chưa hoàn hảo, nhưng đó là ý tưởng của Excalidraw, vì vậy hãy để tôi lưu nó.

Tôi nhấp vào biểu tượng lưu và nhập tên tệp vào hộp thoại lưu tệp. Trong Chrome, một trình duyệt có hỗ trợ API Truy cập hệ thống tệp, đây không phải là hoạt động tải xuống mà là thao tác lưu thực sự, nơi tôi có thể chọn vị trí và tên tệp cũng như trường hợp mà nếu chỉnh sửa thì tôi chỉ cần lưu chúng vào cùng một tệp.

Để tôi thay đổi biểu trưng và chữ "i" trở thành màu đỏ. Nếu bây giờ tôi nhấp lại vào Lưu, nội dung sửa đổi của tôi sẽ được lưu vào chính tệp đó như trước đây. Để chứng minh, hãy để tôi xoá canvas rồi mở lại tệp. Như bạn có thể thấy, biểu trưng xanh đỏ đã được sửa đổi xuất hiện trở lại.

Làm việc với tệp

Trên các trình duyệt hiện không hỗ trợ API Truy cập hệ thống tệp, mỗi thao tác lưu là một tệp tải xuống. Vì vậy, khi thực hiện thay đổi, tôi sẽ nhận được nhiều tệp có số tăng dần trong tên tệp sẽ lấp đầy thư mục Tệp đã tải xuống. Nhưng bất chấp nhược điểm này, tôi vẫn có thể lưu tệp.

Mở tệp

Vậy bí mật là gì? Làm cách nào để thao tác mở và lưu hoạt động trên nhiều trình duyệt có thể hỗ trợ hoặc không hỗ trợ API Truy cập hệ thống tệp? Thao tác mở tệp trong Excalidraw sẽ diễn ra trong một hàm có tên là loadFromJSON)(. Hàm này sẽ gọi một hàm có tên là fileOpen().

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

Hàm fileOpen() đến từ một thư viện nhỏ mà tôi đã viết có tên là browser-fs-access (trình duyệt-fs-access) mà chúng ta sử dụng trong Excalidraw. Thư viện này cung cấp quyền truy cập vào hệ thống tệp thông qua API Truy cập hệ thống tệp bằng một bản dự phòng cũ để có thể dùng trong mọi trình duyệt.

Trước tiên, tôi sẽ cho bạn thấy cách triển khai khi API được hỗ trợ. Sau khi thương lượng các loại MIME và đuôi tệp được chấp nhận, phần chính là gọi hàm showOpenFilePicker() của API Truy cập hệ thống tệp. Hàm này trả về một mảng tệp hoặc một tệp, tuỳ thuộc vào việc có nhiều tệp được chọn hay không. Bạn chỉ cần đặt tên người dùng tệp vào đối tượng tệp để có thể truy xuất lại tệp.

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

Việc triển khai dự phòng dựa trên phần tử input thuộc loại "file". Sau khi thương lượng các loại và tiện ích MIME được chấp nhận, bước tiếp theo là nhấp vào phần tử đầu vào theo cách lập trình để hộp thoại mở tệp hiển thị. Khi thay đổi, tức là khi người dùng đã chọn một hoặc nhiều tệp, lời hứa sẽ được giải quyết.

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

Đang lưu tệp

Bây giờ là lưu. Trong Excalidraw, quá trình lưu diễn ra trong một hàm có tên là saveAsJSON(). Trước tiên, phương thức này chuyển đổi tuần tự mảng các phần tử Excalidraw thành JSON, chuyển đổi JSON thành một blob, sau đó gọi một hàm có tên là fileSave(). Hàm này cũng do thư viện browser-fs-access cung cấp.

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

Một lần nữa, hãy cho phép tôi xem xét cách triển khai cho các trình duyệt có hỗ trợ API Truy cập hệ thống tệp. Một vài dòng đầu tiên có vẻ hơi liên quan, nhưng tất cả những gì chúng làm là thương lượng loại MIME và phần mở rộng tệp. Khi tôi đã lưu trước đó và đã có trình xử lý tệp, thì không cần hộp thoại lưu nào xuất hiện. Tuy nhiên, nếu đây là lần lưu đầu tiên, hộp thoại tệp sẽ xuất hiện và ứng dụng sẽ nhận lại bộ xử lý tệp để sử dụng sau này. Công việc còn lại sau đó sẽ là ghi vào tệp, diễn ra thông qua một luồng có thể ghi.

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

Tính năng "lưu dưới dạng"

Nếu quyết định bỏ qua một tên người dùng tệp hiện có, tôi có thể triển khai tính năng "lưu dưới dạng" để tạo tệp mới dựa trên tệp hiện có. Để hiển thị nội dung này, hãy để tôi mở một tệp hiện có, thực hiện một số sửa đổi và sau đó không ghi đè tệp hiện có, mà tạo một tệp mới bằng cách sử dụng tính năng lưu dưới dạng. Thao tác này sẽ giữ nguyên tệp gốc.

Quá trình triển khai cho các trình duyệt không hỗ trợ API Truy cập hệ thống tệp là rất ngắn, vì tất cả chỉ cần tạo một phần tử liên kết với thuộc tính download có giá trị là tên tệp mong muốn và URL blob làm giá trị thuộc tính href.

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

Sau đó, hệ thống sẽ nhấp vào phần tử liên kết theo phương thức lập trình. Để tránh rò rỉ bộ nhớ, bạn cần thu hồi URL của blob sau khi sử dụng. Do đây chỉ là tệp tải xuống nên sẽ không có hộp thoại lưu tệp nào hiển thị và tất cả tệp đều nằm trong thư mục Downloads mặc định.

Kéo và thả

Một trong những công cụ tích hợp hệ thống yêu thích của tôi trên máy tính để bàn là kéo và thả. Trong Excalidraw, khi tôi thả một tệp .excalidraw vào ứng dụng, tệp đó sẽ mở ra ngay lập tức và tôi có thể bắt đầu chỉnh sửa. Trên các trình duyệt hỗ trợ API Truy cập hệ thống tệp, tôi thậm chí có thể lưu ngay các thay đổi của mình. Bạn không cần phải chuyển qua hộp thoại lưu tệp vì thao tác kéo và thả tệp đã lấy tên người dùng tệp cần thiết.

Bí quyết để thực hiện việc này là bằng cách gọi getAsFileSystemHandle() trên mục chuyển dữ liệu khi API Truy cập hệ thống tệp được hỗ trợ. Sau đó, tôi sẽ chuyển phần điều khiển tệp này đến loadFromBlob() mà bạn có thể nhớ từ một vài đoạn ở trên. Bạn có thể làm rất nhiều việc với tệp: mở, lưu, lưu quá mức, kéo, thả. Đồng nghiệp của tôi là Pete và tôi đã ghi lại tất cả các thủ thuật này và nhiều nội dung khác trong bài viết của chúng tôi để bạn có thể nắm bắt thông tin phòng trường hợp mọi việc diễn ra quá nhanh.

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

Chia sẻ tệp

Một công cụ tích hợp hệ thống khác hiện có trên Android, ChromeOS và Windows là thông qua Web Share Target API. Tôi đang ở trong ứng dụng Files trong thư mục Downloads. Tôi có thể thấy hai tệp, một trong số đó có tên không phải là untitled và có dấu thời gian. Để kiểm tra nội dung, tôi nhấp vào ba dấu chấm rồi chia sẻ và một trong các tuỳ chọn xuất hiện là Excalidraw. Khi nhấn vào biểu tượng này, tôi có thể thấy rằng tệp lại chỉ chứa biểu trưng I/O.

Lipis trên phiên bản electron không dùng nữa

Một việc bạn có thể làm với các tệp mà tôi chưa nói đến là nhân đôi các tệp đó. Điều thường xảy ra khi bạn sao chép một tệp là ứng dụng liên kết với loại MIME của tệp sẽ mở ra. Ví dụ: đối với .docx, đây sẽ là Microsoft Word.

exalidraw được sử dụng để có một phiên bản electron của ứng dụng hỗ trợ các mối liên kết loại tệp như vậy, vì vậy, khi bạn nhấp đúp vào một tệp .excalidraw, ứng dụng Excalidraw Electronic sẽ mở ra. Lipis, người mà bạn đã gặp trước đây, vừa là nhà sáng tạo và là chủ sở hữu của Excalidraw Electronic. Tôi hỏi ông về lý do ông cảm thấy có thể không dùng phiên bản Electrictron nữa:

Ngay từ đầu, mọi người đã yêu cầu sử dụng ứng dụng electron, chủ yếu vì họ muốn mở tệp bằng cách nhấp đúp. Chúng tôi cũng có ý định đưa ứng dụng này vào cửa hàng ứng dụng. Đồng thời, có người đề xuất tạo PWA nên chúng tôi vừa làm cả hai. Thật may là chúng tôi được giới thiệu các API Project Fugu như quyền truy cập vào hệ thống tệp, quyền truy cập vào bảng nhớ tạm, xử lý tệp, v.v. Chỉ với một cú nhấp chuột là bạn có thể cài đặt ứng dụng trên máy tính hoặc thiết bị di động mà không cần dùng đến electron. Quyết định dễ dàng được dùng là ngừng sử dụng phiên bản electron, chỉ tập trung vào ứng dụng web và đặt nó làm ứng dụng web tiến bộ (PWA) tốt nhất có thể. Ngoài ra, chúng tôi hiện đã có thể phát hành PWA lên Cửa hàng Play và Cửa hàng Microsoft! Rất nhiều!

Có thể nói rằng Excalidraw dành cho electron không còn được dùng nữa vì electron không tốt chút nào, hoàn toàn không phải vì web đã trở nên tốt. Tôi thích ảnh này!

Xử lý tệp

Khi tôi nói "web đã đủ tốt", thì đó là do các tính năng như Xử lý tệp sắp tới.

Đây là một bản cài đặt macOS Big Sur thông thường. Bây giờ, hãy xem điều gì sẽ xảy ra khi tôi nhấp chuột phải vào một tệp Excalidraw. Tôi có thể chọn mở tệp đó bằng Excalidraw, một ứng dụng web tiến bộ (PWA) đã cài đặt. Dĩ nhiên, thao tác nhấp đúp cũng sẽ mang lại hiệu quả. Việc minh hoạ trong bản ghi màn hình sẽ đỡ kịch tính hơn.

Vậy tính năng này hoạt động như thế nào? Bước đầu tiên là làm cho các loại tệp mà ứng dụng của tôi có thể xử lý để hệ điều hành biết. Tôi thực hiện việc này trong một trường mới có tên là file_handlers trong tệp kê khai ứng dụng web. Giá trị của lớp là một mảng các đối tượng có một thao tác và một thuộc tính accept. Thao tác này xác định đường dẫn URL mà hệ điều hành khởi chạy ứng dụng của bạn và đối tượng chấp nhận là các cặp giá trị khoá của loại MIME và đuôi tệp liên quan.

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

Bước tiếp theo là xử lý tệp khi ứng dụng khởi chạy. Điều này xảy ra trong giao diện launchQueue, nơi tôi cần thiết lập đối tượng tiêu dùng bằng cách gọi setConsumer(). Tham số cho hàm này là một hàm không đồng bộ nhận launchParams. Đối tượng launchParams này có một trường được gọi là tệp cung cấp cho tôi một loạt các trình xử lý tệp để xử lý. Tôi chỉ quan tâm đến phần tử đầu tiên và từ ô điều khiển tệp này, tôi sẽ nhận được một blob mà sau đó tôi truyền cho người bạn cũ loadFromBlob().

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

Xin nhắc lại, nếu quá trình này diễn ra quá nhanh, bạn có thể đọc thêm về API Xử lý tệp trong bài viết của tôi. Bạn có thể bật tính năng xử lý tệp bằng cách đặt cờ tính năng của nền tảng web thử nghiệm. Theo dự kiến, phiên bản này sẽ xuất hiện trên Chrome vào cuối năm nay.

Tích hợp bảng nhớ tạm

Một tính năng thú vị khác của Excalidraw là tích hợp bảng nhớ tạm. Tôi có thể sao chép toàn bộ hoặc chỉ một phần bản vẽ của mình vào bảng nhớ tạm, có thể thêm hình mờ nếu muốn và sau đó dán vào một ứng dụng khác. Nhân tiện, đây là phiên bản web của ứng dụng Windows 95 Paint.

Cách thức hoạt động của tính năng này rất đơn giản. Tất cả những gì tôi cần là canvas ở dạng blob, sau đó tôi ghi vào bảng nhớ tạm bằng cách truyền một mảng một phần tử có ClipboardItem chứa blob vào hàm navigator.clipboard.write(). Để biết thêm thông tin về những việc bạn có thể làm với API bảng nhớ tạm, hãy xem bài viết của Jason và bài viết của tôi.

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

Cộng tác với những người khác

Chia sẻ URL của phiên

Bạn có biết rằng Excalidraw cũng có chế độ cộng tác không? Những người khác nhau có thể làm việc cùng nhau trên cùng một tài liệu. Để bắt đầu một phiên mới, tôi nhấp vào nút cộng tác trực tiếp rồi bắt đầu một phiên. Tôi có thể dễ dàng chia sẻ URL của phiên với các cộng tác viên nhờ API Chia sẻ web mà Excalidraw đã tích hợp.

Cộng tác trực tiếp

Tôi đã mô phỏng một phiên cộng tác trên thiết bị bằng cách thao tác với biểu trưng Google I/O trên Pixelbook, điện thoại Pixel 3a và iPad Pro của tôi. Bạn có thể thấy những thay đổi tôi thực hiện trên một thiết bị được phản ánh trên tất cả các thiết bị khác.

Thậm chí tôi có thể thấy tất cả các con trỏ di chuyển xung quanh. Con trỏ của Pixelbook di chuyển ổn định vì được điều khiển bằng bàn di chuột, nhưng con trỏ của điện thoại Pixel 3a và con trỏ trên máy tính bảng của iPad Pro lại di chuyển ổn định, vì tôi điều khiển các thiết bị này bằng cách nhấn ngón tay.

Xem trạng thái cộng tác viên

Để cải thiện trải nghiệm cộng tác theo thời gian thực, thậm chí còn có cả hệ thống phát hiện trạng thái rảnh đang chạy. Con trỏ của iPad Pro hiển thị một chấm màu xanh lục khi tôi sử dụng. Dấu chấm chuyển sang màu đen khi tôi chuyển sang một thẻ hoặc ứng dụng trình duyệt khác. Và khi tôi đang ở trong ứng dụng Excalidraw nhưng không làm gì cả, con trỏ sẽ hiện tôi ở trạng thái rảnh, được biểu thị bằng ba zZZ.

Những độc giả thích đọc ấn bản của chúng tôi có thể có xu hướng nghĩ rằng tính năng phát hiện trạng thái rảnh được thực hiện thông qua API Phát hiện trạng thái rảnh, một đề xuất ở giai đoạn đầu đã được thực hiện trong bối cảnh của Project Fugu. Cảnh báo tiết lộ nội dung: Không phải vậy. Mặc dù đã triển khai dựa trên API này trong Excalidraw, nhưng cuối cùng, chúng tôi đã quyết định sử dụng phương pháp truyền thống nhiều hơn dựa trên việc đo lường chuyển động của con trỏ và khả năng hiển thị trang.

Ảnh chụp màn hình về phản hồi về tính năng Phát hiện trạng thái rảnh được gửi trong kho lưu trữ tính năng Phát hiện trạng thái rảnh của WICG.

Chúng tôi đã gửi ý kiến phản hồi về lý do API Phát hiện trạng thái rảnh không giải quyết trường hợp sử dụng của chúng tôi. Tất cả các API Project Fugu đều được phát triển công khai, vì vậy, mọi người có thể tham gia và lắng nghe ý kiến của họ!

Lipis đang ngăn cản việc Excalidraw

Nhân lúc nói về vấn đề này, tôi đã hỏi lipis câu hỏi cuối cùng về những gì anh ấy nghĩ đang bị thiếu trên nền tảng web đang giữ Excalidraw:

API Truy cập hệ thống tệp rất tuyệt, nhưng bạn biết chưa? Hầu hết các tệp mà tôi quan tâm ngày nay đều nằm trong Dropbox hoặc Google Drive chứ không phải trên ổ đĩa cứng. Tôi muốn API Truy cập hệ thống tệp sẽ bao gồm một lớp trừu tượng cho các nhà cung cấp hệ thống tệp từ xa như Dropbox hoặc Google để tích hợp và nhà phát triển có thể viết mã dựa trên đó. Sau đó, người dùng có thể thư giãn và biết rằng tệp của mình an toàn với nhà cung cấp dịch vụ đám mây mà họ tin tưởng.

Tôi hoàn toàn đồng ý với lipis, tôi cũng sống trên đám mây. Chúng tôi hy vọng rằng tính năng này sẽ sớm được triển khai.

Chế độ ứng dụng dạng thẻ

Rất ấn tượng! Chúng tôi đã thấy rất nhiều tiện ích tích hợp API thực sự tuyệt vời trong Excalidraw. Hệ thống tệp, xử lý tệp, bảng nhớ tạm, chia sẻ trên webmục tiêu chia sẻ trên web. Nhưng sau đây là một điều khác. Cho đến bây giờ, tôi chỉ có thể chỉnh sửa một tài liệu tại một thời điểm. Không còn nữa. Lần đầu tiên hãy tận hưởng phiên bản ban đầu của chế độ ứng dụng dạng thẻ trong Excalidraw. Đây là giao diện của trang web.

Tôi đang mở một tệp trong ứng dụng web tiến bộ (PWA) Excalidraw đã cài đặt. Tệp này đang chạy ở chế độ độc lập. Bây giờ, tôi mở một thẻ mới trong cửa sổ độc lập. Đây không phải là thẻ trình duyệt thông thường mà là thẻ PWA. Trong thẻ mới này, tôi có thể mở một tệp phụ và xử lý các tệp đó một cách độc lập từ cùng một cửa sổ ứng dụng.

Chế độ ứng dụng dạng thẻ đang ở giai đoạn đầu và chưa có gì hoàn tất. Nếu bạn quan tâm, hãy nhớ đọc thêm về trạng thái hiện tại của tính năng này trong bài viết của tôi.

Closing (Đang đóng)

Để cập nhật thông tin về tính năng này và các tính năng khác, hãy nhớ xem công cụ theo dõi API Fugu của chúng tôi. Chúng tôi rất vui khi được giúp trang web phát triển và cho phép bạn làm được nhiều việc hơn trên nền tảng này. Sau đây là một bản cải tiến của Excalidraw, sau đây là tất cả những ứng dụng thú vị mà bạn sẽ xây dựng. Hãy bắt đầu tạo tại excalidraw.com.

Tôi rất nóng lòng được thấy một số API mà tôi đã giới thiệu hôm nay bật lên trong ứng dụng của bạn. Tôi là Tom, bạn có thể tìm tôi bằng địa chỉ @tomayac trên Twitter và Internet nói chung. Cảm ơn bạn rất nhiều vì đã theo dõi và chúc bạn có những giây phút vui vẻ tại Google I/O.