כל טכנולוגיה מתקדמת מספיק לא ניתנת להבדלה מכשף. אלא אם אתם מבינים את זה. קוראים לי תומס שטיינר ואני עובד ב-Google בתחום קשרי המפתחים. בסקירה הזו של ההרצאה שלי ב-Google I/O, אציג כמה ממשקי API חדשים של Fugu ואראה איך הם משפרים את תהליכי השימוש העיקריים ב-Excalidraw PWA. כך תוכלו לקבל השראה מהרעיונות האלה ולהחיל אותם על האפליקציות שלכם.
איך הגעתי ל-Excalidraw
אני רוצה להתחיל עם סיפור. ב-1 בינואר 2020, Christopher Chedeau, מהנדס תוכנה ב-Facebook, צייץ על אפליקציית ציור קטנה שהוא התחיל לעבוד עליה. בעזרת הכלי הזה אפשר לצייר תיבות וחצים שנראים כמו קריקטורות ומצוירים ביד. למחרת, תוכלו גם לצייר אליפסות וטקסט, לבחור אובייקטים ולהזיז אותם. ב-3 בינואר, האפליקציה קיבלה את השם Excalidraw, וכמו בכל פרויקט צדדי טוב, רכישת שם הדומיין הייתה אחת הפעולות הראשונות של כריסטופר. עכשיו תוכלו להשתמש בצבעים ולייצא את כל הציור כקובץ PNG.
ב-15 בינואר, כריס פרסם פוסט בבלוג שמשך הרבה תשומת לב ב-Twitter, כולל שלי. הפוסט התחיל עם כמה נתונים סטטיסטיים מרשימים:
- 12,000 משתמשים פעילים ייחודיים
- 1,500 כוכבים ב-GitHub
- 26 תורמי תוכן
זה לא רע בכלל לפרויקט שהתחיל רק לפני שבועיים. אבל מה שעורר את העניין שלי היה בהמשך הפוסט. כריסטופר כתב שהוא ניסה משהו חדש הפעם: העניק לכל מי ששלח בקשת משיכה גישה ללא תנאים לשליחת התחייבויות (commits). באותו יום שבו קראתי את הפוסט בבלוג, פרסמתי בקשת משיכה להוספת תמיכה ב-File System Access API ל-Excalidraw, ותיקון של בקשת תכונת שמישהו שלח.
בקשת ה-pull שלי אוחדה יום לאחר מכן, ומאותו רגע הייתה לי גישה מלאה לביצוע השינויים. למותר לציין שלא נהגתי לרעה בסמכות שלי. וגם אף אחד אחר מתוך 149 האנשים שתרמו עד כה.
היום, Excalidraw היא אפליקציית Progressive Web App מלאה שניתן להתקין, עם תמיכה אופליין, מצב לילה מדהים, כן, וגם היכולת לפתוח ולשמור קבצים בזכות File System Access API.
Lipis מסביר למה הוא מקצה כל כך הרבה זמן ל-Excalidraw
זהו סוף הסיפור שלי על הדרך שבה הגעתי ל-Excalidraw, אבל לפני שאכנס לעומק לחלק מהתכונות המדהימות של Excalidraw, אשמח להציג לכם את Panayiotis. Panayiotis Lipiridis, שנקרא באינטרנט פשוט 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 עם חלופה קודמת, כך שאפשר להשתמש בה בכל דפדפן.
קודם אראה לכם את ההטמעה כשיש תמיכה ב-API. אחרי שמסכימים על סוגי ה-MIME וסיומת הקבצים המותרים, החלק המרכזי הוא קריאה לפונקציה showOpenFilePicker()
של File System Access API. הפונקציה מחזירה מערך של קבצים או קובץ יחיד, בהתאם למספר הקבצים שנבחרו. כל מה שנותר הוא להוסיף את ה-file handle לאובייקט הקובץ, כדי שניתן יהיה לאחזר אותו שוב.
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 ל-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 ועל סיומות הקבצים. אם כבר שמרתם קובץ ויש לכם מזהה קובץ, לא צריך להציג תיבת דו-שיח לשמירה. אבל אם זו הפעם הראשונה ששומרים את הקובץ, תוצג תיבת דו-שיח של קובץ והאפליקציה תקבל חזרה את ה-handle של הקובץ לשימוש עתידי. בשלב הבא פשוט כותבים לקובץ באמצעות זרם לכתיבה.
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, אפשר גם לשמור את השינויים באופן מיידי. אין צורך לעבור דרך תיבת דו-שיח לשמירת קובץ, כי ה-handle הנדרש של הקובץ הושג מהפעולה של גרירה ושחרור.
הסוד הוא לבצע קריאה ל-getAsFileSystemHandle()
על הפריט data transfer כשיש תמיכה ב-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
וחותמת זמן. כדי לבדוק מה הוא מכיל, לוחצים על שלוש הנקודות ואז על 'שיתוף'. אחת מהאפשרויות שמופיעות היא 'Excalidraw'. כשהקשתי על הסמל, ראיתי שהקובץ מכיל שוב רק את הלוגו של I/O.
Lipis בגרסה הקודמת של Electron
דבר אחד שאפשר לעשות עם קבצים שעדיין לא דיברתי עליו הוא ללחוץ עליהם פעמיים. בדרך כלל, כשלוחצים לחיצה כפולה על קובץ, נפתחת האפליקציה שמשויכת לסוג ה-MIME של הקובץ. לדוגמה, עבור .docx
זה יהיה Microsoft Word.
בעבר הייתה לאפליקציית Excalidraw גרסה ל-Electron שתומכת בשיוכים כאלה של סוגי קבצים, כך שלחיצה כפולה על קובץ .excalidraw
פותחת את אפליקציית Excalidraw ל-Electron. ליפס, שכבר פגשנו קודם, הוא היוצר של Excalidraw Electron והוא גם זה שהחליט להפסיק את השימוש בו. שאלתי אותו למה לדעתו אפשר להוציא משימוש את הגרסה ל-Electron:
אנשים ביקשו אפליקציית Electron כבר מההתחלה, בעיקר כי הם רצו לפתוח קבצים בלחיצה כפולה. התכוונו גם להציב את האפליקציה בחנויות אפליקציות. במקביל, מישהו הציע ליצור אפליקציית PWA במקום זאת, אז עשינו את שניהם. למזלנו, הכרנו את ממשקי ה-API של 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 שמספק לי מערך של מאחזי קבצים לעבודה. אני מתעניין רק ב-handle הראשון, וממנו אני מקבל 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 של הלוח, אפשר לעיין במאמר של 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 אבל לא עושים כלום, הסמן מראה שהמחשב לא פעיל, ומופיע הסמל של שלוש האותיות zZZ.
קוראים ותיקים של הפרסומים שלנו עשויים לחשוב שהזיהוי של מצב חוסר פעילות מתבצע באמצעות Idle Detection API, הצעה בשלב מוקדם שעל הפרויקט שלה עבדו בהקשר של Project Fugu. אזהרת ספוילר: לא. אמנם הייתה לנו הטמעה שמבוססת על ממשק ה-API הזה ב-Excalidraw, אבל בסופו של דבר החלטנו לבחור בגישה מסורתית יותר שמבוססת על מדידת תנועת הסמן ועל חשיפת הדף.
שלחנו משוב על הסיבה לכך ש-Idle Detection API לא פתר את התרחיש לדוגמה שלנו. כל ממשקי ה-API של Project Fugu מפותחים באופן פתוח, כך שכל אחד יכול להצטרף ולבטא את דעתו.
Lipis על מה שעומד בדרכו של Excalidraw
אגב, שאלתי את lipis שאלה אחרונה לגבי מה לדעתו חסר בפלטפורמת האינטרנט של Excalidraw:
ממשק File System Access API נהדר, אבל אתם יודעים מה? רוב הקבצים שחשובים לי נמצאים ב-Dropbox או ב-Google Drive, ולא בדיסק הקשיח. הייתי רוצה ש-File System Access API יכלול שכבת הפשטה לספקים של מערכות קבצים מרוחקות, כמו Dropbox או Google, כדי שאפשר יהיה לשלב אותם ולפתח קוד עבורם. כך המשתמשים יכולים להיות רגועים ולדעת שהקבצים שלהם בטוחים אצל ספק הענן שהם סומכים עליו.
אני מסכים לחלוטין עם lipis, גם אני חי בענן. אני מקווה שהתכונה הזו תוטמע בקרוב.
מצב אפליקציה עם כרטיסיות
וואו! ראינו הרבה שילובי API מצוינים ב-Excalidraw. מערכת קבצים, טיפול בקובץ, לוח העריכה, שיתוף אינטרנט ויעד של שיתוף אינטרנט. אבל יש עוד משהו. עד עכשיו, תמיד יכולתי לערוך רק מסמך אחד בכל פעם. כבר לא. אנחנו שמחים להציג לכם לראשונה גרסה מוקדמת של מצב האפליקציה עם כרטיסיות ב-Excalidraw. כך זה נראה.
פתחתי קובץ קיים ב-Excalidraw PWA המורחב שפועל במצב עצמאי. עכשיו פותחים כרטיסייה חדשה בחלון העצמאי. זו לא כרטיסייה רגילה בדפדפן, אלא כרטיסייה של אפליקציית PWA. בכרטיסייה החדשה הזו אוכל לפתוח קובץ משני ולעבוד על שניהם בנפרד מאותו חלון של האפליקציה.
מצב האפליקציה עם כרטיסיות נמצא בשלבי פיתוח מוקדמים, ולא הכל נקבע באופן סופי. אם זה מעניין אותך, כדאי לקרוא את המאמר שלי על הסטטוס הנוכחי של התכונה הזו.
סגירה
כדי להתעדכן בתכונה הזו ובתכונות אחרות, כדאי לעקוב אחרי מעקב אחר Fugu API. אנחנו שמחים מאוד לקדם את האינטרנט ולאפשר לכם לעשות יותר בפלטפורמה. שתמיד יהיה שיפור ב-Excalidraw, ושייצאו מכם אפליקציות מדהימות. אתם יכולים להתחיל ליצור ב-excalidraw.com.
אני לא יכול לחכות לראות חלק מה-API שציינתי היום מופיעים באפליקציות שלכם. שמי Tom, אפשר למצוא אותי ב-Twitter ובאינטרנט בכלל בתור @tomayac. תודה רבה שצפיתם, ושיהיה לכם המשך כנס Google I/O נעים.