Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,74 @@ const resolved = resolution.status === "uniform" ? resolution.value : void 0;
התיקון החיווי נשאר יציב לאורך כל התרחיש — הקלדה, הזזת סמן, סימון טווח ולחיצה במקום
אחר — ב‑0 הבהובים.

## איטיות בהקלדה — חישוב סגנון של כל המסמך בכל תו

„כשאני מקליד, לוקח כמה שניות עד שהתו מופיע.” נמדד ב‑Chrome על ה‑`dist` הארוז,
במסמך של חמישה עמודים: על כל הקשה הדפדפן חישב סגנון מחדש (`UpdateLayoutTree`)
ל‑**כל** עץ המסמך — 2,748 אלמנטים, 60–115ms לחישוב — 42 פעמים ב‑50 הקשות, עם
53 long tasks. במסמך ריק זה לא קרה; במסמך אמיתי, על מכונה עמוסה ובתוך ה‑WebView
של אוצריא, זה מה שמצטבר ל„כמה שניות”.

המקור לא היה בקוד שרץ בהקלדה (פחות מאחוז מהזמן היה שלנו) אלא בכלל CSS יחיד
ב‑[src/styles/engine-chrome.css](src/styles/engine-chrome.css): ההסתרה של באנר
`edit-rejected` של המנוע נכתבה כ‑`.superdoc__mutation-status:has([data-superdoc-v2-edit-rejected])`.
`:has()` שהעוגן שלו יושב **בתוך** `.superdoc` גורם ל‑Blink לסמן את `.superdoc`
כמושפע מ‑`:has()`, ומאותו רגע כל הוספה או הסרה של צומת במסמך — כל תו — מתזמנת
חישוב סגנון של תת‑העץ כולו (ב‑trace: „Affected by :has()” על `DIV.superdoc`,
„Invalidation set invalidates subtree”). ההכרעה הייתה ניסוי מבוקר על אותו
מסמך: מחיקת הכלל הזה בלבד, בזמן ריצה, הורידה את החישובים המלאים ל‑0 ואת ה‑long
tasks ל‑3; מחיקת כלל אקראי אחר, או של ה‑`:has()` שברצועה, לא שינתה דבר.

התיקון מסתיר את ה‑`<p>` שנושא את התכונה כילד ישיר של העוטף, בלי `:has()` —
העוטף הוא `height: 0` בלי ריפוד, ואין הבדל נראה. שני שערים שומרים שזה לא
יחזור: [tests/unit/css-hygiene.test.ts](tests/unit/css-hygiene.test.ts) חוסם כל
`:has()` שלא אושר במפורש עם נימוק, ו‑`npm run check:typing-recalc`
([scripts/qa/typing-style-recalc-qa.mjs](scripts/qa/typing-style-recalc-qa.mjs))
מקליד במסמך של כמה עמודים תחת trace של ה‑renderer וסופר חישובי סגנון בגודל
המסמך כולו — הוא אדום על הכלל הישן (112 חישובים ב‑40 הקשות) וירוק אחרי התיקון.
לאבחון ידני יש [scripts/typing-latency-probe.mjs](scripts/typing-latency-probe.mjs):
זמן מלחיצה עד ציור, long tasks, והודעות קונסולה לכל הקשה — גם על התוסף כשהוא
רץ **בתוך אוצריא** (`--attach`, אחרי הרצת אוצריא עם
`WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=9444`).

### בתוך אוצריא: מה נמדד אחרי התיקון, ומה נשאר למארח

על המסמך של המשתמש (חמישה עמודים, שני טורים, הערות שוליים), בתוך ה‑WebView של
אוצריא, עם הקשות **אמיתיות** של מערכת ההפעלה: ההקשה מגיעה לדף תוך כמה
מילישניות, הדף מצייר אותה בלי long tasks ובלי חישוב סגנון של המסמך כולו, ולא
נמצא שום סטול. אבל הפיקסלים מופיעים על המסך רק כ‑150ms **אחרי** שה‑renderer
צייר אותם — זה שלב המארח: WebView2 מצייר ל‑visual, אוצריא לוכדת אותו
ב‑Windows.Graphics.Capture, מעבירה כטקסטורה ל‑Flutter, ו‑Flutter (בבניית Debug,
שבה נמדד) מצייר את הפריים. גם ריחוף על כפתור ברשת הניווט של אוצריא עצמה לקח
כ‑70ms עד המסך. זה אינו בתוסף ואינו במנוע, ואי אפשר לראותו ממדידה בתוך הדף.

שני מצבים נראים כ„הקלדה איטית” ואינם כאלה: חלון ממוזער, או WebView שאוצריא
השהתה (`pause` → `IsVisible(false)`), הופכים את הדף ל‑`hidden` — rAF לא רץ
וטיימרים מוגבלים, וכל הקשה מחכה שניות. ופוקוס JS על משטח ההקלדה אינו פוקוס
המקלדת של המערכת: בלי לחיצת עכבר אמיתית ההקשות נופלות ב‑Flutter ולא בדף.
[scripts/typing-latency-inapp.mjs](scripts/typing-latency-inapp.mjs) מודד את
כל השרשרת — הקשה אמיתית → keydown → התו ב‑DOM → ציור → פיקסלים על המסך
(`--screen`) — ומדפיס את מצב הנראות והפוקוס לפני שהוא מתחיל. `--target`
בוחר איפה להקליד (כותרת ברוחב העמוד, טור שמאלי/ימני, או שורה לפי טקסט).

