Excalidraw وFugu: تحسين تجربة المستخدم الأساسية

لا يمكن تمييز أي تقنية متقدمة بما فيه الكفاية عن السحر. إلا إذا تفهم ذلك. اسمي "توماس شتاينر"، أعمل في فريق علاقات المطوّرين في Google. وفي هذه المقالة لحديثتي في مؤتمر Google I/O، سألقي نظرة على بعض واجهات Fugu API الجديدة وكيفية تحسينها لتجارب المستخدمين الأساسية في تطبيق ExcaliDraw PWA، كي تتمكّن من استلهام الأفكار من هذه الأفكار وتطبيقها على تطبيقاتك الخاصة.

كيف جئت إلى ExcaliDraw

أريد أن أبدأ بقصة. في 1 كانون الثاني (يناير) 2020، كريستوفر شيدو، مهندس برمجيات لدى Facebook، نشر تغريدة حول تطبيق رسم صغير بدأ العمل عليه. باستخدام هذه الأداة، يمكنك رسم مربعات وأسهم تبدو كرتونية ومرسومة يدويًا. في اليوم التالي، يمكنك أيضًا رسم علامات الحذف والنصوص، فضلاً عن تحديد الكائنات وتحريكها. في 3 كانون الثاني (يناير)، حصل التطبيق على اسمه ExcaliDraw، وكان شراء اسم النطاق أحد الإجراءات الأولى التي اتخذها "كريستوفر"، كما هو الحال في كل مشروع جانبي جيد. الآن، يمكنك استخدام الألوان وتصدير الرسم بالكامل كملف PNG.

لقطة شاشة لتطبيق النموذج الأوّلي ExcaliDraw تُظهر أنّه يتوافق مع المستطيلات والأسهم والقطع الناقص والنصية

في 15 كانون الثاني (يناير)، نشر "كريستوفر" مشاركة مدونة جذبت الكثير من الاهتمام على Twitter، بما في ذلك موقعي الإلكتروني. بدأت المشاركة ببعض الإحصاءات الرائعة:

  • 12 ألف مستخدم نشط فريد
  • 1.5 ألف نجمة على GitHub
  • 26 مساهمًا

بالنسبة للمشروع الذي بدأ قبل أسبوعين فقط، فهذا ليس سيئًا على الإطلاق. لكن الشيء الذي أثار اهتمامي حقًا كان في أسفل المشاركة. كتب "كريستوفر" أنه جرب شيئًا جديدًا هذه المرة: منح كل من وصل إلى طلب سحب الإذن بالوصول غير المشروط. في اليوم نفسه من قراءة مشاركة المدونة، تلقّيت طلب سحب أدّى إلى إضافة دعم File System Access API إلى ExcaliDraw، وحلّ طلب ميزة قدّمه أحد المستخدمين.

لقطة شاشة من التغريدة التي أعلن فيها عن علاقاتي العامة.

تم دمج طلب السحب بعد يوم، وبعد ذلك، حصلت على إذن بالوصول الكامل. لا حاجة إلى القول بأنني لم أستغل قوتي. ولم يتم انضمام أي مساهمين آخرين من بين 149 مساهمًا حتى الآن.

واليوم، ExcaliDraw هو تطبيق ويب تقدّمي كامل وقابل للتثبيت مع إمكانية استخدامه بلا إنترنت، كما يتيح له وضع "الوضع الداكن" بشكل مذهل، إضافةً إلى إمكانية فتح الملفات وحفظها بفضل واجهة برمجة التطبيقات File System Access API.

لقطة شاشة لتطبيق ExcaliDraw PWA في الحالة الحالية

ليبيس عن سبب تخصيص الكثير من وقته لـ ExcaliDraw

لقد وصلنا إلى نهاية قصتي "كيف أتيت إلى ExcaliDraw"، ولكن قبل أن أتعمق في بعض ميزات ExcaliDraw الرائعة، يسعدني أن أقدّم لك Panayiotis. Panayiotis Lipiridis، الموجودة على شبكة الإنترنت، تُعرف ببساطة باسم lipis، هي المساهم الأكثر إثراءً في موقع ExcaliDraw. سألته ليبيس عما يدفعه إلى تخصيص الكثير من وقته لـ ExcaliDraw:

