Skip to content

Latest commit

 

History

16 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

חנות התוספים של אוצריא

חנות התוספים כתוכנה עצמאית לווינדוס: קובץ הרצה אחד, שמסנכרן את קטלוג התוספים מ-otzaria.org אל תיקייה שצמודה לו, מזהה איזו גרסה של אוצריא מותקנת במחשב, ומתקין את התוסף שתואם לה.

חנות התוספים.exe      ← זה כל מה שמפיצים
Data\                  ← נוצרת לידו בהרצה הראשונה
  catalog.json           הקטלוג הממוראה
  plugins\<id>\...       קובצי ה-.otzplugin, האייקונים וצילומי המסך
  logs\store-<תאריך>.log
  state.json             מה זוהה על ההתקנה של אוצריא במחשב הזה

מרגע שהסנכרון הסתיים התוכנה עובדת בלי אינטרנט: הקטלוג והקבצים יושבים ב-Data\, והתקנה אינה נוגעת ברשת. זה מה שמאפשר להעתיק את התיקייה לכונן נייד ולהתקין ממנה במחשב מנותק.

ולמי שאין לו רשת גם בפעם הראשונה מתפרסמת חבילה מלאה — אותו exe עם כל התוספים כבר בתוכו.

מה מיוחד כאן

‎0.5MB, קובץ אחד. אין מתקין, אין DLL לצד ה-exe, ואין תיקיית חילוץ. זה מושג בשלושה דברים:

ה-loader של WebView2 מקושר סטטית → אין WebView2Loader.dll
ה-CRT של Visual C++ מקושר סטטית (/MT) → אין vcruntime140.dll
קובצי הממשק דחוסים (LZMS) כ-resource בתוך ה-exe → אין תיקיית ממשק

מנוע התצוגה עצמו הוא ה-WebView2 Runtime שכבר נמצא במחשב (מותקן מראש בווינדוס 11, ומגיע עם Edge בווינדוס 10), ולכן הוא אינו נספר בגודל.

להשוואה: הגרסה הקודמת נבנתה ב-Flutter ושקלה ‎11.8MB — היא נשאה איתה מנוע רינדור שלם (flutter_windows.dll לבדו ‎21MB לא-דחוס), ופרסה ‎29MB לתיקיית app-files\ בהרצה הראשונה כדי להריץ משם קובץ הרצה שני.

החבילה המלאה

מי שמתקין במחשב מנותק צריך שהתוספים כבר יהיו אצלו. לכן מתפרסמת, לצד ה-exe הרזה, חבילה מלאה: אותו קובץ הרצה בדיוק, עם מראה שלמה של כל התוספים משורשרת לסופו.

Otzaria-Plugin-Store-Full.exe   ~96MB, הרצה אחת
        ↓
חנות התוספים.exe                0.52MB — עותק של עצמו בלי המטען
Data\                            כל התוספים, מוכנים

הרצה ראשונה פורסת את Data\, כותבת עותק רזה של עצמה, מפעילה אותו ויוצאת. הרזה שואל אם למחוק את קובץ ההתקנה — ולא מוחק מעצמו: אותה חבילה עשויה להיות מיועדת גם למחשב שני, ומחיקה שקטה של ‎96MB שהורדו הייתה מאלצת הורדה מחדש.

החילוץ מחליף את plugins\ ואת catalog.json במלואם, ולא ממזג. המראה היא נגזרת של (קטלוג, גרסאות היעד) ולא נתוני משתמש — זה בדיוק מה ש-pruneUnusedFiles כבר עושה, רק בבת אחת. state.json ו-logs\, שהם מקומיים למכונה, אינם נגעים.

⚠️ החותמת נכתבת אחרונה, אחרי שהקובץ האחרון נחת. חילוץ שנקטע משאיר תיקייה בלי חותמת, וההרצה הבאה עושה אותו שוב — המטען עדיין בתוך ה-exe. זו כל האטומיות שנדרשת, ולכן אין תיקייה זמנית ואין כפל מקום.

למה שרשור ולא resource

המטען אינו נדחס: .otzplugin הוא ZIP והתמונות webp/png, ו-LZMS עליהם מחזיר אחוזים בודדים תמורת פרישה של ‎96MB לזיכרון. שרשור לסוף ה-PE משאיר את הבינארי שפורסם בייט-לבייט — כך שהעותק הרזה שנכתב בחילוץ הוא בדיוק ה-exe שנבדק — ומאפשר חילוץ בזרימה בלי הקצאה גדולה אחת. RCDATA היה מחייב בנייה מחדש של ה-exe סביב המטען, כלומר בינארי אחר מזה שפורסם.

הפורמט מוגדר ב-native/tools/pack_overlay.mjs ונקרא ב-native/src/overlay.cpp.

מבנה