**מה שנמדד תלוי בפסקה, לא בטורים.** התחושה „בטורים זה איטי” לא אושרה: פסקה
קצרה עם ריצת עיצוב אחת בתוך הטורים מקלידה מהר כמו הכותרת, גם בקצב של עשר
הקשות בשנייה. מה שכן איטי הוא פסקה שיש בתוכה ירידת שורה (`w:br`), טאב או
הפניה להערת שוליים: המנוע מסווג אותה כ‑`paragraph-complex-inline`, משליך אותה
מחדש מה‑XML בכל הקשה, והמחיר להקשה גדול בערך פי שניים. מספר ריצות העיצוב,
`jc=distribute`, ריווח אותיות וסגנון תו אינם משנים (ביסקציה על מסמכים סינתטיים;
דווח ל‑SuperDoc ב‑[issue #3984](https://github.com/superdoc/docx-editor/issues/3984)).
המנוע מעבד הקשה אחת בכל פעם ואינו מאחד הקשות שנערמו, ולכן במחשב עמוס הקלדה
מהירה בפסקה כזאת נערמת לתור. הסקריפט מדפיס בדיוק את המדד הזה („ההקשה האחרונה →
התו האחרון ב‑DOM”); מדוד אותו עם `--gap 100`. במחשב שקט (שתי ליבות, בניית
Release של אוצריא) העורך עומד בקצב גם בפסקה כזאת — התו האחרון מופיע חלקיק
שנייה אחרי ההקשה האחרונה, כמו באותו מסמך ב‑Chrome וב‑Edge. מדידה קודמת שהראתה
„יותר משנייה” רצה בזמן שסשן אחר בנה את אוצריא והריץ חבילת בדיקות באותו מחשב;
לכן הסקריפט מדפיס גם את ה‑CPU של שאר התהליכים בזמן הפרץ ומזהיר כשהוא גדול.
שימו לב שהמוטציה הראשונה אחרי הקשה היא שכבת הסמן ולא התו — מדידה שנעצרת בה
מדווחת „מהר” גם כשהתו מגיע מאוחר. המחיר להקשה יושב במנוע
(`@superdoc/docx-engine`), לא בתוסף ולא במארח; מספריו אינם מתפרסמים כאן.

## מצב מיקוד

`F11`, הכפתור ב„תצוגה” או הכפתור בשורת המצב — ו‑`Escape`, `F11` או כפתור
Expand Down
17 changes: 17 additions & 0 deletions docs/engine-gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -1569,3 +1569,20 @@ wrap: { success: false, code: "INVALID_TARGET" }

הוספת קישור כשהסמן כבר בתוך קישור קיים נכשלת בקוד הזה, עם הודעה באנגלית מן
המנוע. `at-mention-overlay.ts` מתרגם אותו להודעה בעברית ואינו מנסה לעקוף.

## הקלדה — `:has()` בגיליון המארח מחשב סגנון לכל המסמך בכל תו

נמדד ב-9.2026 על ה-`dist` הארוז (Chrome, trace של ה-renderer עם
`invalidationTracking`): כלל CSS **שלנו** עם `:has()` שעוגנו ישב בתוך `.superdoc`
גרם לחישוב סגנון מחדש של כל עץ המסמך על כל הקשה. הסיפור, המספרים והתיקון —
בראש README („איטיות בהקלדה”) ובהערת הכלל ב-src/styles/engine-chrome.css.

מה שנוגע למנוע, ולכן נרשם כאן: גיליון המנוע עצמו משתמש ב-`:has()`, ולכן קבוצת
האינבלידציה של `:has` בדף אינה שלנו לבד. בלי `:has()` שלנו בתוך `.superdoc`
התופעה אינה מופיעה (נמדד: מחיקת כללי המנוע לבדם ומחיקת הכלל שלנו לבדו נתנו
שתיהן 0), ולכן ההגנה היא בצד המארח: אין `:has()` שעוגנו יכול להיות אב של
צומת במסמך. tests/unit/css-hygiene.test.ts אוכף זאת סטטית, ו-
`npm run check:typing-recalc` מודד זאת בדפדפן.

לא נשלח למעלה: הגיליון הזה הוא של `@superdoc/docx-engine` הקנייני, והרישיון שלו
אוסר לפרסם מדידות של המנוע. אם יידווח — כתיאור איכותי בלבד, בלי מספרים.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
"check:open-dialog": "node scripts/open-dialog-probe.mjs",
"check:focus": "node scripts/qa/focus-mode-qa.mjs",
"check:font-list": "node scripts/qa/font-list-layout-qa.mjs",
"check:typing-recalc": "node scripts/qa/typing-style-recalc-qa.mjs",
"verify": "npm run typecheck && npm run test && npm run build && npm run check:dist && npm run check:boot && npm run check:fonts && npm run check:icons && npm run check:rtl && npm run check:ruler && npm run check:startup && npm run check:gutter && npm run check:readout && npm run check:tooltip && npm run check:ribbon && npm run check:session && npm run check:open-dialog && npm run check:menu && npm run check:sdk",
"verify:qa": "npm run build && node scripts/qa/run-all.mjs scripts/qa/smoke.mjs scripts/qa/hebrew-numbering-probe.mjs scripts/qa/hebrew-list-probe.mjs scripts/qa/hebrew-numbering-ui-qa.mjs scripts/qa/replace-all-probe.mjs scripts/qa/replace-race-probe.mjs scripts/qa/replace-ui-probe.mjs scripts/qa/replace-all-multiparagraph-qa.mjs scripts/qa/find-replace-navigation-qa.mjs scripts/qa/replace-all-undo-qa.mjs scripts/qa/replace-buttons-reenable-qa.mjs scripts/qa/export-pdf-qa.mjs scripts/qa/file-ops-qa.mjs scripts/qa/file-freeze-qa.mjs scripts/qa/installed-fonts-qa.mjs scripts/qa/load-progress-qa.mjs scripts/qa/multi-doc-qa.mjs scripts/qa/spellcheck-qa.mjs scripts/qa/bold-cs-qa.mjs scripts/qa/list-resolver-qa.mjs scripts/qa/focus-mode-qa.mjs scripts/qa/context-font-focus-probe.mjs scripts/qa/list-caret-qa.mjs scripts/qa/font-caret-qa.mjs scripts/qa/at-mention-qa.mjs scripts/qa/book-completion-qa.mjs scripts/qa/acronym-completion-qa.mjs scripts/qa/static-completion-qa.mjs scripts/qa/font-sample-qa.mjs scripts/qa/font-availability-qa.mjs scripts/qa/font-list-layout-qa.mjs scripts/qa/tell-me-qa.mjs",
"verify:qa": "npm run build && node scripts/qa/run-all.mjs scripts/qa/smoke.mjs scripts/qa/hebrew-numbering-probe.mjs scripts/qa/hebrew-list-probe.mjs scripts/qa/hebrew-numbering-ui-qa.mjs scripts/qa/replace-all-probe.mjs scripts/qa/replace-race-probe.mjs scripts/qa/replace-ui-probe.mjs scripts/qa/replace-all-multiparagraph-qa.mjs scripts/qa/find-replace-navigation-qa.mjs scripts/qa/replace-all-undo-qa.mjs scripts/qa/replace-buttons-reenable-qa.mjs scripts/qa/export-pdf-qa.mjs scripts/qa/file-ops-qa.mjs scripts/qa/file-freeze-qa.mjs scripts/qa/installed-fonts-qa.mjs scripts/qa/load-progress-qa.mjs scripts/qa/multi-doc-qa.mjs scripts/qa/spellcheck-qa.mjs scripts/qa/bold-cs-qa.mjs scripts/qa/list-resolver-qa.mjs scripts/qa/focus-mode-qa.mjs scripts/qa/context-font-focus-probe.mjs scripts/qa/list-caret-qa.mjs scripts/qa/font-caret-qa.mjs scripts/qa/at-mention-qa.mjs scripts/qa/book-completion-qa.mjs scripts/qa/acronym-completion-qa.mjs scripts/qa/static-completion-qa.mjs scripts/qa/font-sample-qa.mjs scripts/qa/font-availability-qa.mjs scripts/qa/font-list-layout-qa.mjs scripts/qa/tell-me-qa.mjs scripts/qa/typing-style-recalc-qa.mjs",
"postverify:qa": "node scripts/qa/click-snap-qa.mjs",
"verify:fast": "npm run typecheck && npm run test && npm run build && npm run check:dist",
"golden:capture": "npm run build && node scripts/qa/golden.mjs capture scripts/qa/smoke.mjs scripts/qa/hebrew-numbering-probe.mjs scripts/qa/hebrew-list-probe.mjs scripts/qa/hebrew-numbering-ui-qa.mjs scripts/qa/replace-all-probe.mjs scripts/qa/replace-race-probe.mjs scripts/qa/replace-ui-probe.mjs scripts/qa/replace-all-multiparagraph-qa.mjs scripts/qa/find-replace-navigation-qa.mjs scripts/qa/replace-all-undo-qa.mjs scripts/qa/replace-buttons-reenable-qa.mjs scripts/qa/export-pdf-qa.mjs scripts/qa/file-ops-qa.mjs scripts/qa/file-freeze-qa.mjs scripts/qa/installed-fonts-qa.mjs scripts/qa/load-progress-qa.mjs scripts/qa/spellcheck-qa.mjs scripts/qa/bold-cs-qa.mjs scripts/qa/page-border-overlay-qa.mjs scripts/qa/line-number-overlay-qa.mjs scripts/qa/pilcrow-overlay-qa.mjs",
Expand Down
Loading
Loading