ومثل أي شخص آخر، تعلمت عن هذا المشروع من تغريدة كريستوفر. كانت مساهمتي الأولى هي إضافة مكتبة الألوان المفتوحة، الألوان التي لا تزال جزءًا من ExcaliDraw اليوم. مع نمو المشروع وتلقينا العديد من الطلبات، كانت مساهمتي الكبيرة التالية هي إنشاء خلفية لتخزين الرسومات حتى يتمكن المستخدمون من مشاركتها. لكن ما يدفعني حقًا للمساهمة هو أن كل من جرب ExcaliDraw يتطلع إلى العثور على أعذار لاستخدامها مرة أخرى.

أتفق تماما مع الكلمات غير اللائقة. من حاول استخدام ExcaliDraw، يسعى للعثور على أعذار لاستخدامها مرة أخرى.

أمثلة واقعية عن الاستخراج

أودّ أن أوضح لك الآن كيف يمكنك استخدام ExcaliDraw بشكل عملي. أنا لست فنانًا رائعًا، لكن شعار مؤتمر Google I/O بسيط بما فيه الكفاية، لذا دعني أجربه. المربع هو "i"، والخط هو الشرطة المائلة، و "o" دائرة. اضغط مع الاستمرار على مفتاح Shift، حتى تحصل على دائرة مثالية. دعني أحرك الشرطة المائلة قليلاً، حتى يبدو أفضل. الآن بعض الألوان لـ "i" و "o". اللون الأزرق جيد. ربما نمط تعبئة مختلف؟ هل كل شيء صلب أم متداخل؟ يا إلهي يبدو رائعًا. إنها ليست مثالية، لكن هذه هي فكرة ExcaliDraw، لذا دعني أحفظها.

أنقر فوق أيقونة الحفظ وأدخل اسم ملف في مربع حوار حفظ الملف. في Chrome، وهو متصفّح متوافق مع File System Access API، لا تُعدّ عملية التنزيل هذه عملية تنزيل، لكنّها عملية حفظ حقيقية تتيح لي اختيار موقع الملف واسمه، ومكان حفظ الملفات في الملف نفسه إذا أجريت تعديلات عليها.

سأغيّر الشعار وأجعل الحرف "i" أحمر. إذا قمت الآن بالنقر فوق حفظ مرة أخرى، فسيتم حفظ التعديل في نفس الملف كما كان من قبل. كإثبات على ذلك، اسمح لي بمحو اللوحة وإعادة فتح الملف. كما ترى، الشعار الأحمر والأزرق المعدّل ظهر مرة أخرى.

التعامل مع الملفات

في المتصفحات التي لا تتيح حاليًا استخدام File System Access API، تكون كلّ عملية حفظ عبارة عن عملية تنزيل، وبالتالي عندما أُجري تغييرات، ينتهي الأمر بإنشاء ملفات متعدّدة تتضمن أرقامًا متزايدة في اسم الملف الذي يملأ مجلد "عمليات التنزيل". ولكن على الرغم من هذا الجانب السلبي، لا يزال بإمكاني حفظ الملف.

جارٍ فتح الملفات

إذن ما السر؟ كيف يمكن فتح الملفات وحفظها على متصفّحات مختلفة قد تتوافق مع واجهة File System Access API أو لا؟ يتم فتح ملف في ExcaliDraw في دالة تُسمى loadFromJSON)()، والتي تستدعي بدورها دالة تسمى 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);
};

الدالة fileOpen() التي تأتي من مكتبة صغيرة كتبتها تُسمى browser-fs-access التي نستخدمها في ExcaliDraw. توفّر هذه المكتبة إمكانية الوصول إلى نظام الملفات من خلال File System Access API مع عنصر احتياطي قديم، كي يمكن استخدامه في أي متصفّح.

سأوضّح لك أولاً طريقة التنفيذ في حال كانت واجهة برمجة التطبيقات متاحة. بعد التفاوض بشأن أنواع MIME المقبولة وامتدادات الملفات، يكون الجزء الأساسي هو استدعاء دالة File System Access API showOpenFilePicker(). تعرض هذه الدالة صفيفًا من الملفات أو ملفًا واحدًا، بناءً على ما إذا تم تحديد ملفات متعددة أم لا. كل ما تبقى هو وضع مقبض الملف على كائن الملف، حتى يمكن استرداده مرة أخرى.

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

