# תמונ״ך — מדריך למפתח

מסמך זה מיועד למי שממשיך לתחזק/לפתח את התוסף הזה (כולל אם זה מפתח אחר, לא מי שכתב את הקוד המקורי).

## התמונה הגדולה

זה **תוסף אחד** שמאגד כמה מדריכים שהיו קודם תוספים נפרדים לגמרי. הרעיון המרכזי: **התוכן של כל מדריך יושב בקובץ נתונים עצמאי משלו** — ומעליהם יש "מעטפת" (shell) שמטפלת בזיהוי המאוחד, בתצוגה ובעמוד השער. עריכת תוכן במדריך אחד לעולם לא נוגעת בקבצים של מדריך אחר.

## ⭐ תשובה לשאלה הכי חשובה: האם צריך לעדכן פעמיים?

**התשובה הקצרה: לא — בתנאי שאתם עורכים רק תוכן.**

| מה | האם זהה למקור? | אפשר להעתיק בין התוסף המאוחד לתוסף הנפרד? |
|---|---|---|
| `guides/<שם>/data/<שם>-data.js` | ✅ **כן** — חילוץ מדויק של `DATA` + `CATS` + `CARD_IMAGES` מהתוסף המקורי, בלי שינוי תוכן | ✅ **כן.** זה בדיוק אותו קוד |
| `guides/<שם>/view.html` | ❌ שונה (בוטל רישום תפריט, נוסף כפתור חזרה וכו') | ❌ לא |
| `shell/router.js`, `router.css`, `index.html` | 🆕 חדשים לגמרי | ❌ לא רלוונטי לתוסף הנפרד |

### מה זה אומר בפועל

**זרימת עבודה מומלצת לעדכון תוכן** (הוספת ערך, תיקון הסבר, הוספת תמונה):

1. עורכים **רק** את `guides/<שם>/data/<שם>-data.js` כאן.
2. מריצים `.\build\pack.ps1` → מקבלים `.otzplugin` מעודכן של תמונ״ך.
3. אם רוצים לעדכן גם את התוסף הנפרד: מעתיקים את **תוכן** `DATA` (ו/או `CARD_IMAGES`/`CATS`) מקובץ ה-data ומדביקים במקום המתאים ב-`index.html` של התוסף הנפרד.

> **הערה חשובה:** בתוסף הנפרד המקורי, ה-`DATA` יושב **בתוך** ה-`index.html` (inline), לא בקובץ חיצוני. לכן זו העתקה-הדבקה של המערך, לא החלפת קובץ. **אם תרצו להפוך את זה להחלפת-קובץ אמיתית**, צריך פעם אחת לחלץ גם בתוסף הנפרד את ה-`DATA` לקובץ `data.js` חיצוני ולטעון אותו ב-`<script src>` — מאותו רגע והלאה זה יהיה קובץ אחד משותף שפשוט מעתיקים בין השניים. זו עבודה של כמה דקות לכל תוסף וממליץ עליה בחום אם מתכננים לתחזק את שניהם לאורך זמן.

### ומה עם `view.html`?

ב**תוסף המאוחד** קבצי ה-`view.html` **כבר לא בשימוש כתצוגה** (ר' "מה שנשאר לא בשימוש" למטה) — התצוגה נבנית ילידית ב-`router.js`. הם נשארים בחבילה רק לגרסה העצמאית ולתיעוד. לכן: **אין טעם לתחזק אותם בתוסף המאוחד.** כל שינוי UI נעשה ב-`shell/router.js`.

## מבנה התיקיות

```
madaei-hatanach/
  manifest.json              ← המניפסט של אוצריא (id, גרסה, הרשאות, entrypoint)
  index.html                 ← עמוד השער - הכניסה היחידה שאוצריא טוען
  shell/
    router.js                ← "המוח" - זיהוי מאוחד, ניווט, משוב, דפי HTML מותאמים
    router.css                ← עיצוב עמוד השער והחלוניות
  guides/
    people/
      view.html               ← המדריך המלא (UI+לוגיקה) - זהה במבנהו למה שהיה כתוסף נפרד
      data/
        people-data.js         ← *** כאן עורכים תוכן *** (מערך DATA)
        _loader.html            ← קובץ טכני קטן, אל תיגעו (ר' "איך הזיהוי טוען נתונים" למטה)
    places/
      view.html
      css/ , js/ , data/         ← מבנה עשיר יותר (יש גם מפה, leaflet)
    animal/  flora/  domem/  beithamikdash/
      view.html + data/<שם>-data.js + data/_loader.html
  build/
    pack.ps1                    ← סקריפט אריזה (ר' למטה)
  dist/                        ← כאן נוצרים קבצי הפלט אחרי הרצת pack.ps1
  _serve.ps1                  ← שרת מקומי זמני לבדיקה בדפדפן רגיל (לא נכלל באריזה)
```

## איך לערוך תוכן (הוספה/עריכה של ערך במדריך קיים)

1. פותחים את `guides/<שם המדריך>/data/<שם>-data.js`.
2. זה קובץ JS פשוט שמכיל `const DATA = [ {...}, {...}, ... ];` — מערך של אובייקטים. מוסיפים/עורכים אובייקט לפי התבנית של האובייקטים הסמוכים.
3. שומרים.
4. **זהו — לא צריך לגעת בשום קובץ אחר.** ה-`view.html` של אותו מדריך טוען את הקובץ הזה אוטומטית דרך `<script src="data/...">`.

שדות נפוצים בכל ערך: `name` (שם, חובה), `aliases` (מערך כינויים/צורות נוספות, יכול להיות ריק `[]`), `cat` (מזהה קטגוריה פנימי של אותו מדריך). מעבר לזה, לכל מדריך שדות משלו (methods/verses/midrash/academic למדריכים "רגילים"; שדות גנאלוגיה שונים לגמרי באישים; explanation/identification/note/tribe לדומם). הכי קל להעתיק ערך קיים דומה ולשנות.

**חשוב על "כתיב חסר/מלא":** שם הערך (`name`) כדאי לכתוב בכתיב מלא הרגיל ("חיטה", לא "חטה") — מנוע הזיהוי כבר יודע להתמודד עם זה שהפסוק עצמו כתוב בכתיב חסר.

## איך לארוז ולפרסם גרסה חדשה

אחרי כל שינוי בתוכן או בקוד:

```powershell
.\build\pack.ps1
```

לשחרור גרסה גדולה (למשל 2.0.0) — כותבים את הגרסה ידנית ב-`manifest.json` ומריצים עם `-NoBump` כדי שהסקריפט לא יעלה אותה עוד:

```powershell
.\build\pack.ps1 -NoBump
```

הסקריפט הזה:
1. מעלה את הגרסה במניפסט (`manifest.json`) ב-**0.0.1** אוטומטית (patch bump), אלא אם הועבר `-NoBump`.
2. אורז שני קבצים לתוך `dist/`:
   - `com.chadbedera.madaeihatanach-X.Y.Z.otzplugin` — להתקנה באוצריא.
   - `madaei-hatanach-standalone-X.Y.Z.zip` — גרסה עצמאית (חילוץ + פתיחת index.html בדפדפן, בלי אוצריא. שימושי לבדיקה מהירה, אבל זיהוי בספרייה/מייל/שמירה קבועה לא עובדים שם כי הם תלויים ב-API של אוצריא).

**אין צורך לערוך את pack.ps1 בשביל עדכון גרסה רגיל** — הוא עושה את זה לבד בכל הרצה.

## "shell" — איך המעטפת עובדת (למי שנוגע בניווט/זיהוי, לא רק בתוכן)

### עמוד השער (`index.html` + `shell/router.js`)
זה ה-**entrypoint היחיד** שרשום ב-manifest. הוא מציג את הריבועים (6 מדריכים + משוב + הוספת HTML), ומכיל את מנגנון הזיהוי המאוחד.

### הצגת מדריך (`openGuide` ב-router.js) — רינדור ילידי, בלי iframe

**היסטוריה חשובה:** ניסינו שתי גישות אחרות לפני זו, ושתיהן נכשלו בפועל בתוך אוצריא (אבל עבדו מצוין בבדיקות מקומיות שלי - הפער התגלה רק אצל המשתמש בפועל):
1. **ניווט מלא** (`window.location.href` לעמוד guides/X/view.html) — אוצריא "נועל" את חלון התוסף לעמוד הכניסה שלו, וניסה לטעון מחדש את index.html כשהחלון עזב אותו, מה שגרם לשגיאת "שגיאה בטעינת הקובץ".
2. **iframe גלוי** (`#guideFrame` בתוך `#frameWrap`, מצביע ל-view.html של המדריך) — גם עם CSS מפורש (top/right/bottom/left, לא `inset`) זה הציג עמוד לבן/ריק בפועל באוצריא, אף שעבד מושלם בבדיקות המקומיות שלי דרך שרת http אמיתי.

**הפתרון שעובד בפועל:** כל תצוגת מדריך נבנית **ישירות בתוך index.html עצמו**, מהנתונים שכבר נטענים דרך ה-iframe הנסתר (`loadGuideData` - זה כן עובד, כי הוא לא-גלוי ורק מריץ script+postMessage). הפונקציות המרכזיות:
- `openGuide(catId, term)` — מסתיר את `#landing`, מציג את `#guideView` (רשת כרטיסים ילידית), וטוען את הנתונים.
- `renderGuideGrid(filterText)` — מציירת רשת כרטיסים גנרית (`entryCardHTML`) מתוך ה-DATA שכבר בזיכרון, עם חיפוש חי.
- `renderEntryDetailHTML(entry)` / `openEntryDetail(entry)` — חלונית פרטים גנרית שיודעת להתמודד עם **שתי צורות** של ערכים: שדות ישירים (`explanation`/`tribe`/`note` בדומם, שדות גנאלוגיה באישים) **וגם** מבנה `methods[]` (בעלי חיים/צומח/בית המקדש/מקומות). היא בונה HTML לפי אילו שדות קיימים בפועל בערך הספציפי - לא "יודעת" מראש איזה מדריך זה.
- `openGenericEditForm(entry)` / `openGenericProposeForm(name)` — טפסי עריכה/הצעת-תוספת גנריים, משותפים לכל המדריכים (לא הטפסים הייעודיים שבכל `view.html` - אלה כבר לא בשימוש בפועל, ר' "מה שנשאר לא בשימוש" למטה).

כפתור ה"חזרה" (`#guideBackBtn`) פשוט מסתיר את `#guideView` ומחזיר את `#landing`. אין יותר iframe/postMessage/כפתור-חזרה-בתוך-מדריך בזרימה הזו.

**חשוב:** בגלל זה, לפתיחת ספר מהמקור (`reader.openBookAtRef`) יש עכשיו parser refs (`parseVerseRef`/`openInReader`) **ישירות בתוך router.js** - כי הקריאה ל-Otzaria חייבת לרוץ מהעמוד הראשי עצמו (לא מ-iframe שאולי אין לו גישה לגשר).

### איך הזיהוי טוען נתונים (חשוב להבין לפני שנוגעים ב-router.js)
מנוע הזיהוי (`identify()` ב-router.js) צריך גישה לתוכן ה-DATA (וגם CATS ו-CARD_IMAGES, אם קיימים) של **כל שישה** המדריכים בו-זמנית, בלי לפתוח את כולם ויזואלית. זה נעשה כך:
- לכל מדריך יש קובץ `data/_loader.html` זעיר (script + postMessage).
- `router.js` יוצר **iframe נסתר** (display:none) שמצביע ל-`_loader.html` של אותו מדריך.
- ה-loader טוען את `<script src="../<שם>-data.js">` (בדיוק כמו שה-view.html האמיתי עושה), ואז שולח `postMessage` להורה עם `data` (מערך DATA), `cats` (מערך CATS, לחלוקה לקטגוריות - ר' למטה) ו-`images` (מילון CARD_IMAGES, אם קיים).
- `router.js` מאזין להודעה, שומר הכל בזיכרון (`dataCache`/`catsCache`/`imagesCache`), ומוחק את ה-iframe.

**למה לא `fetch()`?** ניסינו קודם וזה כשל בפועל בתוך אוצריא (כנראה fetch לקבצים מקומיים חסום ב-webview שם) — לכן המנגנון עובר דרך iframe + תגית script רגילה, בדיוק כמו שכל מדריך טוען את הנתונים של עצמו, ולא דרך fetch.

### חלוקה לקטגוריות (chips + כותרות סעיף)
כל מדריך (חוץ ממקומות, שכבר כולל CATS בתוך `places.js` עצמו) הגדיר במקור `const CATS = [...]` **בתוך ה-view.html**, נפרד מה-DATA. חולצה גם היא לקובץ ה-data (באותו אופן כמו CARD_IMAGES) ונחשפת דרך ה-loader. `renderGuideChips()`/`renderGuideGrid()` ב-router.js בונים את שורת הצ'יפים ("הכל" + קטגוריה לכל אחת) ומקבצים את הכרטיסים תחת כותרות סעיף (`entry-section-head`) בדיוק כמו שכל מדריך עשה בעצמו - **רק כשאין חיפוש פעיל וה-צ'יפ הפעיל הוא "הכל"**; אחרת מוצגת רשימה שטוחה מסוננת.

### מפה אופלין (מקומות)
בניגוד לניסיון הראשון (הטמעת Google Maps באייפריים - זה דורש אינטרנט, וזה **לא** מה שהיה במקור!), מקומות משתמש במפה **וקטורית אופלין לגמרי** (Natural Earth + Leaflet, בלי שרתי אריחים חיצוניים). הקבצים `guides/places/js/vendor/leaflet.js`, `guides/places/data/geo-basemap.js` (הנתונים הגאוגרפיים הגולמיים, כ-480KB) ו-`guides/places/js/map.js` (הפונקציות `addBaseLayers`/`addLabels`/`pinIcon`) נטענים **פעם אחת בתוך `index.html` עצמו** (לא ב-iframe, לא ב-loader) - כי אלה רק ספריות/פונקציות גלובליות, לא צריך לבודד אותן. `renderOfflineMiniMap()` ב-router.js יוצרת מפה קטנה בכל כרטיס מקום שיש לו קואורדינטות (`methods[].geo`), עם הבסיס הווקטורי + סמן - **בלי לוויין** (בהתאם לגרסה הרזה - השכבה הזו דורשת רשת ולא הוטמעה).

### ⚠️ בדקו תמיד שאתם עובדים מהגרסה העדכנית ביותר של כל מדריך!
זו טעות אמיתית שקרתה: תוסף "צומח" חולץ בטעות מגרסה 1.3.0, בעוד שהגרסה העדכנית בפועל הייתה 1.9.0 (עם שישה עדכוני תוכן שכללו, בין השאר, תמונות מוטבעות!). לפני שמעדכנים/מוסיפים מדריך, **תמיד** לבדוק בתיקיית ההורדות מה הגרסה הכי גבוהה הקיימת בפועל (`sort -V` על שמות הקבצים), ולא להסתמך על מה שכבר חולץ קודם בפרויקט הזה.

**אם מוסיפים מדריך שביעי** (ר' סעיף "הוספת מדריך חדש" למטה) — צריך גם ליצור לו `_loader.html` תואם, ולהוסיף שורה למערך `CATEGORIES` ב-router.js.

### מנוע הזיהוי עצמו (`identify()`)
עובד ברמת **מילה שלמה** (לא "תת-מחרוזת בכל מקום בטקסט" — זה גרם בעבר להתאמות שווא כמו "פיל" בתוך "אפילת"). לכל מדריך נבנה מראש "lookup map" (שם/כינוי → ערך), ולכל מילה מהטקסט שנבחר מנסים כמה "צורות מועמדות" (`candidateForms`): הסרת עד שתי תחיליות (ה/ו/ב/כ/ל/מ/ש) והסרת סיומת ריבוי. אם אין התאמה מדויקת, יש נפילה חזרה ל"כתיב חסר" (`looseForm` — מסיר את כל אותיות ו/י ובודק שוב) כדי לתפוס מילים כמו "חטה" בפסוק מול "חיטה" במאגר. **זו בדיוק אותה טכניקה שכל מדריך כבר משתמש בה בזיהוי הפנימי שלו** (tokenizeHeb/candidateForms/looseForm) — לא המצאה חדשה, רק מיושמת גם ברמת ה-shell כדי לבדוק בכל המדריכים בבת אחת.

### הוספת HTML מותאם (הריבוע "➕ הוספת דף HTML")
נשמר דרך `Otzaria.call('storage.set'/'storage.get', ...)` (בדיוק כמו תוסף "צופה HTML" הקודם) — **לא** קובץ בדיסק, אלא ערך בזיכרון הקבוע של אוצריא, keyed לפי `madaei_html_page__<שם>`. רשימת השמות נשמרת במפתח נפרד `madaei_hatanach_html_pages_index`. חשוב: קריאות ה-storage תלויות ב-`window.Otzaria` שקיים — הפונקציה `renderCustomPageCards()` (מציגה את הריבועים בעמוד השער) נקראת רק **אחרי** ש-`waitForOtzaria` מאשר שהגשר קיים, אחרת `storage.get` נכשל בשקט ומחזיר ריק (זה היה באג אמיתי בגרסה מוקדמת יותר).

## הוספת מדריך חדש (תחום שביעי)

1. לחלץ/להעתיק את קובץ ה-view.html המקורי של המדריך ל-`guides/<שם>/view.html`.
2. לאתר את המערך `const DATA = [...]` בקובץ ולחתוך אותו לקובץ נפרד `guides/<שם>/data/<שם>-data.js` (עם `<script src="data/<שם>-data.js"></script>` במקום שבו ה-DATA היה מוגדר בעבר, כדי לשמור על סדר הריצה של שאר הקוד).
   - **שימו לב לקבועים חיצוניים**: אם ה-DATA הישן השתמש בקבועים שהוגדרו מעל (כמו `DEUT_8_8` בצומח), צריך להעתיק גם אותם *לתוך* קובץ ה-data החדש (לא רק להשאיר ב-view.html), אחרת טעינת ה-data בבידוד (דרך ה-loader הנסתר) תיכשל עם `SyntaxError: Identifier already declared` אם הם עדיין מוגדרים גם ב-view.html. זה באג אמיתי שקרה עם "צומח" - ר' פרק ההיסטוריה בסוף.
3. ליצור `guides/<שם>/data/_loader.html` לפי התבנית הזהה לשאר (רק להחליף את שם הקובץ ואת ה-`cat`).
4. להוסיף שורה ל-`CATEGORIES` ב-`shell/router.js` (id, label, icon, path, loaderPath).
5. להוסיף ריבוע תואם ב-`index.html` (`<div class="card" data-cat="...">`).
6. לבטל את רישום התפריט העצמאי של המדריך (`registerContextMenuItem`/`reader.addContextMenuItem` הפנימי שלו) — כי עכשיו רק ה-shell רושם פריט תפריט אחד ("זהה במדעי התנ״ך"), לא כל מדריך בנפרד.
7. להוסיף hook טעינה: בסוף הקובץ, אחרי `render();`, קטע כזה:
   ```js
   (function(){
     try {
       const focus = new URLSearchParams(location.search).get('focus');
       if (focus) handleContextMenuClick({ itemId: MENU_ITEM_ID, selectedText: focus });
     } catch(e){}
   })();
   ```
   (משתמשים בפונקציית ה-handler הקיימת של אותו מדריך — לא כותבים לוגיקה חדשה.)
8. להוסיף כפתור "חזרה למדעי התנ״ך" בתחילת ה-body (postMessage, ר' דוגמה בכל מדריך קיים).
9. להריץ `.\build\pack.ps1` ולבדוק.

## הרשאות שנדרשות במניפסט (איחוד מכל המדריכים)
`reader.context_menu`, `reader.open`, `app.run_on_startup`, `notifications.send`, `ui.feedback`, `feedback.send_email`, `app.open_url`, `navigation.write`, `plugin.storage.read`, `plugin.storage.write`. אם מוסיפים מדריך עם הרשאה נוספת - להוסיף גם ל-manifest.json.

## דברים לזכור / באגים אמיתיים שכבר תוקנו (ואל תחזרו עליהם)

- **אל תשתמשו ב-`fetch()`** לטעינת קבצים מקומיים בתוך התוסף — נכשל בפועל באוצריא. תמיד iframe/script tag.
- **אל תנווטו את חלון התוסף** (`window.location.href`) לעמוד אחר מלבד index.html — אוצריא לא אוהב את זה, גם אם זה עמוד בתוך אותה חבילה.
- **אל תציגו iframe גלוי** שמצביע ל-view.html של מדריך — גם זה נכשל בפועל (עמוד לבן), למרות שעובד מצוין בבדיקה מקומית. iframe **נסתר** (display:none) לטעינת נתונים בלבד כן עובד מצוין.
- **בדקו כפילות הצהרות `const`** כשמעתיקים קוד/קבועים בין view.html לקובץ data נפרד — כפילות גורמת ל-SyntaxError שקטה שתקוע את כל טעינת הנתונים לאותו מדריך.
- כשמריצים בדיקה מקומית (`_serve.ps1`), שימו לב: פתיחת קבצים ישירות מהדיסק (`file://`) מתנהגת לפעמים אחרת מהפעלה אמיתית באוצריא — עדיף לבדוק גם דרך שרת http מקומי אמיתי. **אבל גם זה לא מבטיח 100%** - כמו שקרה עם ה-iframe הגלוי, שעבד מצוין בבדיקה המקומית ונכשל אצל המשתמש בפועל. הבדיקה הכי אמינה היא תמיד בתוך אוצריא עצמו.

## מה שנשאר בקבצי ה-view.html אבל כבר לא בשימוש בפועל

מאז המעבר לרינדור ילידי (ר' למעלה), קבצי ה-`guides/<שם>/view.html` **עדיין קיימים בחבילה** (כדי לשמור על "כל מדריך קובץ עצמאי" ולתמוך בגרסה העצמאית/standalone), אבל בפועל דרך התוסף המאוחד הם **לא נטענים כתצוגה** - רק ה-`data/<שם>-data.js` וה-`_loader.html` שלהם נטענים (ברקע, להזנת מנוע הזיהוי והרשת הילידית). המשמעות:
- הטפסים הייעודיים בכל view.html (`openEditForm`, `openNotFoundModal` עם save/send/download, כפתורי הדפסה) **לא רצים** דרך התוסף המאוחד - יש להם **מקבילה גנרית** ב-router.js (`openGenericEditForm`, `openGenericProposeForm`) שמכסה את אותה פונקציונליות בצורה פשוטה יותר, משותפת לכל המדריכים.
- אם רוצים לשפר/להוסיף פיצ'ר לעריכה/הצעת-תוספת - עורכים ב-`router.js` (הפונקציות הגנריות), **לא** בכל view.html בנפרד.
- ה-view.html עדיין רלוונטי לגמרי אם מישהו פותח אותו ישירות (בגרסה העצמאית, קובץ בקובץ) - שם כן רץ הקוד המקורי במלואו.

## עריכת כרטיסים - איך זה בנוי (מגרסה 2.0.0)

`openGenericEditForm(entry, catId)` פותח עורך שמכסה **את כל השדות**, לא רק את המלאים:

- **`GUIDE_FIELDS`** (ב-router.js) מגדיר לכל מדריך אילו שדות "אמורים" להיות בו. זה מה שמאפשר גם להציג "פרטים שאינם במדריך" בתחתית כל כרטיס, וגם לפתוח בעורך שדות ריקים.
- **`METHOD_FIELDS`** - חלק מהשדות (explanation/latin/wiki/confidence/modern) יושבים פיזית בתוך `entry.methods[0]` ולא ישירות על הערך. `readField`/`writeField` מסתירים את ההבדל הזה, כך שהעורך עובד אחיד על שני סוגי המבנה.
- **`entry.customFields`** - מילון חופשי `{ "שם קטגוריה": "תוכן" }` שהמשתמש מוסיף בעצמו ("קישורים חיצוניים", "מידע מחז״ל" וכו'). מוצג בכרטיס אחרי השדות הרגילים.
- **`entry.customImage`** - תמונה שהמשתמש הוסיף (URL או `data:` base64 מקובץ מקומי). גוברת על `wiki` אבל לא על `CARD_IMAGES` המוטבע.
- מקורות (`verses`/`makorot`, `midrash`, `academic`) נערכים כטקסט רב-שורתי עם `|` כמפריד, ומפורסרים ב-`parseMulti`.

### שמירת עריכות שנשארות אחרי רענון

- **אחסון:** `localStorage`, מפתח `<catId>_edits_v1`, מבנה `{ "<שם מקורי>": { savedAt, entry: <עותק מלא> } }`.
- **המפתח הוא תמיד השם המקורי** (`entry.__origName`), לא השם אחרי העריכה — אחרת שינוי שם היה מנתק את העריכה מהערך בטעינה הבאה.
- **החלה בטעינה:** `applyStoredEdits(catId, data)` נקראת בתוך `loadGuideData` **לפני** השמירה ב-`dataCache`. זה קריטי: מנוע הזיהוי (`buildLookup`) נבנה מאותם נתונים, כך שכינויים/שמות שהמשתמש הוסיף נכנסים גם לזיהוי בלחיצה ימנית.
- **שחזור:** `pristineCache[catId][origName]` שומר עותק של הערך *לפני* העריכה (נלכד ב-`applyStoredEdits` וב-`openGenericEditForm`). כפתור "↺ שחזור לגרסת המקור" מופיע רק כשקיימת עריכה שמורה, ומריץ `restoreEntryToOriginal` שמוחק את הרשומה מ-localStorage ומחזיר את הערך למקור.
- **אחרי כל עריכה/שחזור** צריך לקרוא ל-`invalidateLookup(catId)` כדי שמפת הזיהוי תיבנה מחדש.
- ערך שנערך מסומן ב-`__edited` — מוצג כ-✏️ קטן בכרטיס ברשת, ובהודעה בראש הכרטיס המלא.

> ⚠️ **שימו לב לתופעת לוואי:** מכיוון שנשמר עותק *מלא* של הערך, אם תשחררו בעתיד גרסה עם תוכן מתוקן לאותו ערך — המשתמש שערך אותו מקומית ימשיך לראות את הגרסה שלו, גם בשדות שלא נגע בהם. מי שמפריע לו זה יכול לשנות את `saveEntryEdit` כך שישמור רק שדות שהשתנו (patch) במקום עותק מלא.

## מה עוד פתוח / ידוע כחסר (נכון לגרסה 2.0.0)

- אין מסך לצפייה/ניהול של כל הטיוטות והדיווחים השמורים (`<guide>_nf_drafts_v1`, `identify_error_reports_v1`) - רק שמירה עצמה.
- תצוגת הכרטיס הגנרית (`renderEntryDetailHTML`) פשוטה יותר מהעיצוב הייעודי שהיה לכל מדריך בנפרד - מלאה מבחינת תוכן, אך פחות "מעוצבת" מהמקור.
- שכבת הלוויין (Sentinel-2) של מפת המקומות לא הועברה - רק הבסיס הווקטורי האופלין. זו החלטה מכוונת (הגרסה ה"רזה"), אבל הקוד ל-satellite עדיין קיים ב-`js/map.js` אם תרצו להחזיר.
- **משימות שהוזמנו וטרם בוצעו:** הוספת מסכתות סוכה+חולין (ושינוי השם ל"מדעי תנ״ך וש״ס"), הסתרת שם ה' מפני קדושתו, אריזת "מומים במסכת בכורות" כאב-טיפוס, והעתקת שאר התוספים לתיקייה חדשה עם המייל המעודכן.
