أي تكنولوجيا متطوّرة بما يكفي لا يمكن تمييزها عن السحر. ما لم تفهمها. اسمي توماس شتاينر، وأعمل في قسم "علاقات المطوّرين" في Google. في هذه المقالة التي تتضمّن ملخّصًا لجلسة Google I/O التي قدّمتها، سأستعرض بعض واجهات برمجة التطبيقات الجديدة في مشروع Fugu وكيفية تحسينها لمسارات المستخدمين الأساسية في تطبيق Excalidraw التقدّمي على الويب، حتى تتمكّن من الاستفادة من هذه الأفكار وتطبيقها على تطبيقاتك.
كيف بدأت استخدام Excalidraw؟
أريد أن أبدأ بقصة. في 1 كانون الثاني (يناير) 2020، غرّد كريستوفر شيدو، وهو مهندس برامج في Facebook، حول تطبيق صغير للرسم بدأ العمل عليه. باستخدام هذه الأداة، يمكنك رسم مربّعات وأسهم تبدو كرتونية ومرسومة يدويًا. في اليوم التالي، أصبح بإمكانك أيضًا رسم أشكال بيضاوية ونصوص، بالإضافة إلى اختيار الكائنات ونقلها. في 3 يناير، تم اختيار اسم Excalidraw للتطبيق، وكما هو الحال مع أي مشروع جانبي جيد، كان شراء اسم النطاق من أولى الإجراءات التي اتّخذها "كريستوفر". حتى الآن، كان بإمكانك استخدام الألوان وتصدير الرسم بأكمله كملف PNG.
في 15 يناير، نشر كريستوفر مشاركة في مدونته حظيت باهتمام كبير على تويتر، بما في ذلك اهتمامي. بدأت المشاركة ببعض الإحصاءات الرائعة:
- 12,000 مستخدم نشط فريد
- 1,500 نجمة على GitHub
- 26 مساهمًا
بالنسبة إلى مشروع بدأ قبل أسبوعين فقط، هذا ليس سيئًا على الإطلاق. لكن ما أثار اهتمامي حقًا كان في الجزء السفلي من المشاركة. كتب كريستوفر أنّه جرّب شيئًا جديدًا هذه المرة، وهو منح كل من يقدّم طلب سحب إذنًا غير مشروط بتنفيذ عمليات الدمج. في اليوم نفسه الذي قرأت فيه مشاركة المدونة، أرسلت طلب سحب أضفت فيه إمكانية استخدام File System Access API إلى Excalidraw، ما أدى إلى حل طلب ميزة كان قد أرسله أحد المستخدمين.
تم دمج طلب السحب بعد يوم واحد، ومنذ ذلك الحين، أصبح بإمكاني الوصول إلى جميع عمليات الدمج. وغني عن القول، لم أسيء استخدام سلطتي. ولم يفعل ذلك أي من المساهمين الـ 149 حتى الآن.
اليوم، أصبح Excalidraw تطبيق ويب تقدّميًا كاملاً يمكن تثبيته ويتيح استخدامه بلا إنترنت، كما يتضمّن وضعًا داكنًا رائعًا، ويتيح فتح الملفات وحفظها بفضل File System Access API.
Lipis يوضّح سبب تخصيص الكثير من وقته لتطبيق Excalidraw
هذا يمثّل نهاية قصة "كيف بدأت استخدام Excalidraw"، ولكن قبل أن أستعرض بعض ميزات Excalidraw الرائعة، يسرّني أن أقدّم لكم Panayiotis. "باناجيوتيس ليبيريديس"، المعروف على الإنترنت باسم lipis، هو المساهم الأكثر إنتاجية في Excalidraw. سألنا lipis عن الدافع الذي يجعله يكرّس الكثير من وقته لـ Excalidraw:
مثل الجميع، عرفتُ عن هذا المشروع من تغريدة كريستوفر. كانت مساهمتي الأولى هي إضافة مكتبة Open Color، وهي الألوان التي لا تزال جزءًا من Excalidraw حتى اليوم. مع توسّع المشروع وتلقّينا عددًا كبيرًا من الطلبات، كان إنجازي الكبير التالي هو إنشاء خادم خلفي لتخزين الرسومات كي يتمكّن المستخدمون من مشاركتها. لكنّ ما يدفعني حقًا إلى المساهمة هو أنّ كل من جرّب Excalidraw يبحث عن أعذار لاستخدامه مرة أخرى.
أتفق تمامًا مع lipis. كل من جرّب 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 وامتدادات الملفات المقبولة، يصبح الجزء المركزي هو استدعاء الدالة showOpenFilePicker()
في واجهة برمجة التطبيقات File System Access API. تعرض هذه الدالة صفيفًا من الملفات أو ملفًا واحدًا، وذلك حسب ما إذا تم اختيار ملفات متعددة. كل ما تبقى بعد ذلك هو وضع معرّف الملف في عنصر الملف، حتى يمكن استرداده مرة أخرى.
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 لكائن ثنائي كبير (blob) كقيمة للسمة 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 Target API. أنا هنا في تطبيق "الملفات" في مجلّد Downloads
. يمكنني رؤية ملفَين، أحدهما يحمل الاسم غير الوصفي untitled
وطابعًا زمنيًا. للاطّلاع على محتوى الملف، أنقر على النقاط الثلاث، ثم على "مشاركة"، ويظهر خيار Excalidraw ضمن الخيارات المتاحة. عند النقر على الرمز، أرى أنّ الملف يحتوي على شعار I/O مرة أخرى.
Lipis على إصدار Electron المتوقّف
أحد الإجراءات التي يمكنك تنفيذها على الملفات ولم أتحدّث عنها بعد هو النقر المزدوج عليها. عند النقر مرّتين على ملف، يتم عادةً فتح التطبيق المرتبط بنوع MIME الخاص بالملف. على سبيل المثال، بالنسبة إلى .docx
، سيكون هذا التطبيق هو Microsoft Word.
كانت Excalidraw تتضمّن إصدارًا من التطبيق يستند إلى Electron ويتيح ربط أنواع الملفات، لذا عند النقر مرّتين على ملف .excalidraw
، كان تطبيق Excalidraw المستند إلى Electron يفتح الملف. كان "ليبيس"، الذي سبق أن قابلته، هو من أنشأ Excalidraw Electron وأوقفها نهائيًا. سألته عن سبب اعتقاده بإمكانية إيقاف إصدار Electron نهائيًا، فأجاب:
لقد طلب المستخدمون تطبيقًا يستند إلى Electron منذ البداية، ويرجع ذلك بشكل أساسي إلى أنّهم أرادوا فتح الملفات من خلال النقر المزدوج. وقد أردنا أيضًا إتاحة التطبيق في متاجر التطبيقات. في الوقت نفسه، اقترح أحد الأشخاص إنشاء تطبيق ويب تقدّمي بدلاً من ذلك، لذا نفّذنا كلا الخيارين. لحسن الحظ، تم تعريفنا على واجهات برمجة تطبيقات Project Fugu، مثل إمكانية الوصول إلى نظام الملفات، وإمكانية الوصول إلى الحافظة، والتعامل مع الملفات، وغير ذلك. بنقرة واحدة فقط، يمكنك تثبيت التطبيق على الكمبيوتر أو الجهاز الجوّال بدون الحاجة إلى Electron. كان قرار إيقاف إصدار Electron نهائيًا والتركيز على تطبيق الويب فقط وتحويله إلى أفضل تطبيق ويب تقدّمي ممكن قرارًا سهلاً. علاوةً على ذلك، أصبح بإمكاننا الآن نشر تطبيقات الويب التقدّمية على "متجر Play" و"متجر Microsoft". هذا إنجاز عظيم!
يمكن القول إنّ Excalidraw for Electron لم يتم إيقافه نهائيًا لأنّ Electron سيئ، بل لأنّ الويب أصبح جيدًا بما يكفي. أعجبني هذا!
معالجة الملفات
عندما أقول "أصبح الويب جيدًا بما يكفي"، فإنّ السبب هو ميزات مثل ميزة "معالجة الملفات" القادمة.
هذا هو التثبيت العادي لنظام التشغيل macOS Big Sur. والآن، لنرَ ما يحدث عند النقر بزر الماوس الأيمن على ملف Excalidraw. يمكنني اختيار فتحه باستخدام Excalidraw، وهو تطبيق ويب تقدّمي مثبَّت. بالطبع، يمكن أيضًا النقر مرّتين، ولكنّ ذلك أقل إثارة للاهتمام عند عرضه في فيديو.
كيف يمكن إجراء ذلك؟ الخطوة الأولى هي تعريف نظام التشغيل بأنواع الملفات التي يمكن لتطبيقي التعامل معها. أجري ذلك في حقل جديد باسم 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
هذا على حقل باسم files يمنحني مجموعة من معرّفات الملفات التي يمكنني العمل بها. أهتم فقط بالملف الأول، ومن خلال معرّف الملف هذا، أحصل على كائن ثنائي كبير (BLOB) أرسله إلى الدالة 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 هي إمكانية دمج الحافظة. يمكنني نسخ الرسم بأكمله أو أجزاء منه فقط إلى الحافظة، وربما إضافة علامة مائية إذا أردت ذلك، ثم لصقه في تطبيق آخر. هذه هي نسخة ويب من تطبيق Paint في نظام التشغيل Windows 95.
طريقة عمل هذه الميزة بسيطة للغاية. كل ما أحتاج إليه هو اللوحة ككائن ثنائي كبير الحجم، ثم أكتبه في الحافظة من خلال تمرير مصفوفة ذات عنصر واحد مع ClipboardItem
مع الكائن الثنائي الكبير الحجم إلى الدالة navigator.clipboard.write()
. لمزيد من المعلومات حول الإجراءات التي يمكنك اتّخاذها باستخدام واجهة برمجة التطبيقات الخاصة بلوحة الحافظة، يمكنك الاطّلاع على مقالة Jason ومقالتي.
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 ولكن لا أفعل شيئًا، يظهر المؤشر على أنّني غير نشط، ويتم تمثيل ذلك بثلاثة أحرف Z متجاورة.
قد يظنّ القراء الأوفياء لمنشوراتنا أنّ ميزة رصد الخمول يتم تنفيذها من خلال Idle Detection API، وهو اقتراح في مرحلة مبكرة تم العمل عليه في سياق Project Fugu. تنبيه بكشف تفاصيل عن الحبكة: ليس كذلك. على الرغم من أنّنا نفّذنا ميزة استنادًا إلى واجهة برمجة التطبيقات هذه في Excalidraw، قرّرنا في النهاية اتّباع نهج أكثر تقليدية يستند إلى قياس حركة المؤشر ومدى ظهور الصفحة.
لقد أرسلنا ملاحظات حول سبب عدم حلّ واجهة برمجة التطبيقات Idle Detection API لحالة الاستخدام التي واجهتنا. يتم تطوير جميع واجهات برمجة التطبيقات في Project Fugu بشكل علني، ما يتيح للجميع المشاركة وإبداء آرائهم.
Lipis on what is holding back Excalidraw
وبالمناسبة، طرحت على lipis سؤالاً أخيرًا حول ما يراه ينقص منصة الويب ويؤثر سلبًا في Excalidraw:
واجهة File System Access API رائعة، ولكن هل تعرف ما هو الأفضل؟ معظم الملفات التي أهتم بها هذه الأيام موجودة في Dropbox أو Google Drive، وليس على القرص الصلب. أتمنى أن تتضمّن واجهة برمجة التطبيقات File System Access API طبقة تجريدية لمقدّمي أنظمة الملفات البعيدة، مثل Dropbox أو Google، ليتم دمجها، وأن يتمكّن المطوّرون من كتابة الرموز البرمجية لها. وبعد ذلك، يمكن للمستخدمين الاطمئنان إلى أنّ ملفاتهم آمنة لدى مزوّد الخدمة السحابية الذي يثقون به.
أتفق تمامًا مع lipis، فأنا أيضًا أستخدم السحابة الإلكترونية. نأمل أن يتم تنفيذ ذلك قريبًا.
وضع التطبيق ذو علامات التبويب
رائع! لقد رأينا الكثير من عمليات دمج واجهات برمجة التطبيقات الرائعة في Excalidraw. نظام الملفات ومعالجة الملفات والحافظة والمشاركة على الويب وهدف المشاركة على الويب ولكن إليك معلومة أخرى. حتى الآن، كان بإمكاني تعديل مستند واحد فقط في كل مرة. لم يعُد الأمر كذلك. يسرّنا أن نتيح لك للمرة الأولى تجربة إصدار مبكّر من وضع التطبيق ذي علامات التبويب في Excalidraw. إليك الشكل الذي يظهر به.
لديّ ملف حالي مفتوح في تطبيق Excalidraw PWA المثبَّت الذي يعمل في وضع التطبيق المستقل. الآن، أفتح علامة تبويب جديدة في النافذة المستقلة. هذه ليست علامة تبويب متصفّح عادية، بل هي علامة تبويب لتطبيق ويب تقدّمي. في علامة التبويب الجديدة هذه، يمكنني بعد ذلك فتح ملف ثانوي والعمل عليه بشكل مستقل عن نافذة التطبيق نفسها.
لا يزال وضع التطبيق ذي علامات التبويب في مراحله الأولى، ولا يمكننا تأكيد توفّره بشكل نهائي. إذا كنت مهتمًا، ننصحك بالاطّلاع على الحالة الحالية لهذه الميزة في مقالتي.
الخاتمة
للبقاء على اطّلاع على هذه الميزة وغيرها، احرص على متابعة أداة تتبُّع Fugu API. نحن متحمّسون جدًا لتطوير الويب وإتاحة المزيد من الإمكانات على المنصة. نتطلّع إلى تحسين Excalidraw باستمرار، ونتطلّع إلى جميع التطبيقات الرائعة التي ستنشئها. يمكنك البدء في إنشاء الرسومات على excalidraw.com.
أتطلّع إلى رؤية بعض واجهات برمجة التطبيقات التي عرضتها اليوم تظهر في تطبيقاتك. اسمي توم، ويمكنك التواصل معي على Twitter من خلال @tomayac وعلى الإنترنت بشكل عام. شكرًا جزيلاً على المشاهدة، ونتمنى لك الاستفادة من بقية فعاليات مؤتمر Google I/O.