يعتمد التنفيذ الاحتياطي على عنصر input من النوع "file". بعد التفاوض على أنواع وإضافات MIME التي سيتم قبولها، تتمثل الخطوة التالية في النقر آليًا على عنصر الإدخال ليظهر مربع حوار فتح الملف. عند التغيير، أي عندما يختار المستخدم ملفًا أو عدة ملفات، يتم حل الوعد.

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

جارٍ حفظ الملفات

والآن لننتقل إلى الحفظ. في ExcaliDraw، يتم الحفظ في دالة تُسمى saveAsJSON(). فهي تنشئ أولاً مصفوفة عناصر ExcaliDraw بتنسيق JSON، وتحوِّل JSON إلى كائن ثنائي، ثم تستدعي دالة تُسمى fileSave(). يتم توفير هذه الدالة أيضًا من خلال مكتبة browser-fs-access.

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

مرة أخرى، لألقي نظرة أولاً على تنفيذ المتصفّحات التي تتيح استخدام File System Access API. يبدو أول سطرين معقدين بعض الشيء، لكن كل ما يفعلونه هو التفاوض بشأن أنواع MIME وإضافات الملفات. عندما قمت بالحفظ من قبل وكان لدي بالفعل مؤشر ملف، لا يلزم عرض مربع حوار الحفظ. ولكن إذا كانت هذه أول عملية حفظ، يتم عرض مربع حوار الملف ويحصل التطبيق على اسم معرِّف الملف للاستخدام مستقبلاً. وبعد ذلك، تتم الكتابة المتبقية فقط في الملف، وهو ما يحدث من خلال بث قابل للكتابة.

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

ميزة "الحفظ باسم"

إذا قررت تجاهل مؤشر ملف موجود بالفعل، فيمكنني تنفيذ ميزة "حفظ باسم" لإنشاء ملف جديد يستند إلى ملف موجود. لتوضيح ذلك، دعني أفتح ملفًا حاليًا، وقم بإجراء بعض التعديلات، ثم عدم استبدال الملف الحالي، ولكن أنشئ ملفًا جديدًا باستخدام ميزة الحفظ باسم. يؤدي ذلك إلى ترك الملف الأصلي بدون تغيير.

بالنسبة إلى المتصفّحات التي لا تتيح استخدام File System Access API، إنّ كل الإجراءات المطلوبة هي إنشاء عنصر ارتساء بسمة download وقيمته هي اسم الملف المطلوب وعنوان URL كائن ثنائي كبير كقيمة السمة 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();
};

ويتم بعد ذلك النقر على عنصر الارتساء آليًا. لمنع تسرب الذاكرة، يجب إبطال عنوان URL للكائن ثنائي المستويات بعد الاستخدام. بما أنّ عملية التنزيل هذه مجرّد تنزيل، لن يظهر مربّع حوار لحفظ الملفات، وسيتم نقل جميع الملفات إلى مجلد Downloads التلقائي.

السحب والإفلات

من بين عمليات دمج الأنظمة المفضّلة لديّ على أجهزة الكمبيوتر المكتبي السحب والإفلات. في ExcaliDraw، عندما أفلت ملف .excalidraw على التطبيق، يتم فتحه على الفور ويمكنني بدء التعديل. في المتصفحات التي تدعم File System Access API، يمكنني حينها حفظ التغييرات على الفور. لا حاجة إلى المرور بمربع حوار حفظ الملفات نظرًا للحصول على مؤشر الملف المطلوب من عملية السحب والإفلات.

يكمن سر هذا الأمر في استدعاء الدالة getAsFileSystemHandle() في عنصر نقل البيانات عندما تكون واجهة برمجة التطبيقات File System Access API متاحة. بعد ذلك، سأمرّر مؤشر الملف هذا إلى loadFromBlob()، والذي قد تتذكره من بضع فقرات أعلاه. الكثير من الأشياء التي يمكنك القيام بها مع الملفات: الفتح والحفظ والتوفير الزائد والسحب والإفلات. وثّقت أنا وزميلي بيت كل هذه الحيل وغيرها في مقالتنا حتى تتمكنوا من المتابعة في حال حدوث ذلك بسرعة كبيرة.

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

مشاركة الملفات

هناك عملية دمج أخرى للنظام حاليًا على أنظمة التشغيل Android وChromeOS وWindows من خلال Web Share API. أنا هنا في تطبيق Files في مجلد Downloads. يمكنني رؤية ملفين، أحدهما يحمل الاسم غير الوصفي untitled والطابع الزمني. للتحقق مما يحتوي عليه، أنقر على النقاط الثلاث، ثم أشارك، وأحد الخيارات التي تظهر هو ExcaliDraw. عندما أنقر على الرمز، يمكنني رؤية أن الملف يحتوي فقط على شعار I/O مرة أخرى.

ليبيس على إصدار Electron المهم

أحد الأشياء التي يمكنك فعلها مع الملفات التي لم أتحدث عنها بعد هو النقر المزدوج عليها. ما يحدث عادةً عند النقر فوق ملف من خلال فتح التطبيق المرتبط بنوع MIME للملف. على سبيل المثال لـ .docx، سيكون هذا هو Microsoft Word.

اعتادت ExcaliDraw الحصول على إصدار Electron من التطبيق الذي يتوافق مع عمليات ربط أنواع الملفات هذه، لذا عندما تنقر مرّتين على ملف .excalidraw، سيتم فتح تطبيق ExcaliDraw Electron. كان ليبيس، الذي قابلته من قبل، هو صانع المحتوى والمتوقّف نهائيًا لشركة ExcaliDraw Electron. سألته عن سبب شعوره بأنه من الممكن إيقاف إصدار Electron:

طلب الأشخاص الحصول على تطبيق Electron منذ البداية، ويرجع ذلك أساسًا إلى أنهم أرادوا فتح الملفات بالنقر المزدوج. كنا نعتزم أيضًا توفير التطبيق في متاجر التطبيقات. بالتوازي مع ذلك، اقترح أحدهم إنشاء تطبيق ويب تقدّمي (PWA) بدلاً من ذلك، لذلك فعلنا كليهما. لحسن الحظ، تم تعريفنا بواجهات برمجة تطبيقات Project Fugu مثل الوصول إلى نظام الملفات والوصول إلى الحافظة ومعالجة الملفات والمزيد. وبنقرة واحدة، يمكنك تثبيت التطبيق على جهاز الكمبيوتر المكتبي أو الجهاز الجوّال بدون أي وزن زائد لـ Electron. كان قرارًا سهلاً بشأن إيقاف إصدار Electron والتركيز فقط على تطبيق الويب وجعله أفضل تطبيق ويب تقدّمي. بالإضافة إلى ذلك، يمكننا الآن نشر تطبيقات الويب التقدّمية (PWA) على "متجر Play" وMicrosoft Store. هذا عظيم!

يمكن للمرء أن يقول أن ExcaliDraw for Electron لم يتم إيقافه نهائيًا لأن Electron سيئ، ليس على الإطلاق، بل لأن شبكة الويب أصبحت جيدة بما فيه الكفاية. أحب هذا!

معالجة الملفات

عندما أقول "أصبح الويب جيدًا بما فيه الكفاية"، يكون ذلك بسبب ميزات مثل التعامل مع الملف القادم.

هذا تثبيت منتظم لتطبيق macOS Big Sur. تحقق الآن مما يحدث عندما أنقر بزر الماوس الأيمن على ملف ExcaliDraw. يمكنني اختيار فتحه باستخدام تطبيق ExcaliDraw، وهو تطبيق الويب التقدّمي (PWA) المثبَّت. وبالطبع، قد ينجح النقر المزدوج أيضًا، ولكن قد يكون عرضه أقل إثارة في التسجيل الرقمي للشاشة.

إذًا، كيف يعمل هذا؟ الخطوة الأولى هي جعل أنواع الملفات التي يمكن لتطبيقي التعامل معها معروفة لنظام التشغيل. أفعل ذلك في حقل جديد يسمّى file_handlers في بيان تطبيق الويب. وقيمته هي مصفوفة من العناصر ذات إجراء وسمة accept. ويحدِّد الإجراء مسار عنوان URL الذي يشغِّل نظام التشغيل تطبيقك عليه، وكائن القبول هو أزواج من القيم الرئيسية من أنواع MIME وامتدادات الملفات المرتبطة بها.

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

والخطوة التالية هي معالجة الملف عند تشغيل التطبيق. ويحدث ذلك في واجهة launchQueue حيث أحتاج إلى تحديد مستهلك من خلال الاتصال بـ setConsumer(). المعلمة لهذه الدالة هي دالة غير متزامنة تتلقّى launchParams. يحتوي كائن launchParams هذا على حقل يُسمى "الملفات" الذي يوفّر لي مجموعة من مؤشرات الملفات للعمل عليها. لا أهتم سوى بالأول، ومن خلال مقبض الملف هذا، أحصل على فقاعة، ثم أنقلها إلى صديقنا القديم 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 });
      });
    });
}

مرة أخرى، إذا كانت عملية المعالجة تتم بسرعة كبيرة، يمكنك قراءة المزيد حول واجهة برمجة تطبيقات File Handling API في مقالتي. يمكنك تفعيل معالجة الملفات من خلال ضبط علامة ميزات النظام الأساسي التجريبي للويب. ومن المقرّر أن تظهر في Chrome في وقت لاحق من هذا العام.

دمج الحافظة

ميزة أخرى رائعة في ExcaliDraw هي تكامل الحافظة. يمكنني نسخ الرسم بالكامل أو أجزاء منه فقط إلى الحافظة، ربما إضافة علامة مائية إن أردت، ثم لصقها في تطبيق آخر. بالمناسبة، هذا إصدار ويب من تطبيق "الطلاء" في Windows 95.

وهذه الطريقة بسيطة بشكل مدهش. كل ما أحتاجه هو لوحة الرسم على شكل فقاعة تفسيرية، والتي أكتبها بعد ذلك في الحافظة عن طريق تمرير مصفوفة من عنصر واحد باستخدام ClipboardItem مع كائن ثنائي كبير (blob) إلى الدالة navigator.clipboard.write(). لمزيد من المعلومات حول ما يمكنك فعله باستخدام واجهة برمجة التطبيقات للحافظة، يمكنك الاطلاع على مقال جايسون ومقالتي.

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

التعاون مع الآخرين

مشاركة عنوان URL لجلسة

هل تعلم أن تطبيق ExcaliDraw يتيح وضع التعاون أيضًا؟ يمكن لأشخاص مختلفين العمل معًا في نفس الوثيقة. لبدء جلسة جديدة، أنقر على زر التعاون المباشر ثم أبدأ جلسة. يمكنني مشاركة عنوان URL للجلسة مع المتعاونين بسهولة بفضل Web Share API التي تم دمجها في ExcaliDraw.

التعاون المباشر

لقد محاكاةت جلسة تعاون محليًا من خلال العمل على شعار مؤتمر Google I/O على Pixelbook وهاتف Pixel 3a وجهاز iPad Pro. تنعكس التغييرات التي أُجريها على أحد الأجهزة على جميع الأجهزة الأخرى.

أستطيع حتى رؤية كل المؤشرات تتحرك. يتحرك مؤشر Pixelbook بثبات ثابتة لأنّه يتم التحكّم فيه بواسطة لوحة اللمس، ولكن مؤشر هاتف Pixel 3a ومؤشر جهاز iPad Pro اللوحي يتحركان، لأنّني أتحكّم في هذه الأجهزة من خلال النقر بإصبعي.

جارٍ الاطّلاع على حالات المتعاونين

لتحسين تجربة التعاون في الوقت الفعلي، يتم تشغيل نظام رصد غير نشِط لفترة قصيرة. يعرض مؤشر iPad Pro نقطة خضراء عندما أستخدمه. تتحول النقطة إلى اللون الأسود عند التبديل إلى علامة تبويب أو تطبيق متصفح آخر. وعندما أستخدم تطبيق ExcaliDraw، ولكن لا يتم اتخاذ أي إجراء، يظهر لي المؤشر في وضع عدم النشاط، ويرمز إليه رمز zZZ الثلاثة.

