תיקונים ושיפורים - #6
Merged
Y-PLONI merged 19 commits intoAug 28, 2026
Merged
Conversation
המנוע מתייחס לניקוד ולטעמים כאל מפרידי מילה, ולכן לחיצה כפולה על „בְּרֵאשִׁ֖ית" בחרה רסיס של אות אחת. נמדד ב-Chrome אמיתי, גם על המנוע לבדו וגם על ה-dist הארוז מ-`file://`; הטבלה נמצאת ב-docs/engine-gaps.md. התיקון הוא שכבה חיצונית בלבד — אין נגיעה במנוע. אחרי הלחיצה קוראים את הבחירה ב-`doc.selection.current()`, פותחים חלון טקסט של 90 תווים לכל צד ב-`doc.ranges.resolve()`, מרחיבים את גבולות המילה על התווים שהמנוע חתך (ניקוד, טעמים, גרש וגרשיים) ומחילים את התוצאה ב-`ui.selection.apply()`. הכול דרך ה-API הציבורי של SuperDoc. ספירת הלחיצות היא שלנו ולא של הדפדפן: באוצריא ה-WebView מוזן דרך `SendMouseInput` של WebView2, שמקבל DOWN/UP/MOVE בלבד — בלי `LEFT_BUTTON_DOUBLE_CLICK` — ולכן `clickCount` נגזר מהיוריסטיקה של Chromium ואינו זהה בין הסביבות. אנחנו סופרים על `mousedown`/`click` לפי ספי Windows (500ms, 5px), מתעלמים מגרירה, ודוחים זרע ארוך מ-90 תווים שהוא בחירה קודמת שלא התחלפה. שלוש לחיצות כבר עבדו במנוע, ובכל זאת נשלחות גם מאיתנו — כדי שהתנהגות הבחירה לא תהיה תלויה בספירה של המארח.
הסעיף תיאר עד עכשיו תסמין בלבד. מדידה שמשווה באותה נקודה את `elementFromPoint`, את `caretRangeFromPoint`, את `getSelection()` ואת `doc.selection.current()` מראה שהקריסה מתרחשת בדיוק בצעד שבו הסמן חדל להיות מעל תיבת שורה ונכנס לרצועה שבין השורה האחרונה לתחתית העמוד. `caretRangeFromPoint` באותן קואורדינטות ממשיך להחזיר את המיקום המהודק הנכון, ו-`getSelection()` ריק לאורך כל הגרירה — כלומר המנוע מחזיק את המיקום בעצמו ואינו יורש את ההידוק של הדפדפן. בקרה: פסקה קצרה שנוספה אחרי הארוכה מזיזה את הקריסה מטה יחד עם הטקסט. וזה גם מסביר את „91" מהמדידה הראשונה — שורה בקובץ ההוא היא כ-90 תווים.
בנתיב `node_modules` ישב קישור סימבולי מנוטר בגיט (mode 120000) אל `/Users/david/Documents/otzaria-others/otzaria-word-editor/node_modules` — נתיב ממכונת macOS אחרת, שנכנס לקומיט 37de9f7. ב-checkout בווינדוס הוא מתממש כקובץ רגיל בן 70 בתים ובו הנתיב עצמו. npm יוצר בנתיב הזה **תיקייה**, ושני אלה אינם יכולים לדור באותו מקום: `npm install` נכשל בכל clone חדש, ואיתו כל בנייה וכל שער. `.gitignore` לא עצר את זה, וזה גם מה שמונע חזרה: `node_modules/` עם לוכסן תופס תיקיות בלבד, ולכן ערך אחר באותו שם עבר דרכו בשקט. בלי הלוכסן הדפוס תופס את שניהם.
מיכל העמודים של המנוע הוא flex column עם `gap: 24px`, כלומר **בין** עמודים
כבר יש מרווח — אבל מעל הראשון ומתחת לאחרון היה `0px`. נמדד ב-CDP על ה-dist
הארוז בארבעה אחוזי זום: בין העמודים 24/14/36/72 (100%/60%/150%/300%),
ובשני הקצוות אפס בכולם. על המסך ראש העמוד נושק לרצועה ותחתיתו לשורת המצב,
והמסמך נראה חתוך במקום מונח על משטח.
`padding-block: 24px` על אותו מיכל. 24 ולא ערך משלנו — זה בדיוק ה-`gap`
שהמנוע כותב, ולכן שלושת המרווחים יוצאים זהים וגדלים יחד עם הזום, כמו ב-Word.
`padding` על המיכל ולא `margin` על העמודים: המרווח האמצעי כבר מגיע מה-`gap`,
ושתי מערכות מרווח על אותה ערימה הן מרווח כפול באמצע. `padding-block` ולא
`padding` — הציר האופקי שייך לנוסחת המרכוז בזום, ו-`50%` שבה נפתר מול תיבת
התוכן של המיכל הזה.
`!important` מפני שהמנוע כותב `padding: 0px` inline על המיכל בכל ציור. זה
CSS ולא JavaScript, ולכן הכלל חל כבר בציור הראשון — בלי הבהוב ובלי „מי כתב
אחרון”, אותו שיקול שמתועד ב-styles/engine-chrome.css.
מה שנבדק לפני שזה נכתב: למנוע יש `PageGeometryHelper` שמחשב „מרחק מראש
המיכל אל ראש עמוד N”, ובמרחב הזה `getPageTop(0)` הוא אפס — כלומר ריפוד היה
יכול להזיז כל שכבה שנשענת על החישוב במקום על ה-DOM. נמדד: קליק על שורה
בעמוד השני מציב את הסמן בדיוק על ראש השורה, סטייה 0px, עם הריפוד ובלעדיו.
ההדפסה מאפסת את הריפוד (print.css). זה לא קישוט: מסמך של שלושה עמודים יצא
שלושה גיליונות עם האיפוס, וחמישה בבקרה בלעדיו — `@page { margin: 0 }` הופך
דחיפה של 24px לגלישה.
scripts/page-gutter-probe.mjs (`npm run check:gutter`, ומחובר ל-verify) מודד
שהמרווח מעל, בין ומתחת זהים בכל אחוז זום, ושהוא מאופס במדיית print. כלומר
היום שבו המנוע ישנה את ה-`gap` שלו ייפול שם, ולא אצל המשתמש.
הבאנדל הארוז הוא 16MB, וב-file:// הפריסה וההרצה שלו לוקחות כשתי שניות. שתי תגיות הסקריפט ישבו ב-<head> בלי defer, ולכן ה-<body> לא נפרס באותו זמן — מסך לבן בלי שום סימן שמשהו קורה. נמדד: צביעה ראשונה ב-2135ms. התגיות מוזרקות עכשיו מטוען inline אחרי הצביעה הראשונה, ו-index.html נושא מסך טעינה (HTML+CSS+JS) שמדווח שלבים ומאמץ את ערכת הנושא של אוצריא. הפלטה זהה ל-tokens.css של התוסף. התגלה תוך כדי: הקריאה הראשונה לערכת הנושא הייתה חד-פעמית מה-latch, אבל plugin.boot מגיע אחרי שהמסך כבר צויר — כך שהוא תמיד פספס אותה, ופס ההתקדמות נשאר כחול במקום לאמץ את הצבע האמיתי. עכשיו יש גם מאזין. scripts/startup-probe.mjs (npm run check:startup) משגר boot באיחור מכוון ומאמת גם את הצביעה וגם את אימוץ הצבע בדפדפן אמיתי.
נמדד מול המנוע האמיתי: doc.clipboard.insert בלי target מכניס את התוכן אחרי הפסקה האחרונה גם כשיש בחירה חיה בתחילת המסמך, למרות שהחוזה מתאר "the current model selection" — ההנחה הזו הייתה שגויה. עם target מהבחירה החיה, ההדבקה נכנסת בדיוק בסמן. אומת מקצה לקצה: לחיצה על כפתור ״הדבק״ האמיתי, עם הרשאת לוח ובחירה בתחילת המסמך, מכניסה את התוכן לפסקה הראשונה ולא בסוף. זה גם ההסבר לבחירת פריט אחר מהיסטוריית Win+V שלא נדבק: הלוח הפנימי היה נקרא בכל פעם שלוח המערכת חסום לקריאה, גם כשהוא רק חסום לקריאה ולא נכתב אליו — ומחזיר תוכן ישן שהועתק בתוך התוסף עצמו במקום מה שהמשתמש בפועל בחר. עכשיו הלוח הפנימי מסומן mirrored כשהתוכן גם הגיע ללוח המערכת, וההדבקה נופלת אליו רק כשהוא העותק היחיד שקיים.
נמדד ב-Chrome אמיתי: כפתור "יישור לימין" הבהב 34 פעמים ב-40 שניות הקלדה. המנוע מקפל "מעורב" ו"עוד לא נפתר" לאותו undefined בכל קריאה אסינכרונית שמתאפסת עם כל תו, והרצועה הציגה זאת כ"אין עיצוב". הפתרון מחזיק את הערך האחרון הידוע רק כשידוע ש-undefined אינו יכול להיות האמת (סמן מכווץ, או קריאה שטרם התיישבה) — ולא כשבחירת טווח באמת מעורבת, ששם undefined הוא התשובה הנכונה. npm run check:readout מודד את זה בדפדפן אמיתי ונוסף ל-verify.
…דה, והסבר מתחתיה כרטיס אחד לכל התוכנה (TooltipLayer.vue), שמאזין במסירה ומכבה את ה-title המולד בזמן שהוא מוצג. פקד שכבר יש לו title — כולל כפתור מנוטרל, שאירועי עכבר אינם מגיעים אליו — מקבל את העיצוב החדש בלי חיווט. RibbonButton מוסיף שדה description חדש, ו-HomeTab מוזן בהסברים לכל כפתוריו.
…ם גופן הממשק הכללי
…חוזר כשההדבקה יצרה פסקה
… אינה נכנסת לשום מקום
…פך אותו לעוגן
כיבוי הטולטיפ של מערכת ההפעלה מסיר את `title` מהעוגן הפעיל. בפקד שאין לו
`data-tip-title` משלו — הפס העליון, שורת המצב, לוח הצבעים, בוררי הגופן,
כלומר בדיוק „הכיסוי המלא ללא חיווט” שהקובץ מבטיח — התכונה הזאת היא מה
שהתאים אותו ל-TIP_ANCHOR_SELECTOR, ועם הסרתה הוא חדל להיות עוגן.
נמדד ב-Chrome על ה-dist הארוז, על `.word-app-badge` שבפס העליון:
after hover: {"tip":true,"text":"וורד לאוצריא"} ← title הוסר
after tiny move: {"tip":false} ← תזוזה של פיקסל
`anchorAt` מחזיר null בשני המסלולים (גם `elementFromPoint` אינו עוזר — הוא
מחזיר את אותו אלמנט חסר-התכונה), `scheduleHide` רץ, `restoreNative` מחזיר
את ה-title, וכעבור 400ms הכול חוזר חלילה.
התיקון אינו מוחק את התוכן אלא **מעביר** אותו ל-`data-tip-title` לאורך
ההשהיה, ומחזיר ביציאה. האלמנט נשאר עוגן, `readTip` מחזיר את אותו תוכן
בדיוק — הוא קורא את שתי התכונות באותה נפילה — ומערכת ההפעלה אינה מציירת
דבר. פקד שכבר מחווט אינו מושאל ואינו נדרס.
שני שומרים: tests/component/tooltip-layer.test.ts (נופל על הקוד שלפני
התיקון — נמדד) ותזוזה שנייה על פקד ללא חיווט ב-scripts/tooltip-probe.mjs.
הבאנדל הוא IIFE, ו-main.ts מרכיב את Vue במיקרו-טסק בתוך אותה הרצה — כלומר התחנה 68 („מכין את סביבת העריכה…”) מדווחת **לפני** שאירוע ה-load של app.js יורה את 55. מסך הטעינה בולע יעד נמוך מהנוכחי, ובולע יחד איתו גם את הטקסט. נמדד ברצף שהוצג בפועל על ה-dist הארוז: מתחיל · טוען את מנוע המסמכים… · מכין את סביבת העריכה… · פותח את המסמך… · מוכן התחנה שבאמצע פשוט אינה קיימת. מכאן שהטוען מדווח מעתה על השלב ש**מתחיל** ולא על זה שנגמר: ההזרקה עצמה מדווחת 22 („טוען את מנוע המסמכים…”, כי אז הבאנדל של המנוע מתחיל לרדת), וסיום ה-workers מדווח 55 („מרכיב את הממשק…”, כי אז app.js מתחיל להיפרס). אחרי התיקון כל חמש התחנות מופיעות. השער: הבדיקה הסטטית ב-tests/unit/splash.test.ts מודדת סדר מספרים ואינה יכולה לראות סדר בזמן, ולכן check:startup רושם מעתה את רצף הטקסטים שהוצגו בפועל ונכשל על כל תחנה שהטוען מצהיר עליה ולא הגיעה למסך. אומת: על הקוד שלפני התיקון השער נופל בדיוק על „מרכיב את הממשק…”.
v2.0.0 כבר קיים כ-Release, ו-release.yml מדלג על יצירת Release ועל הפרסום
לחנות כשהתג קיים ("Check if version release already exists"). כלומר מיזוג
בלי העלאת גרסה מעדכן רק את ערוץ v2-dev, והחנות דוחה ממילא מספר גרסה חוזר.
מקור האמת הוא public/manifest.json, כמתועד ב-README; package.json אינו
נארז ואינו נבדק על ידי הוולידטור, ולכן הוא נשאר כפי שהוא.
…istInstalled `src/types/otzaria_plugin.d.ts` הוא עותק מדויק של מה שאוצריא מפרסמת ב-docs/plugin-sdk, ו-check:sdk נכשל מולו (הוא מדלג ב-CI, כי המקור אינו שם, ולכן הסחיפה לא נעצרה בשום שער אוטומטי). הועתק מ-c8b8023d8 — אותו קומיט בדיוק כמו origin/dev. מה נכנס: - `typography.uiFontFamily` — גופן הממשק, נפרד מגופן הקריאה, מ-0.9.97. `host/theme.ts` מחיל היום `fontSize` ו-`lineHeight` בלבד ומדלג במפורש על `fontFamily` (גופן הקריאה אינו של הממשק). `uiFontFamily` הוא דווקא כן של הממשק, ולכן ההחלה שלו היא שינוי התנהגות — נשאר לדיון נפרד ולא נגרר לכאן. - `RawBookLink` / `GetRawLinksResult` ו-`library.getRawLinks`. - `InstalledPlugin` ו-`plugin.listInstalled`, שהועלה מ-`@internal` לחוזה מתועד. - ניסוח: „FluentUI icon” → „icon (see ICONS.md)”. אף אחד מהם אינו נצרך היום בתוסף, ולכן זהו סנכרון בלבד: typecheck ו-1935 הבדיקות עוברים בלי שינוי נוסף, ו-check:sdk עובר מול המקור.
Y-PLONI
force-pushed
the
fix/word-double-click-selection
branch
from
August 28, 2026 12:21
bbf6127 to
342cfc4
Compare
Y-PLONI
added a commit
that referenced
this pull request
Aug 29, 2026
באג #6 בסקר הפקדים: לחיצה על "מברשת עיצוב" מפעילה את הפקודה copy-format (הכפתור נדלק, cmd(copy-format).active עובר ל-true), אבל סימון היעד לא מחיל דבר — ה-rPr של הטקסט ביעד נשאר ריק בכל מסלול. הסיבה: ui.formatPainter הוא "DOM listener coordination" בלבד — הוא מחיל רק כשמישהו קורא ל-notifyPointerUp/notifyKeyUp שלו. במנוע הרגיל זה SuperToolbar עושה, אבל האפליקציה בונה את המנוע עם ui: false (create-editor.ts), ולכן SuperToolbar אף פעם לא קם ואיש לא קורא לפעולות האלה. חימוש בלי מי שיפעיל את ההחלה. התיקון: מודול חדש src/engine/format-painter.ts שמחווט מאזינים על ה-container של המנוע (כמו word-selection.ts), ומשתחרר ב-onDispose (create-editor.ts). שני עידונים מעבר לחיקוי הפשוט של SuperToolbar, שנמצאו ע"י קריאת המימוש הפנימי של maybeApply/applyFormatPainter ואומתו בדפדפן חי: 1. לחיצה או ניווט מקלדת שרק ממקמים סמן (בלי גרירה/Shift) מפעילים במנוע מסלול "צביעת פסקה על הסמן" שמכבה את המצב החמוש גם כשאין שינוי לצייר — ולכן notifyPointerUp/notifyKeyUp נקראים רק על גרירה אמיתית או Shift. 2. הרחבת בחירה בכמה לחיצות מקש נפרדות (לא החזקה רציפה אחת) מייצרת כמה keyup נפרדים; קריאה מיידית על הראשון מחילה על תו אחד בלבד ומסיימת את המצב החמוש לפני שההרחבה הושלמה. נפתר בדבאונס קצר (250ms) על הקריאה בפועל. בדיקות חדשות ב-tests/unit/format-painter.test.ts: רישום/שחרור המאזינים, הסייג (קליק תמים לא מחיל, גרירה/Shift כן), והדבאונס. אומת בדפדפן חי (scripts/qa/home-font-qa.mjs, שלב 7 מבודד): אחרי סימון היעד, ה-rPr שלו מכיל בפועל <w:sz w:val="24"/><w:rFonts w:ascii="Arial" w:hAnsi="Arial"/><w:b/> — בדיוק העיצוב שהועתק מהמקור. שאר השלבים (הדגשה, נטוי, קו תחתון/חוצה, כתב עילי/תחתי, בוררי גופן/גודל, צבעים) נבדקו יחד בריצה מלאה וממשיכים לעבוד; שלב 5 (דיאלוג גופן מתקדם) נתקע בתקלת קלט ידועה ובלתי קשורה (CDP, מתועדת בסקריפט עצמו) שקדמה לשינוי הזה.
Nathaniel-260
added a commit
to Nathaniel-260/otzaria-word-editor
that referenced
this pull request
Sep 2, 2026
ב-`main` יש רשומה במצב `120000` בשם `node_modules`, שמצביעה לנתיב מוחלט במכונה של מי שיצר אותה (`/Users/david/…`). זה הקישור הסמלי היחיד במאגר בכל ההיסטוריה. מה שזה עושה, נמדד: `git checkout` של ה-main הזה בעץ עבודה שיש בו התקנה **מחליף את התיקייה `node_modules` בקובץ של 70 בייט** — כלומר מוחק את ההתקנה. זה קרה כאן על עץ עבודה שרק דקות לפני כן הושלמה בו התקנה מלאה. **ולמה בשקט:** `node_modules/` גורם לגיט להתעלם מהתיקייה, ולכן השומר „המחיקה הזאת תדרוס קבצים לא-מנוטרים” אינו חל. `checkout` יוצא 0 ואינו מדפיס דבר. זה הצירוף שקובע — הרשומה מצד אחד, ותבנית ההתעלמות מצד שני. ב-Windows הרשומה מתממשת כקובץ טקסט שתוכנו הנתיב, מפני ש-`core.symlinks=false` בקונפיג המערכתי של Git for Windows. זו הגדרה ולא מגבלת מערכת הפעלה: המכונה כן יוצרת קישורים סמליים וגיט מנטר אותם כ-`120000`. ב-Unix הרשומה מתממשת כקישור תלוי שאינו מצביע לשום מקום. מה שזה **אינו** עושה: להפיל את ה-CI. `npm ci`/`npm install` מדווח `npm warn reify Removing non-directory …` ומתאושש — ב-`@npmcli/arborist` הבדיקה היא `lstat`, ולכן גם קישור תלוי ב-Linux אינו נראה כתיקייה ונמחק באותו מסלול. הלוג של `Release` על `8c776e9` מדפיס את האזהרה הזאת בעצמו. לכן הכשל נופל על מי שמשך את main, ולא על ה-CI. ## וזה כבר תוקן פעם אחת, ובוטל הרשומה נוספה ב-37de9f7 ונכנסה ל-main במיזוג של Y-PLONI#5. אחר כך 8f0bbd8 הסיר אותה **והוריד את הלוכסן**, ו-a710b84 — אותו ענף, אותו יום — **החזיר את הלוכסן**; שניהם מוזגו ב-Y-PLONI#6, ונטו: הרשומה ירדה והמלכודת חזרה. שישה ימים אחר כך 2763bcb הכניס את הרשומה בחזרה (`node_modules | 1 +` יושב ב-stat שלו), ומשם ל-main ב-Y-PLONI#29. שני דברים שראוי לדעת על הסבב ההוא. **ההצדקה שם הייתה שגויה:** 8f0bbd8 נקרא „קובץ בשם node_modules חסם כל npm install”, וזה לא נכון — npm מתאושש, כפי שנמדד למעלה. **ול-a710b84 אין הסבר:** כותרתו היא „gitignore מתעלם רק מהתיקייה node_modules, לא מקובץ באותו שם”, כלומר תיאור הבעיה שהלוכסן יוצר — בקומיט שמחזיר את הלוכסן. ומאיפה זה חוזר: הקישור יושב בעץ העבודה של מי שיצר אותו, ו-`git add -A` שם מעלה אותו שוב. הלוכסן הוא מה שמאפשר לזה לקרות בשקט. ## למה `.gitignore` לא עצר את זה `node_modules/` — עם לוכסן — הוא **תבנית לתיקיות בלבד**. קישור סמלי אינו תיקייה, ולכן הוא לא הותאם ונוסף כמו כל קובץ אחר. נמדד עם קישור סמלי אמיתי בתוך clone של המאגר, בשני הכיוונים: עם לוכסן: ln -s … node_modules ; git add -A → M node_modules (120000) בלי לוכסן: ln -s … node_modules ; git add -A → שום דבר לא נבמה הלוכסן הוסר משלוש השורות ולא רק מזו שנשברה: `dist/` ו-`tmp/` הן אותה תבנית בדיוק, ולתקן אחת ולהשאיר שתיים זהות פירושו להשאיר את אותה מלכודת. ההרחבה נבדקה ולא הונחה — הפרש הקבוצות „מותאם בישן ולא בחדש” יצא ריק בשלוש התבניות, אין הרחבה לפי תת-מחרוזת (`distfile`, `my-dist`, `dist.js` אינם מותאמים לא לפני ולא אחרי), וכל 432 הקבצים המנוטרים נבדקו מול ה-`.gitignore` החדש ואף אחד אינו מותאם.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.