טכנולוגיה מתקדמת מספיק לא ניתנת להבדלה מכשף. אלא אם הבנתם את זה. קוראים לי תומאס סטיינר ואני עובד בצוות קשרי מפתחים ב-Google. בכתיבת ההרצאה שלי על Google I/O, אבחן כמה מממשקי ה-API החדשים של Fugu ואיך הם משפרים את התהליכים העיקריים שעוברים המשתמשים ב-EXcalidraw PWA. כך תוכלו לקבל השראה מהרעיונות האלה וליישם אותם באפליקציות שלכם.
איך הגעתי ל-Excalidraw
אני רוצה להתחיל בסיפור. ב-1 בינואר 2020, Christopher Chedeau, מהנדס תוכנה ב-Facebook, צייץ על אפליקציית ציור קטנה שהוא התחיל לעבוד עליה. בעזרת הכלי הזה אפשר לצייר תיבות וחיצים שנראים מצוירים ומצוינים ביד. למחרת, תוכלו גם לצייר אליפסות וטקסט, לבחור אובייקטים ולהזיז אותם. ב-3 בינואר, האפליקציה קיבלה את השם Excalidraw, וכמו בכל פרויקט צדדי טוב, רכישת שם הדומיין הייתה אחת הפעולות הראשונות של כריסטופר. עכשיו אפשר להשתמש בצבעים ולייצא את השרטוט כולו כקובץ PNG.
ב-15 בינואר, כריסטופר פרסם פוסט בבלוג שמשך הרבה תשומת לב בטוויטר, כולל העסק שלי. הפוסט התחיל עם כמה נתונים סטטיסטיים מרשימים:
- 12,000 משתמשים פעילים ייחודיים
- 1.5,000 כוכבים ב-GitHub
- 26 תורמי תוכן
זה לא רע בכלל לפרויקט שהתחיל רק לפני שבועיים. אבל הדבר שבאמת עורר את העניין שלי היה נמוך יותר בפוסט. כריסטופר כתב שהוא ניסה משהו חדש הפעם: מתן גישה ללא תנאי לכל מי שקיבל בקשת משיכה. באותו יום של קריאת הפוסט בבלוג, קיבלתי בקשת משיכה שנוספה תמיכה ב-File System Access API ל-EXcalidraw, כדי לתקן בקשת תכונה שמישהו הגיש.
בקשת ה-pull שלי אוחדה יום לאחר מכן, ומאותו רגע הייתה לי גישה מלאה לביצוע השינויים. למותר לציין שלא נהגתי לרעה בסמכות שלי. עד כה, ולא אף אחד אחר מבין 149 תורמי התוכן.
היום, Excalidraw היא אפליקציית Progressive Web App מלאה שניתן להתקין, עם תמיכה אופליין, מצב לילה מדהים, כן, וגם היכולת לפתוח ולשמור קבצים בזכות File System Access API.
Lipis מסביר למה הוא מקצה כל כך הרבה זמן ל-Excalidraw
זהו סוף הסיפור שלי על הדרך שבה הגעתי ל-Excalidraw, אבל לפני שאכנס לעומק לחלק מהתכונות המדהימות של Excalidraw, אשמח להציג לכם את Panayiotis. Panayiotis Lipiridis, שנקרא באינטרנט פשוט lipis, הוא הגורם הכי פרודוקטיבי ב-Excalidraw. שאלתי את ליפיס מה גורם לו להקדיש כל כך הרבה מזמנו ל-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 עם חלופה מדור קודם, כך שאפשר להשתמש בה בכל דפדפן.
קודם אראה לך איך לבצע את ההטמעה כאשר ה-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();
});
};
מתבצעת שמירה של הקבצים
עכשיו כדאי לשמור. ב-Expalidraw, השמירה מתבצעת בפונקציה saveAsJSON()
. קודם כל, היא מבצעת סריאליזציה של מערך הרכיבים של Excalidraw ל-JSON, ממירה את ה-JSON ל-blob ואז קוראת לפונקציה שנקראת 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;
};
התכונה 'שמירה בשם'
אם אצטרך להתעלם מ-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 של ה-blob אחרי השימוש. מכיוון שזו רק הורדה, אף פעם לא מוצגת תיבת דו-שיח לשמירת קבצים, וכל הקבצים מגיעים לתיקיית ברירת המחדל Downloads
.
גרירה ושחרור
אחת משילובי המערכת האהובים עלי במחשב היא 'גרירה ושחרור'. ב-Excalidraw, כשמשחררים קובץ .excalidraw
באפליקציה, הוא נפתח מיד ומתחילים לערוך אותו. בדפדפנים שתומכים ב-File System Access API, אני יכול אפילו לשמור את השינויים באופן מיידי. אין צורך לעבור דרך תיבת דו-שיח לשמירת קובץ, כי נקודת האחיזה הדרושה לקובץ התקבלה מפעולת הגרירה והשחרור.
הסוד לכך הוא שליחת קריאה ל-getAsFileSystemHandle()
בפריט העברת הנתונים כאשר יש תמיכה ב-File System Access API. לאחר מכן מעבירים את ה-handle של הקובץ אל 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
וחותמת זמן. כדי לבדוק מה הוא מכיל, לוחצים על שלוש הנקודות ואז משתפים, ואחת מהאפשרויות שמופיעה היא Exalidraw. כשמקישים על הסמל, רואים שהקובץ מכיל שוב רק את הלוגו של I/O.
Lipis בגרסה הקודמת של Electron
אחד הדברים שאפשר לעשות לגבי קבצים שעדיין לא דיברתי עליהם הוא doubleclick. בדרך כלל, כשקובץ doubleclick פועל, האפליקציה שמשויכת לסוג ה-MIME של הקובץ נפתחת. לדוגמה, עבור .docx
זו תהיה Microsoft Word.
בעבר הייתה לאפליקציית Excalidraw גרסה ל-Electron שתומכת בשיוכים כאלה של סוגי קבצים, כך שלחיצה כפולה על קובץ .excalidraw
פותחת את אפליקציית Excalidraw ל-Electron. ליפיס, שכבר פגשתם בעבר, הייתה גם היוצרת וגם הוצאה משימוש של Excalidraw Electron. שאלתי אותו למה לדעתו אפשר להוציא משימוש את הגרסה של Electron:
אנשים ביקשו אפליקציית Electron כבר מההתחלה, בעיקר כי הם רצו לפתוח קבצים באמצעות לחיצה כפולה. רצינו גם לפרסם את האפליקציה בחנויות אפליקציות. במקביל, מישהו הציע ליצור PWA במקום זאת, אז פשוט עשינו את שתי הפעולות. למזלנו, הצגנו את ממשקי Project Fugu, כמו גישה למערכת קבצים, גישה ללוח, טיפול בקבצים ועוד. בלחיצה יחידה תוכלו להתקין את האפליקציה במחשב או בנייד, ללא המשקל הנוסף של Electron. היה לנו קל להחליט להוציא את גרסת Electron, להתמקד רק באפליקציית האינטרנט ולהפוך אותה ל-PWA הטובה ביותר האפשרית. בנוסף, עכשיו אנחנו יכולים לפרסם אפליקציות PWA בחנות Play וב-Microsoft Store! זה מצוין!
אפשר לומר ש-EXcalidraw for Electron לא הוצא משימוש כי Electron הוא רע, בכלל לא, אבל בגלל שהאינטרנט הפך טוב מספיק. אהבתי!
טיפול בקבצים
כשאני אומר "האינטרנט הפך טוב מספיק", זה קורה בגלל תכונות כמו התכונה החדשה 'טיפול בקבצים'.
זוהי התקנה רגילה של macOS Big Sur. עכשיו בודקים מה קורה כשלוחצים לחיצה ימנית על קובץ של Exacalidraw. אפשר לפתוח אותו באמצעות 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
הזה יש שדה בשם 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.
האופן שבו זה עובד פשוט להפתיע. כל מה שצריך זה קנבס כ-blob, שאותו כותבים על הלוח על ידי העברת מערך של רכיב אחד עם ClipboardItem
עם ה-blob לפונקציה navigator.clipboard.write()
. מידע נוסף על מה שאפשר לעשות עם ה-API של הלוח זמין במאמר של ג'ייסון ובמאמר שלי.
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 יש גם מצב שיתוף פעולה? אנשים שונים יכולים לעבוד יחד על אותו מסמך. כדי להתחיל סשן חדש, אני לוחץ על הלחצן של שיתוף הפעולה בזמן אמת ואז מתחיל סשן. בעזרת Web Share API ששולבו Excalidraw, את כתובת ה-URL של הסשן עם שותפי העריכה שלי קל לשתף.
שיתוף פעולה בזמן אמת
הדגמתי סשן עבודה משותף באופן מקומי על ידי עבודה על הלוגו של Google I/O ב-Pixelbook, בטלפון Pixel 3a וב-iPad Pro. אפשר לראות שהשינויים שביצעתי במכשיר אחד משתקפים בכל המכשירים האחרים.
אני רואה גם את כל הסמןים זזים. הסמן של Pixelbook זז באופן יציב, מכיוון שהוא נשלט על ידי משטח מגע, אבל הסמן של טלפון Pixel 3a והסמן של טאבלט ה-iPad Pro זזים ממקום למקום, מכיוון שאני שולטת במכשירים האלה באמצעות הקשה עם האצבע.
הצגת הסטטוסים של שותפי עריכה
כדי לשפר את חוויית שיתוף הפעולה בזמן אמת, פועלת אפילו מערכת זיהוי לא פעילה. בסמן של ה-iPad Pro מוצגת נקודה ירוקה כשאני משתמש בו. הנקודה הופכת לשחורה כשעוברים לכרטיסייה או לאפליקציה אחרת בדפדפן. כשנמצאים באפליקציית Excalidraw אבל לא עושים כלום, הסמן מראה שהמחשב לא פעיל, ומופיע הסמל של שלוש האותיות zZZ.
קוראים נלהבים של הפרסומים שלנו עשויים להיות נוטים לחשוב שזיהוי של חוסר פעילות בוצע באמצעות Idle Detection API, הצעה בשלב מוקדם שעובדה עליה בהקשר של Project Fugu. אזהרת ספוילר: לא. אמנם הייתה לנו הטמעה שמבוססת על ממשק ה-API הזה ב-Excalidraw, אבל בסופו של דבר החלטנו לבחור בגישה מסורתית יותר שמבוססת על מדידת תנועת הסמן ועל חשיפת הדף.
שלחנו משוב על כך ש-Idle Detection API לא פתר את התרחיש לדוגמה שלנו. אנחנו מפתחים את כל ממשקי ה-API של Project Fugu במקום פתוח, כך שכולם יכולים להצטרף ולהשמיע את קולם!
ליפיס על מה שמעכב את Excalidraw
אגב, שאלתי את lipis שאלה אחרונה לגבי מה לדעתו חסר בפלטפורמת האינטרנט של Excalidraw:
File System Access API הוא נהדר, אבל אתם יודעים מה? רוב הקבצים שחשובים לי נמצאים ב-Dropbox או ב-Google Drive, ולא בדיסק הקשיח. אני רוצה ש-File System Access API יכלול שכבה מופשטת כדי שספקי מערכות קבצים מרוחקים כמו Dropbox או Google ישתלבו איתה, ושהמפתחים יוכלו להשתמש בה כדי לתכנת. לאחר מכן, המשתמשים יכולים להירגע ולדעת שהקבצים שלהם בטוחים מול ספק שירותי הענן שהם סומכים עליו.
אני לגמרי מסכימה עם ליפיס, גם אני גרה בענן. אנחנו מקווים שהשינוי הזה יוטמע בקרוב.
מצב אפליקציה עם כרטיסיות
וואו! ראינו הרבה שילובי API מצוינים ב-Excalidraw. מערכת קבצים, טיפול בקובץ, לוח העריכה, שיתוף אינטרנט ויעד של שיתוף אינטרנט. אבל יש עוד משהו. עד עכשיו, יכולתי לערוך רק מסמך אחד בכל פעם. כבר לא. אנחנו שמחים להציג לכם לראשונה גרסה מוקדמת של מצב האפליקציה עם כרטיסיות ב-Excalidraw. כך זה נראה.
יש לי קובץ פתוח באפליקציית Excalidraw PWA שמותקנת במצב עצמאי. עכשיו אני פותחת כרטיסייה חדשה בחלון העצמאי. זו לא כרטיסייה רגילה בדפדפן, אלא כרטיסיית PWA. בכרטיסייה החדשה הזו אוכל לפתוח קובץ משני ולעבוד על שניהם בנפרד מאותו חלון של האפליקציה.
מצב האפליקציות בכרטיסיות נמצא בשלבי פיתוח מוקדמים, ולא הכל נקבע באופן סופי. אם זה מעניין אתכם, תוכלו לקרוא על הסטטוס הנוכחי של התכונה במאמר שלי.
סגירה
כדי להתעדכן לגבי תכונה זו ותכונות אחרות, כדאי לצפות בכלי המעקב שלנו לממשקי API של Fuugu. אנחנו שמחים מאוד לקדם את האינטרנט ולאפשר לכם לעשות יותר בפלטפורמה. שתמיד יהיה שיפור ב-Excalidraw, ושייצאו מכם אפליקציות מדהימות. אתם יכולים להתחיל ליצור ב-excalidraw.com.
אני מצפה לראות חלק מממשקי ה-API שהוצגו היום באפליקציות שלכם. קוראים לי תום. אפשר למצוא אותי בתור @tomayac ב-Twitter ובאינטרנט באופן כללי. תודה רבה שצפיתם, ושיהיה לכם המשך כנס Google I/O נעים.