قد يميل القراء المتحمسون لمنشوراتنا إلى الاعتقاد أنّ ميزة "رصد حالات عدم النشاط" يتم رصدها من خلال Idle Detection API، وهو اقتراح في مرحلة مبكرة تم العمل عليه في سياق مشروع Fugu. تنبيه بحرق الأحداث: ليس صحيحًا. في حين كان لدينا تطبيق يستند إلى واجهة برمجة التطبيقات هذه في ExcaliDraw، قررنا في النهاية اتّباع نهج أكثر تقليدية يعتمد على قياس حركة المؤشر وظهور الصفحة.

لقطة شاشة لملاحظات &quot;كشف عدم النشاط&quot; المقدمة في مستودع WICG Idle Detection.

قدّمنا ملاحظات حول سبب عدم نجاح واجهة برمجة التطبيقات Idle Detection API في حلّ مشكلة الاستخدام الحالية. يتم تطوير جميع واجهات برمجة تطبيقات Project Fugu بدون قلق، حتى يتمكن الجميع من التدخل والتعبير عن آرائهم!

ليبيس حول ما يعيق إكساليدريد

بالحديث عن ذلك، طرحت على ليبس سؤالاً أخيرًا بخصوص ما يعتقد أنه مفقود من منصة الويب التي تعيق ExcaliDraw:

تعد File System Access API رائعة، لكن هل تعلم ماذا؟ معظم الملفات التي أهتم بها هذه الأيام موجودة في Dropbox أو Google Drive، وليس على القرص الثابت. أتمنى أن تتضمن واجهة برمجة التطبيقات File System Access API طبقة تجريد لمزودي أنظمة الملفات البعيدة مثل Dropbox أو Google وأن يتكاملوا معها وأن يستطيع المطورون البرمجة مقابلها. وبذلك يمكن للمستخدمين الاسترخاء ومعرفة أن ملفاتهم آمنة مع مقدم خدمات السحابة الإلكترونية الذي يثقون به.

أتفق تمامًا مع كلامي، فأنا أعيش في السحابة أيضًا. نأمل أن يتم تنفيذ ذلك قريبًا.

وضع التطبيقات المبوَّبة

ممتاز لاحظنا الكثير من عمليات تكامل واجهات برمجة التطبيقات الرائعة في ExcaliDraw. نظام الملفات ومعالجة الملفات واللوحة ومشاركة الويب وهدف مشاركة الويب. ولكن هناك شيء آخر. حتى الآن، لم يكن بإمكاني تعديل سوى مستند واحد في وقت معين. ليس بعد الآن. يُرجى الاستمتاع لأول مرة بإصدار مبكر من وضع التطبيق المبوَّب في ExcaliDraw. هكذا يبدو.

لديّ ملف حالي مفتوح في تطبيق ExcaliDraw PWA المثبَّت والذي يتم تشغيله في الوضع المستقل. الآن أفتح علامة تبويب جديدة في النافذة المستقلة. هذه ليست علامة تبويب متصفّح عادية، ولكنها علامة تبويب لتطبيق الويب التقدّمي (PWA). في علامة التبويب الجديدة هذه، يمكنني بعد ذلك فتح ملف ثانوي والعمل عليه بشكل مستقل من نافذة التطبيق نفسها.

لا يزال وضع التطبيق المبوَّب في مراحله الأولى، ولم يعُد كل شيء ثابتًا. إذا كنت مهتمًا بذلك، احرص على الاطّلاع على الحالة الحالية لهذه الميزة في مقالتي.

الخاتمة

للبقاء على اطّلاع على هذه الميزة وغيرها من الميزات، احرص على مشاهدة أداة تتبع واجهة برمجة التطبيقات Fuuu API. نحن متحمسون للغاية للمضي قدمًا في الويب والسماح لك بعمل المزيد على النظام الأساسي. تهانينا على تحسين ExcaliDraw، ونودّ أن نعلّق على جميع التطبيقات المذهلة التي ستنشئها. يمكنك بدء الإنشاء على excalidraw.com.

لا يسعني الانتظار لرؤية بعض واجهات برمجة التطبيقات التي عرضتُها اليوم تظهر في تطبيقاتك. اسمي تامر، ويمكنك التواصل معي من خلال @tomayac على Twitter وعلى الإنترنت بشكل عام. شكرًا جزيلاً على المشاهدة والاستمتاع ببقية مؤتمر Google I/O.