native/
  src/            ה-host ב-C++: חלון, WebView2, וגשר לקבצים/רשת/רג'יסטרי
  web/            הממשק וכל הלוגיקה — HTML/CSS/JS, בלי bundler ובלי npm
  test/           בדיקות `node --test`
  tools/          כלי בנייה ופיתוח, וכלי האריזה של החבילה המלאה
  third_party/    ה-SDK של WebView2 (מובא מ-nuget, אינו בגיט)
  build.ps1       הבנייה כולה
  VERSION         מספר הגרסה — מקום אחד
tool/
  set_version.sh      מציב את הגרסה (מריץ אותו ה-CI)
  make_app_icon.ps1   בונה את ה-ico ואת האייקון שבשורת הכותרת ממקור אחד

חלוקת העבודה בין ה-host ל-JS

ה-host הוא מעטפת טיפשה. כל הלוגיקה — המודלים, בחירת הבילד התואם, הסנכרון, פירוק הכותרות, קריאת ה-manifest — יושבת ב-native/web/js/. ה-host נותן חלון, ומגיש קבצים, רשת, רג'יסטרי ודיאלוג שמירה.

זה מכוון: הלוגיקה בצד ה-JS נבדקת ב-node --test בלי חלון ובלי ווינדוס, ומה שנשאר ב-C++ הוא קריאות מערכת שאין בהן החלטות.

לכן גם אין פרסר JSON ב-C++: JS → host הוא שדות מופרדים ב-U+001F (תו שאינו חוקי בנתיב ואינו מופיע בכתובת, ולכן אין מה לברוח ואין מה לטעות בפרסור), ו-host → JS הוא מחרוזת JSON שנבנית עם escaping בלבד.

בנייה

דורש Visual Studio (או Build Tools) עם כלי C++. אין תלות ב-CMake, ואין צורך להתקין SDK של שפה כלשהי.

.\native\build.ps1            # בנייה
.\native\build.ps1 -Run       # בנייה והרצה
.\native\build.ps1 -Clean     # מאפס את native\build\
.\native\build.ps1 -Debug     # עם סמלי ניפוי, בלי אופטימיזציה

הסקריפט מביא בעצמו את ה-SDK של WebView2 מ-nuget (גרסה נעוצה ב- native/tools/fetch_webview2.ps1), אורז את קובצי הממשק, וצורב את משאב הגרסה.

בדיקות

cd native && node --test

בלי שום תלות npm — node --test הוא המובנה. הבדיקות מכסות את הלוגיקה הטהורה: השוואת גרסאות, בחירת הבילד התואם, פירוק כותרות ההורדה, המודלים, התאריך העברי, וקורא ה-ZIP.

קורא ה-ZIP נבדק מול .otzplugin אמיתי ב-native/test/fixtures/, שנוצר בכותב ZIP חיצוני (System.IO.Compression של .NET) — ולא בקוד שלנו, כדי שהבדיקה לא תהיה מעגלית.

כלי פיתוח

# מריץ, מצלם את החלון, ומדפיס את היומן.
# -CropHeight שומר רק את הפיקסלים העליונים (אימות המסגרת בלבד).
.\native\tools\screenshot.ps1 -Out shot.png -CropHeight 300
# מביא את הקטלוג האמיתי מהאתר עם התמונות ובלי קובצי ההתקנה — כדי לראות
# את המסכים עם נתונים אמיתיים בלי להוריד מאות MB.
node native/tools/seed_catalog.mjs native/build/Data
# בונה מראה מלאה בהרצת הסנכרון האמיתי, ואורז אותה לחבילה — בדיוק מה
# שהג'וב עושה, כדי שאפשר יהיה לשחזר תקלה בלי GitHub.
node native/tools/pack_mirror.mjs Data
node native/tools/pack_overlay.mjs "native/build/חנות התוספים.exe" Data Full.exe
node native/tools/unpack_overlay.mjs Full.exe verify   # ואז diff -r Data verify
# מחלץ מחדש את האייקונים מגופני Fluent UI אל native/web/js/ui/icons.js.
# להריץ רק כשמוסיפים אייקון (ראו ICONS שבסקריפט).
node native/tools/extract_icons.mjs

האייקונים

22 האייקונים שבממשק הם הווקטורים המדויקים של Fluent UI System Icons (MIT) — אותם שגרסת ה-Flutter הציגה. extract_icons.mjs הוא פרסר TTF מינימלי (cmap, loca, glyf, כולל גליפים מורכבים) שמוציא מהגופנים רק את מה שבשימוש: ‎15KB במקום ‎4.5MB של שני גופנים.

הצבעים

ערכות הצבעים ב-native/web/css/tokens.css הן הפלט המדויק של ColorScheme.fromSeed של Material 3 מגרסת ה-Flutter (זרעים #2C1B02 לבהיר ו-#9C27B0 לכהה), שנשפך לקובץ לפני שה-Flutter הוסר.

⚠️ אין לערוך את הערכים ביד — הפלטה נבנית במרחב HCT, וגוון שנבחר "בעין" יוצא מהמשפחה.

גרסאות ופרסום

הגרסה היא מספר שלם אחד (1, 2, 3…) ויושבת ב-native/VERSION בלבד. כל דחיפה ל-main שכל הבדיקות בה עברו מקדמת את המספר, מתייגת vN, ומפרסמת release עם ה-exe — ראו .github/workflows/ci.yml.

התוכנה בודקת פעם אחת בהרצה אם יצא release חדש ומציגה על כך שורה. אין עדכון עצמי: הסיפור מסתיים בכתובת שנפתחת בדפדפן.

החבילה המלאה — .github/workflows/bundle.yml

רץ יומית, וגם כשריצת CI הצליחה ב-main (‏exe חדש), וגם ביד. אינו מקדם גרסה ואינו מתייג v<N>: התג הוא bundle מתגלגל, תמיד אותו אחד, מתעדכן במקום.

שתי סיבות, שתיהן ב-store_release_client.js:

  • isStoreReleaseTag פוסל כל תג שאינו ^v?\d+$, ולכן bundle נעלם מבדיקת העדכון — אחרת כל אריזה הייתה מודיעה למשתמשים על "גרסה חדשה" כשהקוד זהה.
  • fetchLatestStable קורא דף אחד של 30 releases. פרסום חדש בכל ריצה היה דוחף אחרי כמה חודשים את כל ה-v<N> מחוץ לדף, והתראת העדכון הייתה מפסיקה לעבוד בשקט.

הג'וב זורע את עצמו מהחבילה הקודמת לפני שהוא מסנכרן, וכך #plan מוריד מ-otzaria.org רק את הדלתא: עדכון של תוסף אחד עולה קובץ אחד ולא ‎96MB. בדרך אגב זה מריץ ב-CI בדיוק את המסלול האינקרמנטלי שכונן של משתמש עובר בעדכון, ולא "מראה ריקה" שאיש אינו חי בה.

מה נחשב שינוי נקבע ב-mirror_changed.mjs, בהשוואת התוצאה ולא בגיבוב של ה-API. ⚠️ המונים (downloadCount, הדירוגים) מנוטרלים מההשוואה: נמדד שסנכרון שדילג על כל 38 התוספים ולא הוריד דבר עדיין הפיק קטלוג שונה, וכל ההבדל היה מונה ההורדות של תוסף אחד — כלומר בלי הנטרול הג'וב היה מפרסם ‎96MB בכל ריצה.

⚠️ לא רק המראה נבדקת אלא גם ה-exe עצמו: כשמתפרסם בינארי חדש המראה אינה משתנה, והשוואת המראה לבדה הייתה משאירה את החבילה עם exe ישן לנצח. הג'וב מחלץ את ה-exe מהחבילה הקודמת (unpack_overlay.mjs --slim) ומשווה בייטים.

הערות

  • תיקיית הנתונים תמיד צמודה לקובץ ההרצה ואינה ניתנת לשינוי — זו הדרישה של עבודה מכונן נייד. כשאי אפשר לכתוב בה אבל יש בה קטלוג, התוכנה רצה במצב קריאה (ההתקנות כותבות למחשב) והיומן עובר ל- %LOCALAPPDATA%.
  • המאגר הפנימי של ה-WebView2 יושב ב-%LOCALAPPDATA% ולא ב-Data\. נמדד שהוא ‎9.5MB ב-189 קבצים אחרי הרצה אחת ו-‎14MB אחרי שמונה, ומתוכם ~‎5MB הם מטמון שיידרים שנגזר מכרטיס הגרפי של המחשב — כלומר חסר תוקף במחשב אחר. גם מחיקה בסיום נבדקה ואינה עובדת: ה-WebView2 מריץ תהליכי-בן ששורדים את סגירת החלון ומחזיקים מנעולים על הקבצים.
  • שם קובץ ההרצה אינו מכיל "אוצריא" בכוונה: זיהוי ההתקנה של אוצריא עובד לפי השם, ו-exe בשם כזה שיושב ב-C:\אוצריא היה נבחר שם כהתקנה של אוצריא עצמה. ⚠️ הזיהוי מתאים גם ל-otzaria הלטיני, ולכן גם Otzaria-Plugin-Store-Full.exe — קובץ ההתקנה של החבילה, שהמשתמש עשוי לבחור להשאיר — נמצא ב-OUR_OWN_EXE_NAMES שב-otzaria_probe.js. ‏selfPath אינו מכסה אותו: אחרי הפריסה מי שרץ הוא ה-exe הרזה.
  • ההתקנה נמסרת לאוצריא דרך otzaria://plugin/install-local — ולא בפרישת ה-ZIP בעצמנו, כי אוצריא מנהלת רישום פנימי לתוספים המותקנים ופרישה ידנית עוקפת אותו. כשנתיב ההתקנה ידוע ה-URL נמסר לקובץ ההרצה עצמו ולא למערכת ההפעלה, כי התקנה ניידת אינה רושמת את הסכימה בכלל.

רישיון

GPL-3.0 — ראו LICENSE.

About

חנות התוספים של אוצריא כאפליקציה עצמאית — EXE אחד 0.5MB

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages