• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות
    1. דף הבית
    2. לומד''עס
    ל מנותק
    • פרופיל
    • עוקב אחרי 23
    • עוקבים 6
    • נושאים 92
    • פוסטים 504
    • קבוצות 0

    לומד''עס

    @לומד''עס

    188
    מוניטין
    184
    צפיות בפרופיל
    504
    פוסטים
    6
    עוקבים
    23
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה
    גיל 37
    מיקום על הגמרא

    לומד''עס הפסקת מעקב מעקב

    הפוסטים הטובים ביותר שנוצרו על ידי לומד''עס

    • מדריך לא מפספסים שיחות

      עדכון יום ג' י''ב טבת עבור לקוחות רמי לוי ופרטנר כרגע נוסף מספר חדש למערכת (זה אותה מערכת עם מספר נוסף), שבו אין בעיה בהפניה וניתן להפנות אליו
      המספר הוא 093137980
      באם יש לכם תקלה אשמח לעדכון

      בנוסף עודכן שהמספר המצנתק יהיה לפי המספר המפנה כלומר מי שהפנה למספר הישן יקבל ממנו צינתוק ומי שיפנה לחדש יקבל צינתוק מהחדש (זה אותו קו, נטו עניין של נוחות)
      אשמח למשוב האם זה עובד

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

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

      איך זה עובד?
      אז ככה א' נרשמים או באתר או בטלפון (09-313-7980) רושמים את השם שלכם ושם המשפחה שלכם

      עושים הפניית שיחות באם אין קליטה (ניתן לעשות גם אם אין מענה ו/או תפוס, כלומר כאשר אתם דוחים) למספר המערכת. וזהו!!!
      המתקשר ישמע את המלל הבא
      שלום אינני יכול לענות כרגע ניתן להשאיר לי הודעה

      וכעת למערכת עצמה
      כאשר אדם מתקשר אלכם ואתם לא זמינים הוא עובר אוטומטי למערכת שמזהה מי הוא (המתקשר מזוהה ע''י אפליקציית me כעת גם אם הוא ינתק את השיחה ללא הודעה תוכלו אתם לשמוע בקו או לראות באתר מי חיפש אתכם כולל שמו ובמקרה והוא השאיר הודעה ניתן לשמוע את ההודעה לקרוא את התמלול וכן לשמוע באתר בקיצור כמעט כל פונקציה שתעלה על דעתכם כולל התראה בזמן אמת למחשב שמחובר לאזור האישי שלכם באתר.

      אין מה להוסיף נסו את זה בעצמכם
      זה הטלפון של המערכת: 09-313-7980 (גם המספר השני תקין)
      וזה כתובת האתר: https://www.voicecall.online/

      המעלות על פני תא קולי רגיל
      א' לא לכולם יש
      ב' יש עדכון גם על שיחה שלא נענתה
      ג' יש תמלול של ההודעה
      ד' למי ששכח זה עובר דרך "מי" ככה שתוכלו לדעת מי זה שחיפש אתכם
      ה' יש אתר מונגש לראות מי חיפש אותך ומתי
      ו' למי שעל המחשב ולא יושב כל הזמן ליד הטלפון, ניתן לקבל בזמן אמת פושים (הודעות דחיפה) אם מישהו חיפש אתכם
      ז' מזהה חסוי (הזיהוי של חסוי עובד רק בהפניה למספר 0796069620, דא עקא שלא בכל החברות ניתן להפנות לשם ולכן הובא כאן רק המספר השני)

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

      וקצת תמונות להמחשה

      שאלות? בשמחה 👇 👇 👇

      פורסם בהסברים מסודרים ממשתמשים
      ל
      לומד''עס
    • רשימה לבנה - רשימה שחורה - חסימת טווחים

      אשמח אם יהיה אופציה בחסימת כניסה לשלוחה על ידי רשימה שחורה ולבנה לכתוב ברשימה כל מספר שמתחיל ב

      077*******
      

      לדוגמא שיש לי מספר שעושה הפניות אלי ואני מעונין לחסום את הנייעס של ה 079 או 077
      או מעונין שרק המספרים ניידים יכנסו לשלוחה
      שיחסום
      וכל מה שיבאו אחרי לענ"ד זה מאוד נצרך ולא דבר מסובך לפיתוח
      תודה רבה

      אשמח שכל מי שחושב שיש תועלת שיעשה 👍 כדי ש ימות יקבלו אינדקציה לגבי היעילות של הדבר

      פורסם בבקשות לפיתוח
      ל
      לומד''עס
    • חדש במערכת סנן רשומים ממאגר "אל תתקשר אליי"

      92a4779d-659c-4c13-a881-ba2f71cd1e46-image.png

      בהחלט דבר מתבקש שאפוו ענקי !!! לימות המשיח שלא מפסקים להשתדרג

      פורסם בעל הפורום
      ל
      לומד''עס
    • RE: אני צריך הקלטה של התפריט של הווצאפ

      @שרות-פלוס-ivr תודה עשיתי כזו למי שצריך העליתי

        %D7%AA%D7%A4%D7%A8%D7%99%D7%98-%D7%95%D7%95%D7%A6%D7%90%D7%A4.mp3

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • השמעת פרטי ההודעה

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

      פורסם בבקשות לפיתוח
      ל
      לומד''עס
    • RE: עזרה /מערכות להזמנות נסיעות

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • RE: הצעות ייעול - אייקונים לקטגוריות

      @snz יש לו הרבה דברים מעבר ל-לשנות איקונים וכאלה

      זה פחות דחוף לו לשנות

      פורסם בעל הפורום
      ל
      לומד''עס
    • RE: סגירת קווי התוכן והמידע

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

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • כלי HTML ליצירה אוטומטית של שלוחות הדף היומי (כל מסכתות הש״ס לפי סדרים) באמצעות ה־API

      שלום לכולם,

      בניתי כלי קטן בקובץ HTML באמצעות ה AI אחד שיוצר אוטומטית, דרך ה־API של ימות המשיח, מבנה שלוחות מלא לכל מסכתות הדף היומי, כולל קבצי הקראה (TTS). אין צורך בהתקנה: פותחים את הקובץ בדפדפן, מדביקים API Token ולוחצים.

      מה הכלי יוצר:

      שישה סדרים: זרעים, מועד, נשים, נזיקין, קדשים, טהרות
      כל סדר הוא שלוחת תפריט עם M0000.tts אוטומטי ("למסכת ביצה הקישו 1…")
      כל מסכת היא שלוחת השמעת קבצים (שלוחות 1–9), עם שם השלוחה ואפשרות ל־M0000.tts משלה
      בשלוחה 0 יש תפריט עם שאר המסכתות באותו סדר. בתפריט האחרון מוקרא "זוהי השלוחה האחרונה בסדר X"
      תפריט ראשי עם בחירת הסדר

      אפשרויות הגדרה:

      סדר המסכתות בתוך כל סדר: לפי סדר הש״ס, לפי א־ב או לפי מספר הדפים
      כמה מסכתות בכל תפריט (עד 9)
      בחירה אילו סדרים ליצור
      עריכה מלאה של נוסח ההקראה
      הגדרות ברירת מחדל (key=value) שנשלחות אוטומטית לכל שלוחות ההשמעה
      תצוגה מקדימה של כל השלוחות והקבצים לפני היצירה, התקדמות בזמן אמת, ו"נסה שוב" לפעולות שנכשלו

      איך זה עובד טכנית:

      ההתחברות היא באמצעות API KEY (טוקן קבוע מממשק חומת האש). הכלי משתמש בפקודות UpdateExtension ליצירה ועדכון שלוחות, ב־UploadTextFile להעלאת קבצי TTS, וב־GetSession לבדיקת חיבור. UpdateExtension מעדכן רק את מה שנשלח, ולכן לא נמחק תוכן קיים בשלוחות. קבצי M0000.tts קיימים באותו נתיב יידרסו.

      חשוב לדעת:

      הטוקן נשאר בדפדפן ולא נשמר ולא נשלח לשום מקום מלבד שרת ימות המשיח.
      אם הדפדפן חוסם את הקריאות (CORS), אפשר לעבור לשליחת הטוקן בכותרת authorization. אפשר גם להמיר את זה לסקריפט שרץ בשרת.
      סוג השלוחה ברירת המחדל הוא playfile. שמות הגדרות נוספות כדאי להעתיק מ־ext.ini של שלוחה שהגדרתם ידנית.
      מומלץ להריץ קודם על שלוחת ניסיון.

      הקובץ מצורף. אשמח לשמוע הערות והצעות לשיפור.

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>בונה שלוחות דף יומי – ימות המשיח</title>
      <link rel="preconnect" href="https://fonts.googleapis.com">
      <link href="https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@700;900&family=Heebo:wght@400;500;700&display=swap" rel="stylesheet">
      <style>
      :root{
        --bg:#f3f5f7; --card:#ffffff; --ink:#1b2a38; --muted:#5c6b79; --line:#d5dce3;
        --accent:#0e6a6a; --accent-ink:#ffffff; --warn:#a15c00; --bad:#b42318; --ok:#12733f; --code:#eef2f5;
      }
      @media (prefers-color-scheme:dark){
        :root{--bg:#10171e; --card:#18222c; --ink:#e6ecf1; --muted:#97a6b4; --line:#2b3a47;
              --accent:#4fc3c3; --accent-ink:#06201f; --warn:#f0b35a; --bad:#ff8a7a; --ok:#5fd18c; --code:#0f1720;}
      }
      *{box-sizing:border-box}
      body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.6 Heebo,system-ui,Arial,sans-serif}
      header{padding:28px 20px 8px;max-width:980px;margin:0 auto}
      h1{font:900 34px/1.2 "Frank Ruhl Libre",serif;margin:0 0 6px}
      header p{margin:0;color:var(--muted)}
      main{max-width:980px;margin:0 auto;padding:12px 20px 60px;display:grid;gap:16px}
      section{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:18px 20px}
      h2{font-size:18px;margin:0 0 12px}
      .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px 16px}
      label{display:block;font-size:14px;color:var(--muted);margin-bottom:4px}
      input[type=text],input[type=password],input[type=number],select,textarea{
        width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--ink);font:inherit}
      textarea{min-height:70px;resize:vertical}
      input[dir=ltr],textarea[dir=ltr]{text-align:left}
      input:focus-visible,select:focus-visible,textarea:focus-visible,button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
      .chk{display:flex;gap:8px;align-items:center;color:var(--ink);font-size:15px;margin:0}
      .chk input{width:auto}
      .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
      .sed{display:flex;gap:6px 18px;flex-wrap:wrap}
      button{font:inherit;font-weight:500;padding:9px 18px;border-radius:6px;border:1px solid var(--line);background:var(--card);color:var(--ink);cursor:pointer}
      button.primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
      button:disabled{opacity:.5;cursor:not-allowed}
      .hint{font-size:13px;color:var(--muted);margin-top:6px}
      .status{font-size:14px;margin-inline-start:6px}
      .ok{color:var(--ok)} .bad{color:var(--bad)} .warn{color:var(--warn)}
      table{width:100%;border-collapse:collapse;font-size:14px}
      th,td{padding:6px 8px;border-bottom:1px solid var(--line);text-align:start;vertical-align:top}
      th{color:var(--muted);font-weight:500}
      td.path{direction:ltr;text-align:left;font-family:ui-monospace,Consolas,monospace;font-size:13px;white-space:nowrap}
      .tts{color:var(--muted);font-size:13px}
      .wrap{max-height:420px;overflow:auto;border:1px solid var(--line);border-radius:6px}
      progress{width:100%;height:14px;accent-color:var(--accent)}
      #log{background:var(--code);border-radius:6px;padding:10px;max-height:260px;overflow:auto;font:13px/1.5 ui-monospace,Consolas,monospace;direction:ltr;text-align:left;white-space:pre-wrap}
      .pill{display:inline-block;background:var(--code);border-radius:99px;padding:1px 10px;font-size:13px;color:var(--muted)}
      </style>
      </head>
      <body>
      <header>
        <h1>בונה שלוחות הש״ס</h1>
        <p>יוצר אוטומטית שלוחות השמעת קבצים לכל המסכתות, מחולקות לפי סדרים, עם קבצי הקראה (TTS) – דרך ה־API של ימות המשיח.</p>
      </header>
      <main>
      
      <section>
        <h2>חיבור למערכת</h2>
        <div class="grid">
          <div><label for="token">API Token (מפתח קבוע מחומת האש)</label>
            <input id="token" type="password" dir="ltr" autocomplete="off" placeholder="הדבק כאן את הטוקן"></div>
          <div><label for="base">שלוחת הבסיס (למשל 7, או ריק לשלוחה הראשית)</label>
            <input id="base" type="text" dir="ltr" value="" placeholder="ריק = שלוחה ראשית"></div>
          <div><label for="server">שרת</label>
            <select id="server">
              <option value="https://www.call2all.co.il/ym/api/">www.call2all.co.il</option>
              <option value="https://private.call2all.co.il/ym/api/">private.call2all.co.il</option>
            </select></div>
          <div><label for="auth">אופן שליחת הטוקן</label>
            <select id="auth">
              <option value="param">פרמטר token (תואם דפדפן)</option>
              <option value="header">כותרת authorization (מומלץ בשרת)</option>
            </select></div>
        </div>
        <div class="row" style="margin-top:12px">
          <button id="btnTest" type="button">בדיקת חיבור</button><span id="testStatus" class="status"></span>
        </div>
        <p class="hint">הטוקן נשאר בדפדפן בלבד ולא נשמר. טוקן מסוג שם־משתמש:סיסמה דורש אימות דו־שלבי – מומלץ להשתמש ב־API KEY.</p>
      </section>
      
      <section>
        <h2>מבנה וסדר</h2>
        <div class="grid">
          <div><label for="order">סדר המסכתות בתוך כל סדר</label>
            <select id="order">
              <option value="shas">לפי סדר הש״ס</option>
              <option value="alpha">לפי א־ב</option>
              <option value="pages">לפי מספר הדפים</option>
            </select></div>
          <div><label for="pageSize">מסכתות בכל תפריט (עד 9, השאר ב־0)</label>
            <input id="pageSize" type="number" min="2" max="9" value="9"></div>
        </div>
        <div style="margin-top:12px"><label>סדרים שייווצרו</label><div class="sed" id="sedBox"></div></div>
        <div class="grid" style="margin-top:12px">
          <label class="chk"><input type="checkbox" id="emoji" checked> אימוג׳י בשם שלוחת הסדר</label>
          <label class="chk"><input type="checkbox" id="baseMenu" checked> הגדר את שלוחת הבסיס כתפריט (כשלא ראשית)</label>
          <label class="chk"><input type="checkbox" id="tractIntro" checked> קובץ M0000.tts בכל מסכת</label>
          <label class="chk"><input type="checkbox" id="backLine" checked> הוראת חזרה (כוכבית) בתפריטים</label>
        </div>
        <p class="hint">כל סדר הוא שלוחת תפריט. מסכתות הן שלוחות השמעת קבצים במספרים 1–9. ב־0 נמצא תפריט עם שאר המסכתות של אותו סדר, ובתפריט האחרון נאמר שזו השלוחה האחרונה.</p>
      </section>
      
      <section>
        <h2>סוגי שלוחות והגדרות ברירת מחדל</h2>
        <div class="grid">
          <div><label for="menuType">סוג שלוחת תפריט</label><input id="menuType" type="text" dir="ltr" value="menu"></div>
          <div><label for="playType">סוג שלוחת השמעת קבצים</label><input id="playType" type="text" dir="ltr" value="playfile"></div>
        </div>
        <div class="grid" style="margin-top:12px">
          <div><label for="playExtra">הגדרות נוספות לשלוחות ההשמעה (שורה לכל key=value)</label>
            <textarea id="playExtra" dir="ltr" placeholder="key=value"></textarea></div>
          <div><label for="menuExtra">הגדרות נוספות לשלוחות התפריט</label>
            <textarea id="menuExtra" dir="ltr" placeholder="key=value"></textarea></div>
        </div>
        <p class="hint">ההגדרות נשלחות ל־UpdateExtension, שמעדכן רק את מה שנשלח ולא מוחק את שאר ext.ini. את שמות ההגדרות מעתיקים מקובץ ext.ini של שלוחה קיימת שהגדרת כרצונך.</p>
      </section>
      
      <section>
        <h2>נוסח ההקראה</h2>
        <div class="grid">
          <div><label for="tRootIntro">פתיחת התפריט הראשי</label><input id="tRootIntro" type="text" value="ברוכים הבאים ללימוד הש״ס."></div>
          <div><label for="tRootItem">שורת סדר בתפריט הראשי</label><input id="tRootItem" type="text" value="לסדר {seder} הקישו {n}."></div>
          <div><label for="tSedIntro">פתיחת תפריט סדר</label><input id="tSedIntro" type="text" value="סדר {seder}."></div>
          <div><label for="tSedIntro2">פתיחת תפריט המשך</label><input id="tSedIntro2" type="text" value="המשך סדר {seder}."></div>
          <div><label for="tItem">שורת מסכת</label><input id="tItem" type="text" value="למסכת {name} הקישו {n}."></div>
          <div><label for="tMore">יש עוד מסכתות</label><input id="tMore" type="text" value="לשאר המסכתות בסדר זה הקישו 0."></div>
          <div><label for="tLast">התפריט האחרון בסדר</label><input id="tLast" type="text" value="זוהי השלוחה האחרונה בסדר {seder}."></div>
          <div><label for="tBack">הוראת חזרה</label><input id="tBack" type="text" value="לחזרה לתפריט הקודם הקישו כוכבית."></div>
          <div><label for="tTract">פתיחת מסכת</label><input id="tTract" type="text" value="מסכת {name}."></div>
        </div>
        <p class="hint">משתנים: {seder} {name} {n} {pages}. הקישו 0 מופיע רק כשיש תפריט המשך.</p>
      </section>
      
      <section>
        <h2>תצוגה מקדימה <span class="pill" id="count"></span></h2>
        <div class="wrap"><table><thead><tr><th>נתיב</th><th>שלוחה / קובץ</th></tr></thead><tbody id="preview"></tbody></table></div>
      </section>
      
      <section>
        <h2>יצירה במערכת</h2>
        <div class="row">
          <button id="btnRun" class="primary" type="button">צור שלוחות</button>
          <button id="btnStop" type="button" disabled>עצור</button>
          <button id="btnRetry" type="button" disabled>נסה שוב את הכשלונות</button>
          <span id="runStatus" class="status"></span>
        </div>
        <progress id="prog" value="0" max="1" style="margin-top:12px"></progress>
        <div id="log" style="margin-top:12px" aria-live="polite"></div>
        <p class="hint">קובצי M0000.tts קיימים באותו נתיב יידרסו. שלוחות קיימות יעודכנו בלבד, בלי מחיקת תוכן.</p>
      </section>
      
      </main>
      
      <script>
      /* ---------- נתונים (לפי הקובץ שצירפת) ---------- */
      const SEDARIM=[
        {name:'זרעים',emoji:'🌾'},{name:'מועד',emoji:'📜'},{name:'נשים',emoji:'💍'},
        {name:'נזיקין',emoji:'⚖️'},{name:'קדשים',emoji:'🏛️'},{name:'טהרות',emoji:'💧'}
      ];
      // [שם, אינדקס סדר, מספר דפים, דירוג א־ב לפי הקובץ] – לפי סדר הש״ס
      const RAW=[
       ['ברכות',0,63,6],['שבת',1,156,36],['עירובין',1,104,29],['פסחים',1,120,31],['שקלים',1,21,37],
       ['ראש השנה',1,34,34],['יומא',1,87,13],['סוכה',1,55,26],['ביצה',1,39,4],['תענית',1,30,40],
       ['מגילה',1,31,16],['מועד קטן',1,28,18],['חגיגה',1,26,10],
       ['יבמות',2,121,12],['כתובות',2,111,15],['נדרים',2,90,23],['נזיר',2,65,24],['סוטה',2,48,25],
       ['גיטין',2,89,7],['קידושין',2,81,32],
       ['בבא קמא',3,118,2],['בבא מציעא',3,118,1],['בבא בתרא',3,175,3],['סנהדרין',3,112,27],
       ['מכות',3,23,19],['שבועות',3,48,35],['עבודה זרה',3,75,28],['הוריות',3,13,8],
       ['זבחים',4,119,9],['מנחות',4,109,20],['חולין',4,141,11],['בכורות',4,60,5],['ערכין',4,33,30],
       ['תמורה',4,33,38],['כריתות',4,27,14],['מעילה',4,20,21],['קינים',4,3,33],['מדות',4,4,17],['תמיד',4,9,39],
       ['נדה',5,72,22]
      ];
      const TR=RAW.map((r,i)=>({name:r[0],seder:r[1],pages:r[2],alpha:r[3],shas:i+1}));
      
      /* ---------- עזרים ---------- */
      const $=id=>document.getElementById(id);
      const fill=(t,m)=>t.replace(/\{(\w+)\}/g,(_,k)=>m[k]!==undefined?m[k]:'{'+k+'}');
      const esc=s=>String(s).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
      const chunk=(a,n)=>{const o=[];for(let i=0;i<a.length;i+=n)o.push(a.slice(i,i+n));return o};
      const join=(...a)=>a.filter(x=>x!=='').join('/');
      function sortList(list,mode){
        const l=[...list];
        if(mode==='alpha')l.sort((a,b)=>a.alpha-b.alpha);
        else if(mode==='pages')l.sort((a,b)=>a.pages-b.pages||a.shas-b.shas);
        else l.sort((a,b)=>a.shas-b.shas);
        return l;
      }
      function parseExtra(txt){
        const o={};
        txt.split(/\r?\n/).forEach(line=>{
          const i=line.indexOf('=');
          if(i>0){const k=line.slice(0,i).trim(),v=line.slice(i+1).trim();if(k)o[k]=v}
        });
        return o;
      }
      function normBase(v){
        return v.trim().replace(/^ivr2?:/i,'').replace(/^\/+|\/+$/g,'');
      }
      
      /* ---------- בניית התוכנית ---------- */
      function opts(){
        const seds=[...document.querySelectorAll('#sedBox input:checked')].map(x=>+x.value);
        const g=id=>$(id).value;
        return {
          base:normBase(g('base')), order:g('order'),
          pageSize:Math.min(9,Math.max(2,parseInt(g('pageSize'))||9)),
          seds, emoji:$('emoji').checked, baseMenu:$('baseMenu').checked,
          tractIntro:$('tractIntro').checked, backLine:$('backLine').checked,
          menuType:g('menuType').trim()||'menu', playType:g('playType').trim()||'playfile',
          playExtra:parseExtra(g('playExtra')), menuExtra:parseExtra(g('menuExtra')),
          t:{rootIntro:g('tRootIntro'),rootItem:g('tRootItem'),sedIntro:g('tSedIntro'),sedIntro2:g('tSedIntro2'),
             item:g('tItem'),more:g('tMore'),last:g('tLast'),back:g('tBack'),tract:g('tTract')}
        };
      }
      
      function buildPlan(o){
        const ops=[];
        const baseDepth=o.base?o.base.split('/').length:0;
        const depthOf=p=>(p?p.split('/').length:0)-baseDepth;
        const add=(op)=>{op.depth=depthOf(op.path);ops.push(op)};
      
        if(o.baseMenu&&o.base)
          add({k:'ext',path:o.base,label:'שלוחת בסיס',params:{type:o.menuType,...o.menuExtra}});
      
        const sed=o.seds.map(i=>({i,...SEDARIM[i]}));
        const rootText=[o.t.rootIntro,...sed.map((s,k)=>fill(o.t.rootItem,{seder:s.name,n:k+1}))].filter(Boolean).join(' ');
        add({k:'tts',path:o.base,file:'M0000.tts',text:rootText});
      
        sed.forEach((s,si)=>{
          let p=join(o.base,si+1);
          const list=sortList(TR.filter(t=>t.seder===s.i),o.order);
          const pages=chunk(list,o.pageSize);
          pages.forEach((items,pi)=>{
            const isLast=pi===pages.length-1;
            const title=pi===0?((o.emoji?s.emoji+' ':'')+s.name):('המשך '+s.name);
            add({k:'ext',path:p,label:title,params:{type:o.menuType,title,...o.menuExtra}});
            const parts=[fill(pi===0?o.t.sedIntro:o.t.sedIntro2,{seder:s.name})];
            items.forEach((t,ti)=>parts.push(fill(o.t.item,{name:t.name,n:ti+1,pages:t.pages,seder:s.name})));
            if(!isLast)parts.push(o.t.more);
            else if(pi>0)parts.push(fill(o.t.last,{seder:s.name}));
            if(o.backLine)parts.push(o.t.back);
            add({k:'tts',path:p,file:'M0000.tts',text:parts.filter(Boolean).join(' ')});
            items.forEach((t,ti)=>{
              const tp=join(p,ti+1);
              add({k:'ext',path:tp,label:t.name,params:{type:o.playType,title:t.name,...o.playExtra}});
              if(o.tractIntro)add({k:'tts',path:tp,file:'M0000.tts',text:fill(o.t.tract,{name:t.name,pages:t.pages,seder:s.name})});
            });
            p=join(p,0);
          });
        });
        return ops;
      }
      
      /* ---------- תצוגה מקדימה ---------- */
      function render(){
        const ops=buildPlan(opts());
        $('count').textContent=ops.length+' פעולות';
        $('preview').innerHTML=ops.map(op=>{
          const pad=`style="padding-inline-start:${8+op.depth*18}px"`;
          const path=op.path||'/';
          if(op.k==='ext')
            return `<tr><td class="path">${esc(path)}</td><td ${pad}><b>${esc(op.label)}</b> <span class="tts">(${esc(op.params.type)})</span></td></tr>`;
          return `<tr><td class="path">${esc(path)}/${op.file}</td><td ${pad}><span class="tts">${esc(op.text)}</span></td></tr>`;
        }).join('');
      }
      
      /* ---------- API ---------- */
      async function api(cmd,params,method){
        const server=$('server').value, token=$('token').value.trim();
        const p=new URLSearchParams(params), headers={};
        if($('auth').value==='header')headers.authorization=token; else p.set('token',token);
        let res;
        if(method==='POST'){
          headers['Content-Type']='application/x-www-form-urlencoded;charset=UTF-8';
          res=await fetch(server+cmd,{method:'POST',headers,body:p});
        }else{
          res=await fetch(server+cmd+'?'+p.toString(),{headers});
        }
        return res.json();
      }
      const ivrDir=p=>'ivr2:'+p;
      const ivrFile=(p,f)=>p?`ivr2:${p}/${f}`:`ivr2:/${f}`;
      
      async function doOp(op){
        let j;
        if(op.k==='ext')j=await api('UpdateExtension',{path:ivrDir(op.path),...op.params},'GET');
        else j=await api('UploadTextFile',{what:ivrFile(op.path,op.file),contents:op.text},'POST');
        if(j.responseStatus!=='OK')throw new Error((j.message||j.exceptionMessage||j.responseStatus)+(j.message==='MFA_REQUIRED'?' – השתמש בטוקן מסוג API KEY':''));
      }
      const opName=op=>op.k==='ext'?`ext  ${op.path||'/'}  (${op.label})`:`tts  ${op.path||''}/${op.file}`;
      
      $('btnTest').onclick=async()=>{
        const st=$('testStatus');
        if(!$('token').value.trim()){st.className='status bad';st.textContent='חסר טוקן';return}
        st.className='status';st.textContent='בודק…';
        try{
          const j=await api('GetSession',{},'GET');
          if(j.responseStatus==='OK'){st.className='status ok';st.textContent='מחובר: מערכת '+(j.username||'')+(j.name?' – '+j.name:'')}
          else{st.className='status bad';st.textContent=(j.message||j.responseStatus)}
        }catch(e){st.className='status bad';st.textContent='לא ניתן להתחבר מהדפדפן (ייתכן חסם CORS או שהקובץ נפתח בלי אינטרנט)'}
      };
      
      /* ---------- הרצה ---------- */
      let stopFlag=false, failed=[];
      const log=m=>{const l=$('log');l.textContent+=m+'\n';l.scrollTop=l.scrollHeight};
      
      async function run(ops){
        stopFlag=false;failed=[];
        $('btnRun').disabled=true;$('btnRetry').disabled=true;$('btnStop').disabled=false;
        $('prog').max=ops.length;$('prog').value=0;
        let done=0;
        for(const op of ops){
          if(stopFlag){log('— נעצר ידנית —');break}
          let err=null;
          for(let a=0;a<3;a++){
            try{await doOp(op);err=null;break}catch(e){err=e;await new Promise(r=>setTimeout(r,500*(a+1)))}
          }
          if(err){failed.push(op);log('✗ '+opName(op)+'  → '+err.message)}
          else log('✓ '+opName(op));
          $('prog').value=++done;
          $('runStatus').textContent=`${done} מתוך ${ops.length}`;
        }
        $('btnRun').disabled=false;$('btnStop').disabled=true;$('btnRetry').disabled=failed.length===0;
        $('runStatus').className='status '+(failed.length?'warn':'ok');
        $('runStatus').textContent=failed.length?`הסתיים עם ${failed.length} כשלונות`:'הסתיים בהצלחה';
      }
      
      $('btnRun').onclick=()=>{
        if(!$('token').value.trim()){alert('יש להזין API Token');return}
        const o=opts();
        if(!o.seds.length){alert('יש לבחור לפחות סדר אחד');return}
        const ops=buildPlan(o);
        if(!confirm(`ייווצרו או יעודכנו ${ops.length} פעולות במערכת${o.base?' תחת שלוחה '+o.base:' בשלוחה הראשית'}.\nקבצי M0000.tts קיימים יידרסו. להמשיך?`))return;
        $('log').textContent='';
        run(ops);
      };
      $('btnStop').onclick=()=>{stopFlag=true};
      $('btnRetry').onclick=()=>{if(failed.length){$('log').textContent+='— ניסיון חוזר —\n';run([...failed])}};
      
      /* ---------- אתחול ---------- */
      $('sedBox').innerHTML=SEDARIM.map((s,i)=>`<label class="chk"><input type="checkbox" value="${i}" checked> ${s.emoji} ${s.name}</label>`).join('');
      document.querySelectorAll('input,select,textarea').forEach(el=>{
        if(el.id!=='token')el.addEventListener('input',render);
      });
      render();
      </script>
      </body>
      </html>
      
      

      עוד סוג של קוד

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>בונה שלוחות ש"ס אוטומטי - ימות המשיח</title>
        <script src="https://cdn.tailwindcss.com"></script>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Heebo:wght@300;400;500;600;700;800&family=Frank+Ruhl+Libre:wght@700;800&display=swap" rel="stylesheet">
        <style>
          body {
            font-family: 'Heebo', sans-serif;
          }
          .font-serif-hebrew {
            font-family: 'Frank Ruhl Libre', serif;
          }
          /* Custom scrollbars */
          ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
          }
          ::-webkit-scrollbar-track {
            background: #f1f5f9;
          }
          ::-webkit-scrollbar-thumb {
            background: #cbd5e1;
            border-radius: 4px;
          }
          ::-webkit-scrollbar-thumb:hover {
            background: #94a3b8;
          }
        </style>
      </head>
      <body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col">
      
        <header class="bg-gradient-to-r from-blue-900 via-indigo-900 to-slate-900 text-white shadow-lg sticky top-0 z-40">
          <div class="max-w-7xl mx-auto px-4 py-4 flex flex-col sm:flex-row justify-between items-center gap-3">
            <div class="flex items-center gap-3">
              <div class="w-12 h-12 bg-amber-400/20 border border-amber-300/40 rounded-xl flex items-center justify-center text-2xl shadow-inner">
                📖
              </div>
              <div>
                <h1 class="text-xl sm:text-2xl font-bold font-serif-hebrew tracking-wide text-amber-200">יוצר שלוחות ש"ס למערכות ימות המשיח</h1>
                <p class="text-xs text-slate-300">הקמת תפריטי סדרים ומסכתות, שלוחות השמעת קבצים וקובצי TTS באופן אוטומטי מלא</p>
              </div>
            </div>
            <div class="flex items-center gap-2">
              <span id="apiStatusBadge" class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-slate-800 text-slate-300 border border-slate-700">
                <span class="w-2 h-2 rounded-full bg-slate-500"></span> לא מחובר
              </span>
            </div>
          </div>
        </header>
      
        <main class="max-w-7xl mx-auto px-4 py-6 flex-1 w-full grid grid-cols-1 lg:grid-cols-12 gap-6">
      
          <!-- Left / Control Sidebar (4 cols) -->
          <div class="lg:col-span-4 space-y-6">
            
            <!-- API Connection Box -->
            <section class="bg-white rounded-2xl shadow-sm border border-slate-200 p-5">
              <div class="flex items-center justify-between mb-4 border-b border-slate-100 pb-3">
                <h2 class="text-base font-bold text-slate-800 flex items-center gap-2">
                  <span>🔑</span> פרטי חיבור ל-API
                </h2>
                <span class="text-xs text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded font-mono">Yemot IVR</span>
              </div>
              
              <div class="space-y-4">
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">טוקן API (API Token / Key)</label>
                  <input type="password" id="apiTokenInput" placeholder="הזן טוקן קבוע או זמני..." 
                         class="w-full text-sm px-3 py-2 rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 font-mono">
                  <p class="text-[11px] text-slate-400 mt-1">טוקן קבוע שהונפק במערכת או שילוב מספר:סיסמה</p>
                </div>
      
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">נתיב שלוחת הבסיס (Root Path)</label>
                  <div class="flex items-center">
                    <span class="inline-flex items-center px-3 py-2 rounded-r-lg border border-l-0 border-slate-300 bg-slate-100 text-slate-500 text-sm font-mono dir-ltr">/</span>
                    <input type="text" id="basePathInput" value="1" placeholder="לדוגמה: 1 או 8/1" 
                           class="w-full text-sm px-3 py-2 rounded-l-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 font-mono">
                  </div>
                  <p class="text-[11px] text-slate-400 mt-1">באיזו שלוחה יוקם עץ הש"ס (השאר ריק עבור השלוחה הראשית /)</p>
                </div>
      
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">פרוקסי CORS (לשליחה ישירה מהדפדפן)</label>
                  <select id="corsProxySelect" class="w-full text-sm px-3 py-2 rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white">
                    <option value="direct">ללא פרוקסי (פנייה ישירה ל-call2all)</option>
                    <option value="corsproxy" selected>CORS Proxy ציבורי (corsproxy.io)</option>
                    <option value="allorigins">AllOrigins Proxy</option>
                    <option value="custom">פרוקסי מותאם אישית...</option>
                  </select>
                  <input type="text" id="customProxyInput" placeholder="https://your-proxy.com/?url=" 
                         class="hidden mt-2 w-full text-xs px-3 py-1.5 rounded-lg border border-slate-300 font-mono">
                </div>
      
                <div class="pt-2 flex gap-2">
                  <button id="testConnectionBtn" type="button" class="flex-1 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold py-2 px-3 rounded-lg border border-slate-300 transition-colors flex items-center justify-center gap-1.5">
                    <span>🔌</span> בדוק חיבור
                  </button>
                  <button id="viewSessionBtn" type="button" class="bg-slate-50 hover:bg-slate-100 text-slate-600 text-xs py-2 px-3 rounded-lg border border-slate-200 transition-colors" title="פרטי מערכת ויחידות">
                    ℹ️ פרטים
                  </button>
                </div>
      
                <div id="connectionMsg" class="hidden text-xs p-2.5 rounded-lg border"></div>
              </div>
            </section>
      
            <section class="bg-white rounded-2xl shadow-sm border border-slate-200 p-5">
              <div class="flex items-center justify-between mb-4 border-b border-slate-100 pb-3">
                <h2 class="text-base font-bold text-slate-800 flex items-center gap-2">
                  <span>⚙️</span> הגדרות שלוחות השמעת קבצים
                </h2>
                <span class="text-xs bg-emerald-50 text-emerald-700 px-2 py-0.5 rounded font-mono font-bold">playfile</span>
              </div>
      
              <p class="text-xs text-slate-500 mb-3">הגדרות שיוטמעו בקובץ <code class="bg-slate-100 px-1 py-0.5 rounded text-indigo-700 font-mono">ext.ini</code> של כל שלוחת מסכת:</p>
      
              <div class="space-y-3 text-xs">
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">סוג שלוחה (type)</label>
                  <input type="text" id="settingType" value="playfile" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-slate-50 font-mono" readonly>
                </div>
      
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">סדר השמעת קבצים (playfile_order)</label>
                  <select id="settingOrder" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                    <option value="asc" selected>מהישן לחדש (סדר עולה 000, 001...)</option>
                    <option value="desc">מהחדש לישן (סדר יורד)</option>
                  </select>
                </div>
      
                <div class="grid grid-cols-2 gap-2">
                  <div>
                    <label class="font-semibold text-slate-700 block mb-1">השמעה אוטומטית</label>
                    <select id="settingAutoPlay" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                      <option value="yes" selected>כן (yes)</option>
                      <option value="no">לא (no)</option>
                    </select>
                  </div>
                  <div>
                    <label class="font-semibold text-slate-700 block mb-1">זכירת מיקום מאזין</label>
                    <select id="settingRemember" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                      <option value="yes" selected>כן (שמירת מיקום)</option>
                      <option value="no">לא (מהתחלה)</option>
                    </select>
                  </div>
                </div>
      
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">שורות הגדרה נוספות (ext.ini)</label>
                  <textarea id="settingExtraIni" rows="3" class="w-full px-2.5 py-1.5 rounded border border-slate-300 font-mono text-[11px]" placeholder="לדוגמה:&#10;playfile_end_goto=/&#10;speed=1.0"></textarea>
                </div>
      
                <div class="pt-2">
                  <label class="font-semibold text-slate-700 block mb-1">תבנית הודעת הקראה למסכת (TTS)</label>
                  <input type="text" id="massechetTtsTemplate" value="מסכת {name}, {pages} דפים" 
                         class="w-full px-2.5 py-1.5 rounded border border-slate-300 text-xs">
                  <p class="text-[10px] text-slate-400 mt-0.5">קובץ M0000.TTS בשלוחת המסכת להכרזת השם</p>
                </div>
              </div>
            </section>
      
            <!-- Execution Panel -->
            <section class="bg-gradient-to-br from-indigo-900 to-slate-900 text-white rounded-2xl p-5 shadow-lg space-y-4">
              <h3 class="text-base font-bold text-amber-300 flex items-center gap-2">
                <span>🚀</span> יצירה במערכת בפועל
              </h3>
              <p class="text-xs text-slate-300 leading-relaxed">
                בלחיצה על הכפתור, המערכת תשלח פקודות <span class="text-amber-200 font-mono">UpdateExtension</span> ו-<span class="text-amber-200 font-mono">UploadTextFile</span> לשרתי ימות המשיח, תבנה את 6 הסדרים, כל דפי הניווט (כולל מקש 0 לעוד מסכתות), וכל שלוחות ההשמעה עם קובצי TTS.
              </p>
      
              <div class="flex flex-col gap-2 pt-1">
                <button id="startBuildBtn" type="button" class="w-full bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold py-3 px-4 rounded-xl shadow-md transition-all text-sm flex items-center justify-center gap-2">
                  <span>⚡</span> צור את כל שלוחות הש"ס במערכת
                </button>
                
                <button id="downloadZipBtn" type="button" class="w-full bg-slate-800 hover:bg-slate-700 text-slate-200 py-2 px-3 rounded-lg border border-slate-700 text-xs transition-colors flex items-center justify-center gap-1.5">
                  <span>💾</span> הורד עותק כקובץ JSON מקומי
                </button>
              </div>
      
              <!-- Progress bar -->
              <div id="buildProgressContainer" class="hidden space-y-1.5 pt-2 border-t border-slate-800">
                <div class="flex justify-between text-xs text-slate-300">
                  <span id="progressText">מכין שלוחות...</span>
                  <span id="progressPercent" class="font-mono">0%</span>
                </div>
                <div class="w-full bg-slate-800 rounded-full h-2 overflow-hidden border border-slate-700">
                  <div id="progressBar" class="bg-amber-400 h-full w-0 transition-all duration-150"></div>
                </div>
              </div>
            </section>
      
          </div>
      
          <div class="lg:col-span-8 space-y-6">
            
            <!-- Top Overview Stats -->
            <div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סך הכל סדרים</div>
                <div class="text-2xl font-bold text-indigo-900 mt-1">6</div>
                <div class="text-[11px] text-slate-400 mt-0.5">מש"ס בבלי</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סך כל המסכתות</div>
                <div class="text-2xl font-bold text-amber-600 mt-1">40</div>
                <div class="text-[11px] text-slate-400 mt-0.5">כולל מסכת שקלים</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סה"כ דפים בש"ס</div>
                <div class="text-2xl font-bold text-emerald-600 mt-1 font-mono">2,711</div>
                <div class="text-[11px] text-slate-400 mt-0.5">דפי גמרא</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">שלוחות שייווצרו</div>
                <div id="statTotalExtensions" class="text-2xl font-bold text-blue-600 mt-1 font-mono">49</div>
                <div class="text-[11px] text-slate-400 mt-0.5">סדרים + דפי ניווט + מסכתות</div>
              </div>
            </div>
      
            <!-- Tabs Header -->
            <div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
              <div class="flex border-b border-slate-200 bg-slate-50/70 p-1.5 gap-1.5 text-xs font-semibold">
                <button id="tabTreeBtn" class="tab-btn px-4 py-2 rounded-lg bg-white text-indigo-700 shadow-sm border border-slate-200/80">
                  🌳 עץ השלוחות והמסכתות המלא
                </button>
                <button id="tabSimulatorBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📞 סימולטור ניווט קולי
                </button>
                <button id="tabPayloadBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📄 קבצי ini ו-TTS שיישלחו
                </button>
                <button id="tabLogsBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📋 יומן פעולות (Logs)
                </button>
              </div>
      
              <div id="tabTreeContent" class="p-5">
                <div class="flex flex-col sm:flex-row justify-between sm:items-center gap-2 mb-4">
                  <div>
                    <h3 class="font-bold text-slate-800 text-sm">מבנה השלוחות המתוכנן לפי סדר הש"ס</h3>
                    <p class="text-xs text-slate-500">לחיצה על סדר או שלוחה תציג את הפירוט, התפריטים וקובצי ההקראה</p>
                  </div>
                  <div class="flex gap-2">
                    <button id="expandAllTreeBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded border border-slate-300">פתח הכל</button>
                    <button id="collapseAllTreeBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded border border-slate-300">סגור הכל</button>
                  </div>
                </div>
      
                <div id="treeContainer" class="space-y-3 font-mono text-xs">
                  <!-- Dynamically populated -->
                </div>
              </div>
      
              <div id="tabSimulatorContent" class="p-5 hidden">
                <div class="max-w-xl mx-auto bg-slate-900 text-white rounded-3xl p-6 shadow-2xl border border-slate-800">
                  <div class="flex items-center justify-between pb-4 border-b border-slate-800 mb-4">
                    <div class="flex items-center gap-2">
                      <span class="w-3 h-3 rounded-full bg-emerald-500 animate-pulse"></span>
                      <span class="text-xs font-semibold text-slate-300">סימולטור ימות המשיח בזמן אמת</span>
                    </div>
                    <span id="simCurrentPath" class="text-xs font-mono text-amber-300 bg-slate-800 px-2 py-0.5 rounded">שלוחה ראשית /</span>
                  </div>
      
                  <!-- Screen: Speech Bubble of IVR TTS -->
                  <div class="bg-slate-800/80 rounded-2xl p-4 border border-slate-700 mb-5 min-h-[120px] flex flex-col justify-between">
                    <div>
                      <span class="text-[11px] font-bold uppercase tracking-wider text-amber-400 block mb-1">📢 הקראה קולית (M0000.TTS):</span>
                      <p id="simVoiceText" class="text-sm text-slate-100 font-sans leading-relaxed">ברוכים הבאים לש"ס בבלי. לבחירת סדר: הקישו 1 לזרעים, 2 למועד, 3 לנשים, 4 לנזיקין, 5 לקדשים, 6 לטהרות.</p>
                    </div>
                    <div class="mt-3 pt-2 border-t border-slate-700/60 flex items-center justify-between text-[11px] text-slate-400">
                      <span id="simExtType">סוג: תפריט (menu)</span>
                      <button id="simSpeakAudioBtn" class="text-indigo-300 hover:text-indigo-200 flex items-center gap-1">
                        <span>🔊</span> השמע בדפדפן (TTS)
                      </button>
                    </div>
                  </div>
      
                  <!-- Phone Keypad -->
                  <div class="grid grid-cols-3 gap-3 max-w-[280px] mx-auto mb-4">
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="1">1</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="2">2</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="3">3</button>
                    
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="4">4</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="5">5</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="6">6</button>
                    
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="7">7</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="8">8</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="9">9</button>
                    
                    <button class="keypad-btn bg-slate-800/50 hover:bg-slate-800 font-bold text-sm py-3 rounded-xl border border-slate-700 text-slate-400" data-key="*">* (חזרה)</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="0">0</button>
                    <button class="keypad-btn bg-slate-800/50 hover:bg-slate-800 font-bold text-sm py-3 rounded-xl border border-slate-700 text-slate-400" data-key="#"># (אישור)</button>
                  </div>
      
                  <div class="flex justify-between items-center text-xs pt-2 border-t border-slate-800 text-slate-400">
                    <button id="simResetBtn" class="hover:text-amber-300">🔄 חזרה לראש התפריט</button>
                    <span>מקש 0 יעביר למסכתות נוספות בסדר</span>
                  </div>
                </div>
              </div>
      
              <div id="tabPayloadContent" class="p-5 hidden">
                <div class="flex justify-between items-center mb-3">
                  <h3 class="font-bold text-slate-800 text-sm">סקירת קבצים ופקודות שיישלחו למערכת</h3>
                  <span class="text-xs text-slate-500">קבצי ext.ini וקובצי M0000.TTS</span>
                </div>
      
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                  <div class="border border-slate-200 rounded-xl p-3 bg-slate-50 max-h-[460px] overflow-y-auto space-y-1" id="payloadList">
                    <!-- Dynamically populated list of files -->
                  </div>
                  <div class="md:col-span-2 border border-slate-200 rounded-xl p-4 bg-slate-900 text-slate-100 flex flex-col">
                    <div class="flex justify-between items-center border-b border-slate-800 pb-2 mb-2 text-xs">
                      <span id="payloadFileTitle" class="font-mono text-amber-300 font-bold">בחר קובץ מהרשימה</span>
                      <span id="payloadFilePath" class="text-slate-400 font-mono text-[11px]"></span>
                    </div>
                    <pre id="payloadFileContent" class="font-mono text-xs overflow-auto flex-1 p-2 bg-slate-950/60 rounded text-emerald-400 leading-relaxed whitespace-pre-wrap select-all">בחר קובץ מימין לצפייה בתוכנו המדויק</pre>
                  </div>
                </div>
              </div>
      
              <div id="tabLogsContent" class="p-5 hidden">
                <div class="flex justify-between items-center mb-3">
                  <h3 class="font-bold text-slate-800 text-sm">יומן בקשות ותשובות API</h3>
                  <button id="clearLogsBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-600 rounded">נקה יומן</button>
                </div>
                <div id="logsConsole" class="bg-slate-950 text-slate-200 rounded-xl p-4 font-mono text-xs h-96 overflow-y-auto space-y-1.5 border border-slate-800">
                  <div class="text-slate-500">// יומן הפעולות מוכן. לחץ על 'בדוק חיבור' או 'צור שלוחות'.</div>
                </div>
              </div>
      
            </div>
      
          </div>
      
        </main>
      
        <footer class="bg-slate-100 border-t border-slate-200 text-slate-500 text-xs py-4 text-center">
          <div class="max-w-7xl mx-auto px-4">
            מערכת ניהול ובניית שלוחות ש"ס למרכזיית ימות המשיח • תואם API 2026 • מודול השמעת קבצים והקראה אוטומטית (TTS)
          </div>
        </footer>
      
        <script>
          /*
           * נתוני ששת סדרי משנה וש"ס בבלי
           * הסדר המדויק, המספר הסידורי, וכמות הדפים עפ"י הקובץ המצורף (42389.pdf)
           */
          const SEDER_DATA = [
            {
              id: 1,
              key: "1",
              name: "זרעים",
              icon: "🌾",
              title: "סדר זרעים",
              masechtot: [
                { order: 1, name: "ברכות", pages: 63 }
              ]
            },
            {
              id: 2,
              key: "2",
              name: "מועד",
              icon: "📜",
              title: "סדר מועד",
              masechtot: [
                { order: 2, name: "שבת", pages: 156 },
                { order: 3, name: "עירובין", pages: 104 },
                { order: 4, name: "פסחים", pages: 120 },
                { order: 5, name: "שקלים", pages: 21 },
                { order: 6, name: "ראש השנה", pages: 34 },
                { order: 7, name: "יומא", pages: 87 },
                { order: 8, name: "סוכה", pages: 55 },
                { order: 9, name: "ביצה", pages: 39 },
                { order: 10, name: "תענית", pages: 30 },
                { order: 11, name: "מגילה", pages: 31 },
                { order: 12, name: "מועד קטן", pages: 28 },
                { order: 13, name: "חגיגה", pages: 26 }
              ]
            },
            {
              id: 3,
              key: "3",
              name: "נשים",
              icon: "💍",
              title: "סדר נשים",
              masechtot: [
                { order: 14, name: "יבמות", pages: 121 },
                { order: 15, name: "כתובות", pages: 111 },
                { order: 16, name: "נדרים", pages: 90 },
                { order: 17, name: "נזיר", pages: 65 },
                { order: 18, name: "סוטה", pages: 48 },
                { order: 19, name: "גיטין", pages: 89 },
                { order: 20, name: "קידושין", pages: 81 }
              ]
            },
            {
              id: 4,
              key: "4",
              name: "נזיקין",
              icon: "⚖️",
              title: "סדר נזיקין",
              masechtot: [
                { order: 21, name: "בבא קמא", pages: 118 },
                { order: 22, name: "בבא מציעא", pages: 118 },
                { order: 23, name: "בבא בתרא", pages: 175 },
                { order: 24, name: "סנהדרין", pages: 112 },
                { order: 25, name: "מכות", pages: 23 },
                { order: 26, name: "שבועות", pages: 48 },
                { order: 27, name: "עבודה זרה", pages: 75 },
                { order: 28, name: "הוריות", pages: 13 }
              ]
            },
            {
              id: 5,
              key: "5",
              name: "קדשים",
              icon: "🏛️",
              title: "סדר קדשים",
              masechtot: [
                { order: 29, name: "זבחים", pages: 119 },
                { order: 30, name: "מנחות", pages: 109 },
                { order: 31, name: "חולין", pages: 141 },
                { order: 32, name: "בכורות", pages: 60 },
                { order: 33, name: "ערכין", pages: 33 },
                { order: 34, name: "תמורה", pages: 33 },
                { order: 35, name: "כריתות", pages: 27 },
                { order: 36, name: "מעילה", pages: 20 },
                { order: 37, name: "קינים", pages: 3 },
                { order: 38, name: "תמיד", pages: 9 },
                { order: 39, name: "מידות", pages: 4 }
              ]
            },
            {
              id: 6,
              key: "6",
              name: "טהרות",
              icon: "✨",
              title: "סדר טהרות",
              masechtot: [
                { order: 40, name: "נדה", pages: 72 }
              ]
            }
          ];
      
          const State = {
            apiToken: '',
            basePath: '1',
            generatedPlan: [],
            filesMap: new Map(), // path -> { type, content, desc }
            simState: {
              currentPath: '',
              history: []
            }
          };
      
          /**
           * פונקציה לבניית מבנה העץ המלא של השלוחות, התפריטים, קבצי ה-ext.ini וקבצי ה-TTS
           * בהתאם לדרישת הלקוח:
           * - תפריט סדרים
           * - בכל סדר תפריט מסכתות מפוצל לדפים (1-9 מסכתות, מקש 0 מעביר לעוד מסכתות)
           * - בשלוחת 0 האחרונה: הודעה שזו השלוחה האחרונה
           * - שלוחות מסכת מסוג השמעת קבצים (playfile) עם קובץ TTS
           */
          function generateFullIvrPlan() {
            const rootPath = (State.basePath.trim() === '' || State.basePath.trim() === '/') ? '' : State.basePath.trim().replace(/^\/+|\/+$/g, '');
            const formatPath = (...parts) => parts.filter(p => p !== '' && p !== undefined).join('/');
      
            const plan = [];
            State.filesMap.clear();
      
            // 1. שלוחת הש"ס הראשית (תפריט סדרים)
            const rootIvrPath = rootPath === '' ? '' : rootPath;
            const rootTTS = "ברוכים הבאים לש\"ס בבלי. " +
              SEDER_DATA.map(s => `לסדר ${s.name} הקישו ${s.key}`).join(", ") + ".";
      
            const rootIni = [
              "type=menu",
              "title=ש\"ס בבלי - בחירת סדר",
              ...SEDER_DATA.map(s => `${s.key}=${formatPath(rootIvrPath, s.key)}`)
            ].join("\n");
      
            plan.push({
              path: rootIvrPath || '/',
              title: 'ש"ס בבלי - ראשי',
              type: 'menu',
              description: 'תפריט בחירת סדר',
              ini: rootIni,
              tts: rootTTS,
              ttsFileName: 'M0000.TTS'
            });
      
            // רישום קבצים
            State.filesMap.set(formatPath(rootIvrPath, 'ext.ini') || 'ext.ini', {
              title: 'הגדרות שלוחה ראשית',
              content: rootIni
            });
            State.filesMap.set(formatPath(rootIvrPath, 'M0000.TTS') || 'M0000.TTS', {
              title: 'הקראת תפריט סדרים',
              content: rootTTS
            });
      
            // 2. מעבר על כל סדר
            SEDER_DATA.forEach(seder => {
              const sederBasePath = formatPath(rootIvrPath, seder.key);
              const masechtot = seder.masechtot;
      
              // אם יש עד 9 מסכתות - דף יחיד. אם יותר - חלוקה לקבוצות של 9
              // מקשים 1-9 הם למסכתות, מקש 0 הוא מעבר לקבוצה הבאה (או השלוחה האחרונה)
              const CHUNK_SIZE = 9;
              const totalPages = Math.ceil(masechtot.length / CHUNK_SIZE) || 1;
      
              for (let pageIdx = 0; pageIdx < totalPages; pageIdx++) {
                const isFirstPage = (pageIdx === 0);
                // נתיב הדף: דף ראשון בסדר הוא סדר.קיי, דף הבא הוא תחת שלוחה 0 וכך הלאה
                let pagePath = sederBasePath;
                for (let p = 0; p < pageIdx; p++) {
                  pagePath = formatPath(pagePath, '0');
                }
      
                const startIdx = pageIdx * CHUNK_SIZE;
                const pageMasechtot = masechtot.slice(startIdx, startIdx + CHUNK_SIZE);
                const hasNextPage = (pageIdx + 1 < totalPages);
      
                // בניית טקסט TTS והגדרות ext.ini לתפריט המסכתות
                let pageTtsParts = [`סדר ${seder.name}, חלק ${pageIdx + 1}.`];
                const pageIniLines = [
                  "type=menu",
                  `title=סדר ${seder.name} - דף ${pageIdx + 1}`
                ];
      
                pageMasechtot.forEach((m, idx) => {
                  const digit = (idx + 1).toString();
                  const masechetPath = formatPath(pagePath, digit);
                  pageTtsParts.push(`למסכת ${m.name} הקישו ${digit}`);
                  pageIniLines.push(`${digit}=${masechetPath}`);
      
                  // 3. יצירת שלוחת המסכת (השמעת קבצים)
                  const masechetIni = buildPlayfileIni(m.name, seder.name, m.pages);
                  const masechetTts = buildMasechetTts(m.name, m.pages);
      
                  plan.push({
                    path: masechetPath,
                    title: `מסכת ${m.name}`,
                    type: 'playfile',
                    description: `שלוחת השמעת קבצים - מסכת ${m.name} (${m.pages} דפים)`,
                    ini: masechetIni,
                    tts: masechetTts,
                    ttsFileName: 'M0000.TTS',
                    sederName: seder.name,
                    masechetName: m.name,
                    pages: m.pages
                  });
      
                  State.filesMap.set(formatPath(masechetPath, 'ext.ini'), {
                    title: `הגדרות שלוחה: מסכת ${m.name}`,
                    content: masechetIni
                  });
                  State.filesMap.set(formatPath(masechetPath, 'M0000.TTS'), {
                    title: `הקראת פתיחה: מסכת ${m.name}`,
                    content: masechetTts
                  });
                });
      
                // טיפול במקש 0:
                if (hasNextPage) {
                  const nextPagePath = formatPath(pagePath, '0');
                  pageTtsParts.push(`למסכתות נוספות בסדר ${seder.name} הקישו 0`);
                  pageIniLines.push(`0=${nextPagePath}`);
                } else {
                  // זהו הדף האחרון בסדר זה
                  // שלוחה 0 תודיע שזו השלוחה האחרונה בסדר
                  const lastZeroPath = formatPath(pagePath, '0');
                  const endSederTts = `זוהי השלוחה האחרונה לסדר ${seder.name}.`;
                  const endSederIni = [
                    "type=playfile",
                    `title=סיום סדר ${seder.name}`,
                    "playfile_end_goto=.."
                  ].join("\n");
      
                  pageTtsParts.push(`למסכתות נוספות בסדר ${seder.name} הקישו 0`);
                  pageIniLines.push(`0=${lastZeroPath}`);
      
                  plan.push({
                    path: lastZeroPath,
                    title: `סיום סדר ${seder.name}`,
                    type: 'playfile',
                    description: `שלוחה 0 - סיום סדר ${seder.name}`,
                    ini: endSederIni,
                    tts: endSederTts,
                    ttsFileName: 'M0000.TTS',
                    isLastZero: true
                  });
      
                  State.filesMap.set(formatPath(lastZeroPath, 'ext.ini'), {
                    title: `הגדרות שלוחה 0 (סוף סדר ${seder.name})`,
                    content: endSederIni
                  });
                  State.filesMap.set(formatPath(lastZeroPath, 'M0000.TTS'), {
                    title: `הקראת סוף סדר ${seder.name}`,
                    content: endSederTts
                  });
                }
      
                const pageTts = pageTtsParts.join(", ") + ".";
                const pageIni = pageIniLines.join("\n");
      
                plan.push({
                  path: pagePath,
                  title: `סדר ${seder.name} - תפריט ${pageIdx + 1}`,
                  type: 'menu',
                  description: `תפריט מסכתות ${pageIdx * CHUNK_SIZE + 1} עד ${Math.min((pageIdx + 1) * CHUNK_SIZE, masechtot.length)}`,
                  ini: pageIni,
                  tts: pageTts,
                  ttsFileName: 'M0000.TTS',
                  sederName: seder.name,
                  pageIdx: pageIdx + 1
                });
      
                State.filesMap.set(formatPath(pagePath, 'ext.ini'), {
                  title: `הגדרות תפריט: סדר ${seder.name} (חלק ${pageIdx + 1})`,
                  content: pageIni
                });
                State.filesMap.set(formatPath(pagePath, 'M0000.TTS'), {
                  title: `הקראת תפריט: סדר ${seder.name} (חלק ${pageIdx + 1})`,
                  content: pageTts
                });
              }
            });
      
            State.generatedPlan = plan;
            return plan;
          }
      
          function buildPlayfileIni(masechetName, sederName, pages) {
            const order = document.getElementById('settingOrder')?.value || 'asc';
            const autoPlay = document.getElementById('settingAutoPlay')?.value || 'yes';
            const remember = document.getElementById('settingRemember')?.value || 'yes';
            const extra = document.getElementById('settingExtraIni')?.value.trim() || '';
      
            const lines = [
              "type=playfile",
              `title=מסכת ${masechetName} - סדר ${sederName} (${pages} דפים)`,
              `playfile_order=${order}`,
              `playfile_auto_play=${autoPlay}`,
              `playfile_remember_location=${remember}`
            ];
      
            if (extra) {
              lines.push(extra);
            }
            return lines.join("\n");
          }
      
          function buildMasechetTts(masechetName, pages) {
            const template = document.getElementById('massechetTtsTemplate')?.value || 'מסכת {name}, {pages} דפים';
            return template.replace('{name}', masechetName).replace('{pages}', pages);
          }
      
          const YemotAPI = {
            getBaseUrl() {
              return "https://www.call2all.co.il/ym/api/";
            },
      
            wrapUrl(endpoint, params = {}) {
              const token = State.apiToken.trim();
              const fullParams = new URLSearchParams({ token, ...params });
              const targetUrl = `${this.getBaseUrl()}${endpoint}?${fullParams.toString()}`;
      
              const proxyMode = document.getElementById('corsProxySelect').value;
              if (proxyMode === 'corsproxy') {
                return `https://corsproxy.io/?${encodeURIComponent(targetUrl)}`;
              } else if (proxyMode === 'allorigins') {
                return `https://api.allorigins.win/raw?url=${encodeURIComponent(targetUrl)}`;
              } else if (proxyMode === 'custom') {
                const custom = document.getElementById('customProxyInput').value.trim();
                return `${custom}${encodeURIComponent(targetUrl)}`;
              }
              return targetUrl;
            },
      
            async getSession() {
              const url = this.wrapUrl('GetSession');
              log('API', `בודק חיבור עם GetSession...`);
              const res = await fetch(url);
              if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
              return await res.json();
            },
      
            async updateExtension(path, iniObject) {
              // UpdateExtension?token=...&path=ivr2:1&type=menu...
              const formattedPath = path.startsWith('ivr2:') ? path : `ivr2:${path === '/' ? '' : path}`;
              const params = {
                path: formattedPath,
                ...iniObject
              };
              const url = this.wrapUrl('UpdateExtension', params);
              const res = await fetch(url);
              if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
              return await res.json();
            },
      
            async uploadTextFile(fullIvrPath, contents) {
              // UploadTextFile?token=...&what=ivr2:1/ext.ini&contents=...
              const what = fullIvrPath.startsWith('ivr2:') ? fullIvrPath : `ivr2:${fullIvrPath}`;
              const url = this.wrapUrl('UploadTextFile');
              
              // POST עם FormData כדי למנוע חיתוך טקסט עברי ארוך ב-URL
              const formData = new FormData();
              formData.append('token', State.apiToken.trim());
              formData.append('what', what);
              formData.append('contents', contents);
      
              const res = await fetch(url, {
                method: 'POST',
                body: formData
              });
      
              if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
              return await res.json();
            }
          };
      
          function log(type, msg, data = null) {
            const logsConsole = document.getElementById('logsConsole');
            const time = new Date().toLocaleTimeString('he-IL');
            const el = document.createElement('div');
            
            let badgeColor = 'bg-slate-700 text-slate-300';
            if (type === 'SUCCESS') badgeColor = 'bg-emerald-900/80 text-emerald-300 border border-emerald-700';
            if (type === 'ERROR') badgeColor = 'bg-rose-900/80 text-rose-300 border border-rose-700';
            if (type === 'WARN') badgeColor = 'bg-amber-900/80 text-amber-300 border border-amber-700';
            if (type === 'API') badgeColor = 'bg-blue-900/80 text-blue-300 border border-blue-700';
      
            el.innerHTML = `
              <span class="text-slate-500">[${time}]</span>
              <span class="px-1.5 py-0.5 rounded text-[10px] font-bold ${badgeColor}">${type}</span>
              <span class="text-slate-200">${escapeHtml(msg)}</span>
              ${data ? `<pre class="text-[10px] text-slate-400 mt-0.5 pl-4 overflow-x-auto">${escapeHtml(typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data))}</pre>` : ''}
            `;
            logsConsole.appendChild(el);
            logsConsole.scrollTop = logsConsole.scrollHeight;
          }
      
          function escapeHtml(str) {
            if (!str) return '';
            return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
          }
      
          function renderTree() {
            const container = document.getElementById('treeContainer');
            container.innerHTML = '';
      
            const plan = State.generatedPlan;
            document.getElementById('statTotalExtensions').textContent = plan.length;
      
            // קיבוץ לפי סדרים
            SEDER_DATA.forEach(seder => {
              const sederItems = plan.filter(p => p.sederName === seder.name || (p.path.includes(`/${seder.key}`) || p.path === `${State.basePath}/${seder.key}`));
              
              const card = document.createElement('div');
              card.className = "border border-slate-200 rounded-xl bg-white overflow-hidden shadow-sm";
              
              const header = document.createElement('div');
              header.className = "bg-slate-50 px-4 py-3 flex items-center justify-between cursor-pointer hover:bg-slate-100 transition-colors border-b border-slate-100";
              header.innerHTML = `
                <div class="flex items-center gap-2">
                  <span class="text-lg">${seder.icon}</span>
                  <span class="font-bold text-slate-800 text-sm font-sans">${seder.title}</span>
                  <span class="bg-indigo-100 text-indigo-800 px-2 py-0.5 rounded-full text-[10px] font-mono font-bold">${seder.masechtot.length} מסכתות</span>
                </div>
                <div class="flex items-center gap-3">
                  <span class="text-slate-400 text-xs font-mono">מקש ${seder.key}</span>
                  <span class="transform transition-transform text-slate-400 text-xs" id="chevron-${seder.id}">▼</span>
                </div>
              `;
      
              const content = document.createElement('div');
              content.className = "p-4 space-y-3 bg-white";
              content.id = `seder-content-${seder.id}`;
      
              // מסכתות הסדר הזה
              const masechtotInSeder = plan.filter(p => p.sederName === seder.name && p.type === 'playfile' && !p.isLastZero);
              const menusInSeder = plan.filter(p => p.sederName === seder.name && p.type === 'menu');
              const lastZero = plan.find(p => p.sederName === seder.name && p.isLastZero);
      
              let html = '';
      
              // דפי תפריט
              html += `<div class="mb-3">
                <div class="text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-1.5 font-sans">תפריטי ניווט ודפי מקש 0:</div>
                <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">`;
              
              menusInSeder.forEach(m => {
                html += `
                  <div class="bg-indigo-50/60 border border-indigo-100 rounded-lg p-2.5">
                    <div class="flex items-center justify-between font-bold text-indigo-900">
                      <span>📁 שלוחה ${m.path}</span>
                      <span class="text-[10px] bg-indigo-200/70 text-indigo-800 px-1.5 py-0.2 rounded font-sans">תפריט דף ${m.pageIdx || 1}</span>
                    </div>
                    <div class="text-[11px] text-slate-600 font-sans mt-1"><strong>TTS:</strong> "${m.tts}"</div>
                  </div>`;
              });
      
              if (lastZero) {
                html += `
                  <div class="bg-amber-50/70 border border-amber-200 rounded-lg p-2.5">
                    <div class="flex items-center justify-between font-bold text-amber-900">
                      <span>🔔 שלוחה 0 (סוף סדר): ${lastZero.path}</span>
                      <span class="text-[10px] bg-amber-200 text-amber-800 px-1.5 py-0.2 rounded font-sans">הודעת סיום</span>
                    </div>
                    <div class="text-[11px] text-slate-700 font-sans mt-1"><strong>הודעה:</strong> "${lastZero.tts}"</div>
                  </div>`;
              }
              html += `</div></div>`;
      
              // רשימת המסכתות
              html += `<div>
                <div class="text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-1.5 font-sans">שלוחות השמעת קבצים (מסכתות):</div>
                <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2">`;
      
              masechtotInSeder.forEach(m => {
                html += `
                  <div class="border border-slate-200 rounded-lg p-2.5 bg-slate-50 hover:bg-slate-100/80 transition-colors">
                    <div class="flex justify-between items-start">
                      <span class="font-bold text-slate-800 font-sans text-xs">מסכת ${m.masechetName}</span>
                      <span class="bg-emerald-100 text-emerald-800 text-[10px] px-1.5 py-0.5 rounded font-mono font-bold">${m.pages} דף</span>
                    </div>
                    <div class="text-[11px] text-slate-500 font-mono mt-1">נתיב: ${m.path}</div>
                    <div class="text-[10px] text-slate-400 font-sans mt-0.5 truncate">TTS: ${m.tts}</div>
                  </div>`;
              });
              html += `</div></div>`;
      
              content.innerHTML = html;
              header.addEventListener('click', () => {
                content.classList.toggle('hidden');
                const chevron = document.getElementById(`chevron-${seder.id}`);
                chevron.textContent = content.classList.contains('hidden') ? '◀' : '▼';
              });
      
              card.appendChild(header);
              card.appendChild(content);
              container.appendChild(card);
            });
      
            renderPayloadsList();
          }
      
          function renderPayloadsList() {
            const list = document.getElementById('payloadList');
            list.innerHTML = '';
      
            let first = true;
            State.filesMap.forEach((fileObj, path) => {
              const item = document.createElement('div');
              item.className = "px-2.5 py-2 rounded-lg cursor-pointer text-xs font-mono truncate hover:bg-slate-200/70 border border-transparent transition-colors flex items-center justify-between";
              
              const isIni = path.endsWith('.ini');
              const icon = isIni ? '⚙️' : '🔊';
              item.innerHTML = `
                <span class="truncate" title="${path}">${icon} ${path}</span>
                <span class="text-[10px] text-slate-400 font-sans">${isIni ? 'ini' : 'TTS'}</span>
              `;
      
              item.addEventListener('click', () => {
                document.querySelectorAll('#payloadList > div').forEach(el => el.classList.remove('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold'));
                item.classList.add('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold');
                showPayload(path, fileObj);
              });
      
              list.appendChild(item);
      
              if (first) {
                item.classList.add('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold');
                showPayload(path, fileObj);
                first = false;
              }
            });
          }
      
          function showPayload(path, fileObj) {
            document.getElementById('payloadFileTitle').textContent = fileObj.title || path;
            document.getElementById('payloadFilePath').textContent = `ivr2:${path}`;
            document.getElementById('payloadFileContent').textContent = fileObj.content;
          }
      
          const Simulator = {
            currentPlanNode: null,
      
            init() {
              this.goToRoot();
              this.setupKeypad();
            },
      
            goToRoot() {
              const rootPath = (State.basePath.trim() === '' || State.basePath.trim() === '/') ? '' : State.basePath.trim().replace(/^\/+|\/+$/g, '');
              const target = rootPath === '' ? '/' : rootPath;
              this.navigateTo(target);
            },
      
            navigateTo(path) {
              const plan = State.generatedPlan;
              let node = plan.find(p => p.path === path);
              if (!node && path === '/') {
                node = plan.find(p => p.path === '/' || p.path === '');
              }
      
              if (!node) {
                this.setScreen(path, "השלוחה אינה קיימת במערכת", "שגיאה");
                return;
              }
      
              this.currentPlanNode = node;
              State.simState.currentPath = node.path;
              
              let displayPath = node.path === '' ? '/' : `/${node.path}`;
              let speech = node.tts;
              if (node.type === 'playfile') {
                speech = `שלוחת השמעת קבצים: ${node.title}. (${node.tts}). מתחיל השמעת שיעורים...`;
              }
      
              this.setScreen(displayPath, speech, `סוג: ${node.type} | ${node.title}`);
            },
      
            setScreen(path, text, type) {
              document.getElementById('simCurrentPath').textContent = `שלוחה: ${path}`;
              document.getElementById('simVoiceText').textContent = text;
              document.getElementById('simExtType').textContent = type;
            },
      
            pressKey(key) {
              if (!this.currentPlanNode) return;
              
              // כפתור כוכבית לחזרה אחורה
              if (key === '*') {
                const parts = this.currentPlanNode.path.split('/').filter(Boolean);
                if (parts.length > 0) {
                  parts.pop();
                  const parent = parts.join('/') || '/';
                  this.navigateTo(parent);
                } else {
                  this.navigateTo('/');
                }
                return;
              }
      
              // אם זו שלוחת תפריט
              if (this.currentPlanNode.type === 'menu') {
                const ini = this.currentPlanNode.ini;
                const match = ini.split('\n').find(line => line.startsWith(`${key}=`));
                if (match) {
                  const targetPath = match.split('=')[1].trim();
                  this.navigateTo(targetPath);
                } else {
                  this.setScreen(this.currentPlanNode.path, `הקשה ${key} שגויה. ${this.currentPlanNode.tts}`, `סוג: menu`);
                }
              } else if (this.currentPlanNode.type === 'playfile') {
                if (key === '0') {
                  this.setScreen(this.currentPlanNode.path, `בשלוחת השמעת קבצים לא הוגדר מעבר על מקש 0.`, `סוג: playfile`);
                }
              }
            },
      
            setupKeypad() {
              document.querySelectorAll('.keypad-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                  const key = btn.getAttribute('data-key');
                  this.pressKey(key);
                });
              });
      
              document.getElementById('simResetBtn').addEventListener('click', () => {
                this.goToRoot();
              });
      
              // כפתור דיבור TTS של הדפדפן לבדיקת שמיעה
              document.getElementById('simSpeakAudioBtn').addEventListener('click', () => {
                const text = document.getElementById('simVoiceText').textContent;
                if ('speechSynthesis' in window) {
                  window.speechSynthesis.cancel();
                  const utterance = new SpeechSynthesisUtterance(text);
                  utterance.lang = 'he-IL';
                  utterance.rate = 0.95;
                  window.speechSynthesis.speak(utterance);
                } else {
                  alert('הדפדפן אינו תומך בהקראת קולית מובנית');
                }
              });
            }
          };
      
          async function startBuildingExtensions() {
            const token = document.getElementById('apiTokenInput').value.trim();
            if (!token) {
              alert('אנא הזן טוקן API של ימות המשיח כדי לבצע את ההקמה');
              document.getElementById('apiTokenInput').focus();
              return;
            }
            State.apiToken = token;
            State.basePath = document.getElementById('basePathInput').value.trim();
      
            // רענון התוכנית
            generateFullIvrPlan();
            const plan = State.generatedPlan;
      
            if (!confirm(`האם להתחיל בהקמת ${plan.length} שלוחות ש"ס וקבצי TTS במערכת ימות המשיח שלך?`)) {
              return;
            }
      
            const progressContainer = document.getElementById('buildProgressContainer');
            const progressBar = document.getElementById('progressBar');
            const progressText = document.getElementById('progressText');
            const progressPercent = document.getElementById('progressPercent');
            const startBtn = document.getElementById('startBuildBtn');
      
            progressContainer.classList.remove('hidden');
            startBtn.disabled = true;
            startBtn.classList.add('opacity-50', 'cursor-not-allowed');
      
            log('INFO', `מתחיל תהליך הקמה מלא של ${plan.length} שלוחות ש"ס...`);
      
            let completed = 0;
            let errors = 0;
      
            for (let i = 0; i < plan.length; i++) {
              const item = plan[i];
              const percent = Math.round(((i + 1) / plan.length) * 100);
              progressBar.style.width = `${percent}%`;
              progressPercent.textContent = `${percent}%`;
              progressText.textContent = `יוצר: ${item.title} (${item.path || '/'})`;
      
              try {
                // שלב 1: העלאת קובץ ext.ini באמצעות UploadTextFile
                const iniPath = item.path === '/' || item.path === '' ? 'ext.ini' : `${item.path}/ext.ini`;
                log('API', `מעלה הגדרות: ${iniPath}`);
                await YemotAPI.uploadTextFile(iniPath, item.ini);
      
                // שלב 2: העלאת קובץ M0000.TTS עם טקסט ההקראה האוטומטי
                const ttsPath = item.path === '/' || item.path === '' ? item.ttsFileName : `${item.path}/${item.ttsFileName}`;
                log('API', `מעלה קובץ הקראה: ${ttsPath}`);
                await YemotAPI.uploadTextFile(ttsPath, item.tts);
      
                completed++;
                log('SUCCESS', `הושלמה שלוחה: ${item.title} [${item.path}]`);
                
                // השהייה קלה למניעת הצפת ה-API
                await new Promise(r => setTimeout(r, 200));
      
              } catch (err) {
                errors++;
                log('ERROR', `נכשלה הקמת שלוחה ${item.path}: ${err.message}`, err);
                // ממשיך לשלוחה הבאה
              }
            }
      
            progressBar.style.width = '100%';
            progressPercent.textContent = '100%';
            progressText.textContent = 'ההקמה הסתיימה!';
            startBtn.disabled = false;
            startBtn.classList.remove('opacity-50', 'cursor-not-allowed');
      
            if (errors === 0) {
              log('SUCCESS', `כל ${completed} השלוחות הוקמו בהצלחה מלאה!`);
              alert(`ההקמה הסתיימה בהצלחה!\nנוצרו ${completed} שלוחות, תפריטי ניווט וקובצי TTS.`);
            } else {
              log('WARN', `ההקמה הסתיימה עם ${errors} שגיאות מתוך ${plan.length} שלוחות.`);
              alert(`ההקמה הסתיימה.\nהצליחו: ${completed}\nנכשלו: ${errors}\nבדוק את לשונית היומן לפרטים.`);
            }
          }
      
          function initUI() {
            // Tab switching
            const tabs = [
              { btn: 'tabTreeBtn', content: 'tabTreeContent' },
              { btn: 'tabSimulatorBtn', content: 'tabSimulatorContent' },
              { btn: 'tabPayloadBtn', content: 'tabPayloadContent' },
              { btn: 'tabLogsBtn', content: 'tabLogsContent' }
            ];
      
            tabs.forEach(t => {
              document.getElementById(t.btn).addEventListener('click', () => {
                tabs.forEach(other => {
                  const b = document.getElementById(other.btn);
                  const c = document.getElementById(other.content);
                  if (other.btn === t.btn) {
                    b.className = "tab-btn px-4 py-2 rounded-lg bg-white text-indigo-700 shadow-sm border border-slate-200/80 font-bold";
                    c.classList.remove('hidden');
                  } else {
                    b.className = "tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50";
                    c.classList.add('hidden');
                  }
                });
              });
            });
      
            // Expand / Collapse all
            document.getElementById('expandAllTreeBtn').addEventListener('click', () => {
              SEDER_DATA.forEach(s => {
                document.getElementById(`seder-content-${s.id}`).classList.remove('hidden');
                document.getElementById(`chevron-${s.id}`).textContent = '▼';
              });
            });
      
            document.getElementById('collapseAllTreeBtn').addEventListener('click', () => {
              SEDER_DATA.forEach(s => {
                document.getElementById(`seder-content-${s.id}`).classList.add('hidden');
                document.getElementById(`chevron-${s.id}`).textContent = '◀';
              });
            });
      
            // Proxy custom input toggle
            document.getElementById('corsProxySelect').addEventListener('change', (e) => {
              const customInput = document.getElementById('customProxyInput');
              if (e.target.value === 'custom') {
                customInput.classList.remove('hidden');
              } else {
                customInput.classList.add('hidden');
              }
            });
      
            // Inputs change -> regenerate
            ['basePathInput', 'settingOrder', 'settingAutoPlay', 'settingRemember', 'settingExtraIni', 'massechetTtsTemplate'].forEach(id => {
              const el = document.getElementById(id);
              if (el) {
                el.addEventListener('input', () => {
                  State.basePath = document.getElementById('basePathInput').value.trim();
                  generateFullIvrPlan();
                  renderTree();
                  Simulator.goToRoot();
                });
              }
            });
      
            // Test Connection Button
            document.getElementById('testConnectionBtn').addEventListener('click', async () => {
              const token = document.getElementById('apiTokenInput').value.trim();
              const msgEl = document.getElementById('connectionMsg');
              const badge = document.getElementById('apiStatusBadge');
      
              if (!token) {
                msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                msgEl.textContent = "אנא הזן טוקן API";
                msgEl.classList.remove('hidden');
                return;
              }
      
              State.apiToken = token;
              msgEl.className = "text-xs p-2.5 rounded-lg border bg-slate-100 border-slate-300 text-slate-700";
              msgEl.textContent = "בודק חיבור מול ימות המשיח...";
              msgEl.classList.remove('hidden');
      
              try {
                const res = await YemotAPI.getSession();
                if (res.responseStatus === 'OK') {
                  msgEl.className = "text-xs p-2.5 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800";
                  msgEl.innerHTML = `<strong>חיבור תקין!</strong> מערכת: ${res.username || ''} | שם: ${res.name || 'ללא'} | יחידות: ${res.units || 0}`;
                  badge.className = "inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-emerald-950 text-emerald-300 border border-emerald-700";
                  badge.innerHTML = `<span class="w-2 h-2 rounded-full bg-emerald-400"></span> מחובר למערכת`;
                  log('SUCCESS', 'התחברות הצליחה מול שרת ימות המשיח', res);
                } else {
                  msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                  msgEl.textContent = `שגיאה מהשרת: ${res.message || res.responseStatus}`;
                  log('ERROR', 'שגיאת התחברות', res);
                }
              } catch (err) {
                msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                msgEl.innerHTML = `שגיאת רשת / CORS: ${err.message}.<br><span class="text-[11px]">אם אתה משתמש ישירות בדפדפן, מומלץ לבחור באפשרות 'CORS Proxy' או להריץ מתוך שרת מקומי.</span>`;
                log('ERROR', 'שגיאת רשת בבדיקת חיבור', err.message);
              }
            });
      
            // Clear logs
            document.getElementById('clearLogsBtn').addEventListener('click', () => {
              document.getElementById('logsConsole').innerHTML = '';
            });
      
            // Start Build
            document.getElementById('startBuildBtn').addEventListener('click', startBuildingExtensions);
      
            // Download backup JSON
            document.getElementById('downloadZipBtn').addEventListener('click', () => {
              const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(State.generatedPlan, null, 2));
              const dlAnchor = document.createElement('a');
              dlAnchor.setAttribute("href", dataStr);
              dlAnchor.setAttribute("download", `yemot_shas_plan_${Date.now()}.json`);
              document.body.appendChild(dlAnchor);
              dlAnchor.click();
              dlAnchor.remove();
              log('INFO', 'קובץ תוכנית השלוחות הורד למחשב המשתמש');
            });
      
            // View Session Details Modal / Toast
            document.getElementById('viewSessionBtn').addEventListener('click', async () => {
              const token = document.getElementById('apiTokenInput').value.trim();
              if (!token) return alert('נא להזין טוקן תחילה');
              try {
                const res = await YemotAPI.getSession();
                alert(`פרטי מערכת:\nמספר: ${res.username || 'לא זמין'}\nשם: ${res.name || 'לא זמין'}\nיתרת יחידות: ${res.units || 0}\nתוקף יחידות: ${res.unitsExpireDate || 'ללא תפוגה'}`);
              } catch (e) {
                alert(`שגיאה בקבלת פרטים: ${e.message}`);
              }
            });
      
            // Initial generation & render
            generateFullIvrPlan();
            renderTree();
            Simulator.init();
            log('INFO', 'מערכת בניית שלוחות ש"ס מוכנה לפעולה');
          }
      
          // Startup
          window.addEventListener('DOMContentLoaded', initUI);
        </script>
      </body>
      </html>```
      פורסם בהסברים מסודרים ממשתמשים
      ל
      לומד''עס
    • RE: בעיה במיספור במערכת

      @33064325 אמר בבעיה במיספור במערכת:

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

      כן צודק זה עבד ב 100% תודה רבה חסכתי מיספור דרך תוכנות שונות

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס

    פוסטים אחרונים שנוצרו על ידי לומד''עס

    • כלי HTML ליצירה אוטומטית של שלוחות הדף היומי (כל מסכתות הש״ס לפי סדרים) באמצעות ה־API

      שלום לכולם,

      בניתי כלי קטן בקובץ HTML באמצעות ה AI אחד שיוצר אוטומטית, דרך ה־API של ימות המשיח, מבנה שלוחות מלא לכל מסכתות הדף היומי, כולל קבצי הקראה (TTS). אין צורך בהתקנה: פותחים את הקובץ בדפדפן, מדביקים API Token ולוחצים.

      מה הכלי יוצר:

      שישה סדרים: זרעים, מועד, נשים, נזיקין, קדשים, טהרות
      כל סדר הוא שלוחת תפריט עם M0000.tts אוטומטי ("למסכת ביצה הקישו 1…")
      כל מסכת היא שלוחת השמעת קבצים (שלוחות 1–9), עם שם השלוחה ואפשרות ל־M0000.tts משלה
      בשלוחה 0 יש תפריט עם שאר המסכתות באותו סדר. בתפריט האחרון מוקרא "זוהי השלוחה האחרונה בסדר X"
      תפריט ראשי עם בחירת הסדר

      אפשרויות הגדרה:

      סדר המסכתות בתוך כל סדר: לפי סדר הש״ס, לפי א־ב או לפי מספר הדפים
      כמה מסכתות בכל תפריט (עד 9)
      בחירה אילו סדרים ליצור
      עריכה מלאה של נוסח ההקראה
      הגדרות ברירת מחדל (key=value) שנשלחות אוטומטית לכל שלוחות ההשמעה
      תצוגה מקדימה של כל השלוחות והקבצים לפני היצירה, התקדמות בזמן אמת, ו"נסה שוב" לפעולות שנכשלו

      איך זה עובד טכנית:

      ההתחברות היא באמצעות API KEY (טוקן קבוע מממשק חומת האש). הכלי משתמש בפקודות UpdateExtension ליצירה ועדכון שלוחות, ב־UploadTextFile להעלאת קבצי TTS, וב־GetSession לבדיקת חיבור. UpdateExtension מעדכן רק את מה שנשלח, ולכן לא נמחק תוכן קיים בשלוחות. קבצי M0000.tts קיימים באותו נתיב יידרסו.

      חשוב לדעת:

      הטוקן נשאר בדפדפן ולא נשמר ולא נשלח לשום מקום מלבד שרת ימות המשיח.
      אם הדפדפן חוסם את הקריאות (CORS), אפשר לעבור לשליחת הטוקן בכותרת authorization. אפשר גם להמיר את זה לסקריפט שרץ בשרת.
      סוג השלוחה ברירת המחדל הוא playfile. שמות הגדרות נוספות כדאי להעתיק מ־ext.ini של שלוחה שהגדרתם ידנית.
      מומלץ להריץ קודם על שלוחת ניסיון.

      הקובץ מצורף. אשמח לשמוע הערות והצעות לשיפור.

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>בונה שלוחות דף יומי – ימות המשיח</title>
      <link rel="preconnect" href="https://fonts.googleapis.com">
      <link href="https://fonts.googleapis.com/css2?family=Frank+Ruhl+Libre:wght@700;900&family=Heebo:wght@400;500;700&display=swap" rel="stylesheet">
      <style>
      :root{
        --bg:#f3f5f7; --card:#ffffff; --ink:#1b2a38; --muted:#5c6b79; --line:#d5dce3;
        --accent:#0e6a6a; --accent-ink:#ffffff; --warn:#a15c00; --bad:#b42318; --ok:#12733f; --code:#eef2f5;
      }
      @media (prefers-color-scheme:dark){
        :root{--bg:#10171e; --card:#18222c; --ink:#e6ecf1; --muted:#97a6b4; --line:#2b3a47;
              --accent:#4fc3c3; --accent-ink:#06201f; --warn:#f0b35a; --bad:#ff8a7a; --ok:#5fd18c; --code:#0f1720;}
      }
      *{box-sizing:border-box}
      body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.6 Heebo,system-ui,Arial,sans-serif}
      header{padding:28px 20px 8px;max-width:980px;margin:0 auto}
      h1{font:900 34px/1.2 "Frank Ruhl Libre",serif;margin:0 0 6px}
      header p{margin:0;color:var(--muted)}
      main{max-width:980px;margin:0 auto;padding:12px 20px 60px;display:grid;gap:16px}
      section{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:18px 20px}
      h2{font-size:18px;margin:0 0 12px}
      .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:12px 16px}
      label{display:block;font-size:14px;color:var(--muted);margin-bottom:4px}
      input[type=text],input[type=password],input[type=number],select,textarea{
        width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--ink);font:inherit}
      textarea{min-height:70px;resize:vertical}
      input[dir=ltr],textarea[dir=ltr]{text-align:left}
      input:focus-visible,select:focus-visible,textarea:focus-visible,button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
      .chk{display:flex;gap:8px;align-items:center;color:var(--ink);font-size:15px;margin:0}
      .chk input{width:auto}
      .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
      .sed{display:flex;gap:6px 18px;flex-wrap:wrap}
      button{font:inherit;font-weight:500;padding:9px 18px;border-radius:6px;border:1px solid var(--line);background:var(--card);color:var(--ink);cursor:pointer}
      button.primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
      button:disabled{opacity:.5;cursor:not-allowed}
      .hint{font-size:13px;color:var(--muted);margin-top:6px}
      .status{font-size:14px;margin-inline-start:6px}
      .ok{color:var(--ok)} .bad{color:var(--bad)} .warn{color:var(--warn)}
      table{width:100%;border-collapse:collapse;font-size:14px}
      th,td{padding:6px 8px;border-bottom:1px solid var(--line);text-align:start;vertical-align:top}
      th{color:var(--muted);font-weight:500}
      td.path{direction:ltr;text-align:left;font-family:ui-monospace,Consolas,monospace;font-size:13px;white-space:nowrap}
      .tts{color:var(--muted);font-size:13px}
      .wrap{max-height:420px;overflow:auto;border:1px solid var(--line);border-radius:6px}
      progress{width:100%;height:14px;accent-color:var(--accent)}
      #log{background:var(--code);border-radius:6px;padding:10px;max-height:260px;overflow:auto;font:13px/1.5 ui-monospace,Consolas,monospace;direction:ltr;text-align:left;white-space:pre-wrap}
      .pill{display:inline-block;background:var(--code);border-radius:99px;padding:1px 10px;font-size:13px;color:var(--muted)}
      </style>
      </head>
      <body>
      <header>
        <h1>בונה שלוחות הש״ס</h1>
        <p>יוצר אוטומטית שלוחות השמעת קבצים לכל המסכתות, מחולקות לפי סדרים, עם קבצי הקראה (TTS) – דרך ה־API של ימות המשיח.</p>
      </header>
      <main>
      
      <section>
        <h2>חיבור למערכת</h2>
        <div class="grid">
          <div><label for="token">API Token (מפתח קבוע מחומת האש)</label>
            <input id="token" type="password" dir="ltr" autocomplete="off" placeholder="הדבק כאן את הטוקן"></div>
          <div><label for="base">שלוחת הבסיס (למשל 7, או ריק לשלוחה הראשית)</label>
            <input id="base" type="text" dir="ltr" value="" placeholder="ריק = שלוחה ראשית"></div>
          <div><label for="server">שרת</label>
            <select id="server">
              <option value="https://www.call2all.co.il/ym/api/">www.call2all.co.il</option>
              <option value="https://private.call2all.co.il/ym/api/">private.call2all.co.il</option>
            </select></div>
          <div><label for="auth">אופן שליחת הטוקן</label>
            <select id="auth">
              <option value="param">פרמטר token (תואם דפדפן)</option>
              <option value="header">כותרת authorization (מומלץ בשרת)</option>
            </select></div>
        </div>
        <div class="row" style="margin-top:12px">
          <button id="btnTest" type="button">בדיקת חיבור</button><span id="testStatus" class="status"></span>
        </div>
        <p class="hint">הטוקן נשאר בדפדפן בלבד ולא נשמר. טוקן מסוג שם־משתמש:סיסמה דורש אימות דו־שלבי – מומלץ להשתמש ב־API KEY.</p>
      </section>
      
      <section>
        <h2>מבנה וסדר</h2>
        <div class="grid">
          <div><label for="order">סדר המסכתות בתוך כל סדר</label>
            <select id="order">
              <option value="shas">לפי סדר הש״ס</option>
              <option value="alpha">לפי א־ב</option>
              <option value="pages">לפי מספר הדפים</option>
            </select></div>
          <div><label for="pageSize">מסכתות בכל תפריט (עד 9, השאר ב־0)</label>
            <input id="pageSize" type="number" min="2" max="9" value="9"></div>
        </div>
        <div style="margin-top:12px"><label>סדרים שייווצרו</label><div class="sed" id="sedBox"></div></div>
        <div class="grid" style="margin-top:12px">
          <label class="chk"><input type="checkbox" id="emoji" checked> אימוג׳י בשם שלוחת הסדר</label>
          <label class="chk"><input type="checkbox" id="baseMenu" checked> הגדר את שלוחת הבסיס כתפריט (כשלא ראשית)</label>
          <label class="chk"><input type="checkbox" id="tractIntro" checked> קובץ M0000.tts בכל מסכת</label>
          <label class="chk"><input type="checkbox" id="backLine" checked> הוראת חזרה (כוכבית) בתפריטים</label>
        </div>
        <p class="hint">כל סדר הוא שלוחת תפריט. מסכתות הן שלוחות השמעת קבצים במספרים 1–9. ב־0 נמצא תפריט עם שאר המסכתות של אותו סדר, ובתפריט האחרון נאמר שזו השלוחה האחרונה.</p>
      </section>
      
      <section>
        <h2>סוגי שלוחות והגדרות ברירת מחדל</h2>
        <div class="grid">
          <div><label for="menuType">סוג שלוחת תפריט</label><input id="menuType" type="text" dir="ltr" value="menu"></div>
          <div><label for="playType">סוג שלוחת השמעת קבצים</label><input id="playType" type="text" dir="ltr" value="playfile"></div>
        </div>
        <div class="grid" style="margin-top:12px">
          <div><label for="playExtra">הגדרות נוספות לשלוחות ההשמעה (שורה לכל key=value)</label>
            <textarea id="playExtra" dir="ltr" placeholder="key=value"></textarea></div>
          <div><label for="menuExtra">הגדרות נוספות לשלוחות התפריט</label>
            <textarea id="menuExtra" dir="ltr" placeholder="key=value"></textarea></div>
        </div>
        <p class="hint">ההגדרות נשלחות ל־UpdateExtension, שמעדכן רק את מה שנשלח ולא מוחק את שאר ext.ini. את שמות ההגדרות מעתיקים מקובץ ext.ini של שלוחה קיימת שהגדרת כרצונך.</p>
      </section>
      
      <section>
        <h2>נוסח ההקראה</h2>
        <div class="grid">
          <div><label for="tRootIntro">פתיחת התפריט הראשי</label><input id="tRootIntro" type="text" value="ברוכים הבאים ללימוד הש״ס."></div>
          <div><label for="tRootItem">שורת סדר בתפריט הראשי</label><input id="tRootItem" type="text" value="לסדר {seder} הקישו {n}."></div>
          <div><label for="tSedIntro">פתיחת תפריט סדר</label><input id="tSedIntro" type="text" value="סדר {seder}."></div>
          <div><label for="tSedIntro2">פתיחת תפריט המשך</label><input id="tSedIntro2" type="text" value="המשך סדר {seder}."></div>
          <div><label for="tItem">שורת מסכת</label><input id="tItem" type="text" value="למסכת {name} הקישו {n}."></div>
          <div><label for="tMore">יש עוד מסכתות</label><input id="tMore" type="text" value="לשאר המסכתות בסדר זה הקישו 0."></div>
          <div><label for="tLast">התפריט האחרון בסדר</label><input id="tLast" type="text" value="זוהי השלוחה האחרונה בסדר {seder}."></div>
          <div><label for="tBack">הוראת חזרה</label><input id="tBack" type="text" value="לחזרה לתפריט הקודם הקישו כוכבית."></div>
          <div><label for="tTract">פתיחת מסכת</label><input id="tTract" type="text" value="מסכת {name}."></div>
        </div>
        <p class="hint">משתנים: {seder} {name} {n} {pages}. הקישו 0 מופיע רק כשיש תפריט המשך.</p>
      </section>
      
      <section>
        <h2>תצוגה מקדימה <span class="pill" id="count"></span></h2>
        <div class="wrap"><table><thead><tr><th>נתיב</th><th>שלוחה / קובץ</th></tr></thead><tbody id="preview"></tbody></table></div>
      </section>
      
      <section>
        <h2>יצירה במערכת</h2>
        <div class="row">
          <button id="btnRun" class="primary" type="button">צור שלוחות</button>
          <button id="btnStop" type="button" disabled>עצור</button>
          <button id="btnRetry" type="button" disabled>נסה שוב את הכשלונות</button>
          <span id="runStatus" class="status"></span>
        </div>
        <progress id="prog" value="0" max="1" style="margin-top:12px"></progress>
        <div id="log" style="margin-top:12px" aria-live="polite"></div>
        <p class="hint">קובצי M0000.tts קיימים באותו נתיב יידרסו. שלוחות קיימות יעודכנו בלבד, בלי מחיקת תוכן.</p>
      </section>
      
      </main>
      
      <script>
      /* ---------- נתונים (לפי הקובץ שצירפת) ---------- */
      const SEDARIM=[
        {name:'זרעים',emoji:'🌾'},{name:'מועד',emoji:'📜'},{name:'נשים',emoji:'💍'},
        {name:'נזיקין',emoji:'⚖️'},{name:'קדשים',emoji:'🏛️'},{name:'טהרות',emoji:'💧'}
      ];
      // [שם, אינדקס סדר, מספר דפים, דירוג א־ב לפי הקובץ] – לפי סדר הש״ס
      const RAW=[
       ['ברכות',0,63,6],['שבת',1,156,36],['עירובין',1,104,29],['פסחים',1,120,31],['שקלים',1,21,37],
       ['ראש השנה',1,34,34],['יומא',1,87,13],['סוכה',1,55,26],['ביצה',1,39,4],['תענית',1,30,40],
       ['מגילה',1,31,16],['מועד קטן',1,28,18],['חגיגה',1,26,10],
       ['יבמות',2,121,12],['כתובות',2,111,15],['נדרים',2,90,23],['נזיר',2,65,24],['סוטה',2,48,25],
       ['גיטין',2,89,7],['קידושין',2,81,32],
       ['בבא קמא',3,118,2],['בבא מציעא',3,118,1],['בבא בתרא',3,175,3],['סנהדרין',3,112,27],
       ['מכות',3,23,19],['שבועות',3,48,35],['עבודה זרה',3,75,28],['הוריות',3,13,8],
       ['זבחים',4,119,9],['מנחות',4,109,20],['חולין',4,141,11],['בכורות',4,60,5],['ערכין',4,33,30],
       ['תמורה',4,33,38],['כריתות',4,27,14],['מעילה',4,20,21],['קינים',4,3,33],['מדות',4,4,17],['תמיד',4,9,39],
       ['נדה',5,72,22]
      ];
      const TR=RAW.map((r,i)=>({name:r[0],seder:r[1],pages:r[2],alpha:r[3],shas:i+1}));
      
      /* ---------- עזרים ---------- */
      const $=id=>document.getElementById(id);
      const fill=(t,m)=>t.replace(/\{(\w+)\}/g,(_,k)=>m[k]!==undefined?m[k]:'{'+k+'}');
      const esc=s=>String(s).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
      const chunk=(a,n)=>{const o=[];for(let i=0;i<a.length;i+=n)o.push(a.slice(i,i+n));return o};
      const join=(...a)=>a.filter(x=>x!=='').join('/');
      function sortList(list,mode){
        const l=[...list];
        if(mode==='alpha')l.sort((a,b)=>a.alpha-b.alpha);
        else if(mode==='pages')l.sort((a,b)=>a.pages-b.pages||a.shas-b.shas);
        else l.sort((a,b)=>a.shas-b.shas);
        return l;
      }
      function parseExtra(txt){
        const o={};
        txt.split(/\r?\n/).forEach(line=>{
          const i=line.indexOf('=');
          if(i>0){const k=line.slice(0,i).trim(),v=line.slice(i+1).trim();if(k)o[k]=v}
        });
        return o;
      }
      function normBase(v){
        return v.trim().replace(/^ivr2?:/i,'').replace(/^\/+|\/+$/g,'');
      }
      
      /* ---------- בניית התוכנית ---------- */
      function opts(){
        const seds=[...document.querySelectorAll('#sedBox input:checked')].map(x=>+x.value);
        const g=id=>$(id).value;
        return {
          base:normBase(g('base')), order:g('order'),
          pageSize:Math.min(9,Math.max(2,parseInt(g('pageSize'))||9)),
          seds, emoji:$('emoji').checked, baseMenu:$('baseMenu').checked,
          tractIntro:$('tractIntro').checked, backLine:$('backLine').checked,
          menuType:g('menuType').trim()||'menu', playType:g('playType').trim()||'playfile',
          playExtra:parseExtra(g('playExtra')), menuExtra:parseExtra(g('menuExtra')),
          t:{rootIntro:g('tRootIntro'),rootItem:g('tRootItem'),sedIntro:g('tSedIntro'),sedIntro2:g('tSedIntro2'),
             item:g('tItem'),more:g('tMore'),last:g('tLast'),back:g('tBack'),tract:g('tTract')}
        };
      }
      
      function buildPlan(o){
        const ops=[];
        const baseDepth=o.base?o.base.split('/').length:0;
        const depthOf=p=>(p?p.split('/').length:0)-baseDepth;
        const add=(op)=>{op.depth=depthOf(op.path);ops.push(op)};
      
        if(o.baseMenu&&o.base)
          add({k:'ext',path:o.base,label:'שלוחת בסיס',params:{type:o.menuType,...o.menuExtra}});
      
        const sed=o.seds.map(i=>({i,...SEDARIM[i]}));
        const rootText=[o.t.rootIntro,...sed.map((s,k)=>fill(o.t.rootItem,{seder:s.name,n:k+1}))].filter(Boolean).join(' ');
        add({k:'tts',path:o.base,file:'M0000.tts',text:rootText});
      
        sed.forEach((s,si)=>{
          let p=join(o.base,si+1);
          const list=sortList(TR.filter(t=>t.seder===s.i),o.order);
          const pages=chunk(list,o.pageSize);
          pages.forEach((items,pi)=>{
            const isLast=pi===pages.length-1;
            const title=pi===0?((o.emoji?s.emoji+' ':'')+s.name):('המשך '+s.name);
            add({k:'ext',path:p,label:title,params:{type:o.menuType,title,...o.menuExtra}});
            const parts=[fill(pi===0?o.t.sedIntro:o.t.sedIntro2,{seder:s.name})];
            items.forEach((t,ti)=>parts.push(fill(o.t.item,{name:t.name,n:ti+1,pages:t.pages,seder:s.name})));
            if(!isLast)parts.push(o.t.more);
            else if(pi>0)parts.push(fill(o.t.last,{seder:s.name}));
            if(o.backLine)parts.push(o.t.back);
            add({k:'tts',path:p,file:'M0000.tts',text:parts.filter(Boolean).join(' ')});
            items.forEach((t,ti)=>{
              const tp=join(p,ti+1);
              add({k:'ext',path:tp,label:t.name,params:{type:o.playType,title:t.name,...o.playExtra}});
              if(o.tractIntro)add({k:'tts',path:tp,file:'M0000.tts',text:fill(o.t.tract,{name:t.name,pages:t.pages,seder:s.name})});
            });
            p=join(p,0);
          });
        });
        return ops;
      }
      
      /* ---------- תצוגה מקדימה ---------- */
      function render(){
        const ops=buildPlan(opts());
        $('count').textContent=ops.length+' פעולות';
        $('preview').innerHTML=ops.map(op=>{
          const pad=`style="padding-inline-start:${8+op.depth*18}px"`;
          const path=op.path||'/';
          if(op.k==='ext')
            return `<tr><td class="path">${esc(path)}</td><td ${pad}><b>${esc(op.label)}</b> <span class="tts">(${esc(op.params.type)})</span></td></tr>`;
          return `<tr><td class="path">${esc(path)}/${op.file}</td><td ${pad}><span class="tts">${esc(op.text)}</span></td></tr>`;
        }).join('');
      }
      
      /* ---------- API ---------- */
      async function api(cmd,params,method){
        const server=$('server').value, token=$('token').value.trim();
        const p=new URLSearchParams(params), headers={};
        if($('auth').value==='header')headers.authorization=token; else p.set('token',token);
        let res;
        if(method==='POST'){
          headers['Content-Type']='application/x-www-form-urlencoded;charset=UTF-8';
          res=await fetch(server+cmd,{method:'POST',headers,body:p});
        }else{
          res=await fetch(server+cmd+'?'+p.toString(),{headers});
        }
        return res.json();
      }
      const ivrDir=p=>'ivr2:'+p;
      const ivrFile=(p,f)=>p?`ivr2:${p}/${f}`:`ivr2:/${f}`;
      
      async function doOp(op){
        let j;
        if(op.k==='ext')j=await api('UpdateExtension',{path:ivrDir(op.path),...op.params},'GET');
        else j=await api('UploadTextFile',{what:ivrFile(op.path,op.file),contents:op.text},'POST');
        if(j.responseStatus!=='OK')throw new Error((j.message||j.exceptionMessage||j.responseStatus)+(j.message==='MFA_REQUIRED'?' – השתמש בטוקן מסוג API KEY':''));
      }
      const opName=op=>op.k==='ext'?`ext  ${op.path||'/'}  (${op.label})`:`tts  ${op.path||''}/${op.file}`;
      
      $('btnTest').onclick=async()=>{
        const st=$('testStatus');
        if(!$('token').value.trim()){st.className='status bad';st.textContent='חסר טוקן';return}
        st.className='status';st.textContent='בודק…';
        try{
          const j=await api('GetSession',{},'GET');
          if(j.responseStatus==='OK'){st.className='status ok';st.textContent='מחובר: מערכת '+(j.username||'')+(j.name?' – '+j.name:'')}
          else{st.className='status bad';st.textContent=(j.message||j.responseStatus)}
        }catch(e){st.className='status bad';st.textContent='לא ניתן להתחבר מהדפדפן (ייתכן חסם CORS או שהקובץ נפתח בלי אינטרנט)'}
      };
      
      /* ---------- הרצה ---------- */
      let stopFlag=false, failed=[];
      const log=m=>{const l=$('log');l.textContent+=m+'\n';l.scrollTop=l.scrollHeight};
      
      async function run(ops){
        stopFlag=false;failed=[];
        $('btnRun').disabled=true;$('btnRetry').disabled=true;$('btnStop').disabled=false;
        $('prog').max=ops.length;$('prog').value=0;
        let done=0;
        for(const op of ops){
          if(stopFlag){log('— נעצר ידנית —');break}
          let err=null;
          for(let a=0;a<3;a++){
            try{await doOp(op);err=null;break}catch(e){err=e;await new Promise(r=>setTimeout(r,500*(a+1)))}
          }
          if(err){failed.push(op);log('✗ '+opName(op)+'  → '+err.message)}
          else log('✓ '+opName(op));
          $('prog').value=++done;
          $('runStatus').textContent=`${done} מתוך ${ops.length}`;
        }
        $('btnRun').disabled=false;$('btnStop').disabled=true;$('btnRetry').disabled=failed.length===0;
        $('runStatus').className='status '+(failed.length?'warn':'ok');
        $('runStatus').textContent=failed.length?`הסתיים עם ${failed.length} כשלונות`:'הסתיים בהצלחה';
      }
      
      $('btnRun').onclick=()=>{
        if(!$('token').value.trim()){alert('יש להזין API Token');return}
        const o=opts();
        if(!o.seds.length){alert('יש לבחור לפחות סדר אחד');return}
        const ops=buildPlan(o);
        if(!confirm(`ייווצרו או יעודכנו ${ops.length} פעולות במערכת${o.base?' תחת שלוחה '+o.base:' בשלוחה הראשית'}.\nקבצי M0000.tts קיימים יידרסו. להמשיך?`))return;
        $('log').textContent='';
        run(ops);
      };
      $('btnStop').onclick=()=>{stopFlag=true};
      $('btnRetry').onclick=()=>{if(failed.length){$('log').textContent+='— ניסיון חוזר —\n';run([...failed])}};
      
      /* ---------- אתחול ---------- */
      $('sedBox').innerHTML=SEDARIM.map((s,i)=>`<label class="chk"><input type="checkbox" value="${i}" checked> ${s.emoji} ${s.name}</label>`).join('');
      document.querySelectorAll('input,select,textarea').forEach(el=>{
        if(el.id!=='token')el.addEventListener('input',render);
      });
      render();
      </script>
      </body>
      </html>
      
      

      עוד סוג של קוד

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>בונה שלוחות ש"ס אוטומטי - ימות המשיח</title>
        <script src="https://cdn.tailwindcss.com"></script>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link href="https://fonts.googleapis.com/css2?family=Heebo:wght@300;400;500;600;700;800&family=Frank+Ruhl+Libre:wght@700;800&display=swap" rel="stylesheet">
        <style>
          body {
            font-family: 'Heebo', sans-serif;
          }
          .font-serif-hebrew {
            font-family: 'Frank Ruhl Libre', serif;
          }
          /* Custom scrollbars */
          ::-webkit-scrollbar {
            width: 8px;
            height: 8px;
          }
          ::-webkit-scrollbar-track {
            background: #f1f5f9;
          }
          ::-webkit-scrollbar-thumb {
            background: #cbd5e1;
            border-radius: 4px;
          }
          ::-webkit-scrollbar-thumb:hover {
            background: #94a3b8;
          }
        </style>
      </head>
      <body class="bg-slate-50 text-slate-800 min-h-screen flex flex-col">
      
        <header class="bg-gradient-to-r from-blue-900 via-indigo-900 to-slate-900 text-white shadow-lg sticky top-0 z-40">
          <div class="max-w-7xl mx-auto px-4 py-4 flex flex-col sm:flex-row justify-between items-center gap-3">
            <div class="flex items-center gap-3">
              <div class="w-12 h-12 bg-amber-400/20 border border-amber-300/40 rounded-xl flex items-center justify-center text-2xl shadow-inner">
                📖
              </div>
              <div>
                <h1 class="text-xl sm:text-2xl font-bold font-serif-hebrew tracking-wide text-amber-200">יוצר שלוחות ש"ס למערכות ימות המשיח</h1>
                <p class="text-xs text-slate-300">הקמת תפריטי סדרים ומסכתות, שלוחות השמעת קבצים וקובצי TTS באופן אוטומטי מלא</p>
              </div>
            </div>
            <div class="flex items-center gap-2">
              <span id="apiStatusBadge" class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-slate-800 text-slate-300 border border-slate-700">
                <span class="w-2 h-2 rounded-full bg-slate-500"></span> לא מחובר
              </span>
            </div>
          </div>
        </header>
      
        <main class="max-w-7xl mx-auto px-4 py-6 flex-1 w-full grid grid-cols-1 lg:grid-cols-12 gap-6">
      
          <!-- Left / Control Sidebar (4 cols) -->
          <div class="lg:col-span-4 space-y-6">
            
            <!-- API Connection Box -->
            <section class="bg-white rounded-2xl shadow-sm border border-slate-200 p-5">
              <div class="flex items-center justify-between mb-4 border-b border-slate-100 pb-3">
                <h2 class="text-base font-bold text-slate-800 flex items-center gap-2">
                  <span>🔑</span> פרטי חיבור ל-API
                </h2>
                <span class="text-xs text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded font-mono">Yemot IVR</span>
              </div>
              
              <div class="space-y-4">
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">טוקן API (API Token / Key)</label>
                  <input type="password" id="apiTokenInput" placeholder="הזן טוקן קבוע או זמני..." 
                         class="w-full text-sm px-3 py-2 rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 font-mono">
                  <p class="text-[11px] text-slate-400 mt-1">טוקן קבוע שהונפק במערכת או שילוב מספר:סיסמה</p>
                </div>
      
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">נתיב שלוחת הבסיס (Root Path)</label>
                  <div class="flex items-center">
                    <span class="inline-flex items-center px-3 py-2 rounded-r-lg border border-l-0 border-slate-300 bg-slate-100 text-slate-500 text-sm font-mono dir-ltr">/</span>
                    <input type="text" id="basePathInput" value="1" placeholder="לדוגמה: 1 או 8/1" 
                           class="w-full text-sm px-3 py-2 rounded-l-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 font-mono">
                  </div>
                  <p class="text-[11px] text-slate-400 mt-1">באיזו שלוחה יוקם עץ הש"ס (השאר ריק עבור השלוחה הראשית /)</p>
                </div>
      
                <div>
                  <label class="block text-xs font-semibold text-slate-600 mb-1">פרוקסי CORS (לשליחה ישירה מהדפדפן)</label>
                  <select id="corsProxySelect" class="w-full text-sm px-3 py-2 rounded-lg border border-slate-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-white">
                    <option value="direct">ללא פרוקסי (פנייה ישירה ל-call2all)</option>
                    <option value="corsproxy" selected>CORS Proxy ציבורי (corsproxy.io)</option>
                    <option value="allorigins">AllOrigins Proxy</option>
                    <option value="custom">פרוקסי מותאם אישית...</option>
                  </select>
                  <input type="text" id="customProxyInput" placeholder="https://your-proxy.com/?url=" 
                         class="hidden mt-2 w-full text-xs px-3 py-1.5 rounded-lg border border-slate-300 font-mono">
                </div>
      
                <div class="pt-2 flex gap-2">
                  <button id="testConnectionBtn" type="button" class="flex-1 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-semibold py-2 px-3 rounded-lg border border-slate-300 transition-colors flex items-center justify-center gap-1.5">
                    <span>🔌</span> בדוק חיבור
                  </button>
                  <button id="viewSessionBtn" type="button" class="bg-slate-50 hover:bg-slate-100 text-slate-600 text-xs py-2 px-3 rounded-lg border border-slate-200 transition-colors" title="פרטי מערכת ויחידות">
                    ℹ️ פרטים
                  </button>
                </div>
      
                <div id="connectionMsg" class="hidden text-xs p-2.5 rounded-lg border"></div>
              </div>
            </section>
      
            <section class="bg-white rounded-2xl shadow-sm border border-slate-200 p-5">
              <div class="flex items-center justify-between mb-4 border-b border-slate-100 pb-3">
                <h2 class="text-base font-bold text-slate-800 flex items-center gap-2">
                  <span>⚙️</span> הגדרות שלוחות השמעת קבצים
                </h2>
                <span class="text-xs bg-emerald-50 text-emerald-700 px-2 py-0.5 rounded font-mono font-bold">playfile</span>
              </div>
      
              <p class="text-xs text-slate-500 mb-3">הגדרות שיוטמעו בקובץ <code class="bg-slate-100 px-1 py-0.5 rounded text-indigo-700 font-mono">ext.ini</code> של כל שלוחת מסכת:</p>
      
              <div class="space-y-3 text-xs">
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">סוג שלוחה (type)</label>
                  <input type="text" id="settingType" value="playfile" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-slate-50 font-mono" readonly>
                </div>
      
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">סדר השמעת קבצים (playfile_order)</label>
                  <select id="settingOrder" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                    <option value="asc" selected>מהישן לחדש (סדר עולה 000, 001...)</option>
                    <option value="desc">מהחדש לישן (סדר יורד)</option>
                  </select>
                </div>
      
                <div class="grid grid-cols-2 gap-2">
                  <div>
                    <label class="font-semibold text-slate-700 block mb-1">השמעה אוטומטית</label>
                    <select id="settingAutoPlay" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                      <option value="yes" selected>כן (yes)</option>
                      <option value="no">לא (no)</option>
                    </select>
                  </div>
                  <div>
                    <label class="font-semibold text-slate-700 block mb-1">זכירת מיקום מאזין</label>
                    <select id="settingRemember" class="w-full px-2.5 py-1.5 rounded border border-slate-300 bg-white">
                      <option value="yes" selected>כן (שמירת מיקום)</option>
                      <option value="no">לא (מהתחלה)</option>
                    </select>
                  </div>
                </div>
      
                <div>
                  <label class="font-semibold text-slate-700 block mb-1">שורות הגדרה נוספות (ext.ini)</label>
                  <textarea id="settingExtraIni" rows="3" class="w-full px-2.5 py-1.5 rounded border border-slate-300 font-mono text-[11px]" placeholder="לדוגמה:&#10;playfile_end_goto=/&#10;speed=1.0"></textarea>
                </div>
      
                <div class="pt-2">
                  <label class="font-semibold text-slate-700 block mb-1">תבנית הודעת הקראה למסכת (TTS)</label>
                  <input type="text" id="massechetTtsTemplate" value="מסכת {name}, {pages} דפים" 
                         class="w-full px-2.5 py-1.5 rounded border border-slate-300 text-xs">
                  <p class="text-[10px] text-slate-400 mt-0.5">קובץ M0000.TTS בשלוחת המסכת להכרזת השם</p>
                </div>
              </div>
            </section>
      
            <!-- Execution Panel -->
            <section class="bg-gradient-to-br from-indigo-900 to-slate-900 text-white rounded-2xl p-5 shadow-lg space-y-4">
              <h3 class="text-base font-bold text-amber-300 flex items-center gap-2">
                <span>🚀</span> יצירה במערכת בפועל
              </h3>
              <p class="text-xs text-slate-300 leading-relaxed">
                בלחיצה על הכפתור, המערכת תשלח פקודות <span class="text-amber-200 font-mono">UpdateExtension</span> ו-<span class="text-amber-200 font-mono">UploadTextFile</span> לשרתי ימות המשיח, תבנה את 6 הסדרים, כל דפי הניווט (כולל מקש 0 לעוד מסכתות), וכל שלוחות ההשמעה עם קובצי TTS.
              </p>
      
              <div class="flex flex-col gap-2 pt-1">
                <button id="startBuildBtn" type="button" class="w-full bg-amber-400 hover:bg-amber-300 text-slate-950 font-bold py-3 px-4 rounded-xl shadow-md transition-all text-sm flex items-center justify-center gap-2">
                  <span>⚡</span> צור את כל שלוחות הש"ס במערכת
                </button>
                
                <button id="downloadZipBtn" type="button" class="w-full bg-slate-800 hover:bg-slate-700 text-slate-200 py-2 px-3 rounded-lg border border-slate-700 text-xs transition-colors flex items-center justify-center gap-1.5">
                  <span>💾</span> הורד עותק כקובץ JSON מקומי
                </button>
              </div>
      
              <!-- Progress bar -->
              <div id="buildProgressContainer" class="hidden space-y-1.5 pt-2 border-t border-slate-800">
                <div class="flex justify-between text-xs text-slate-300">
                  <span id="progressText">מכין שלוחות...</span>
                  <span id="progressPercent" class="font-mono">0%</span>
                </div>
                <div class="w-full bg-slate-800 rounded-full h-2 overflow-hidden border border-slate-700">
                  <div id="progressBar" class="bg-amber-400 h-full w-0 transition-all duration-150"></div>
                </div>
              </div>
            </section>
      
          </div>
      
          <div class="lg:col-span-8 space-y-6">
            
            <!-- Top Overview Stats -->
            <div class="grid grid-cols-2 sm:grid-cols-4 gap-3">
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סך הכל סדרים</div>
                <div class="text-2xl font-bold text-indigo-900 mt-1">6</div>
                <div class="text-[11px] text-slate-400 mt-0.5">מש"ס בבלי</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סך כל המסכתות</div>
                <div class="text-2xl font-bold text-amber-600 mt-1">40</div>
                <div class="text-[11px] text-slate-400 mt-0.5">כולל מסכת שקלים</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">סה"כ דפים בש"ס</div>
                <div class="text-2xl font-bold text-emerald-600 mt-1 font-mono">2,711</div>
                <div class="text-[11px] text-slate-400 mt-0.5">דפי גמרא</div>
              </div>
              <div class="bg-white rounded-xl border border-slate-200 p-3.5 shadow-sm text-center">
                <div class="text-xs text-slate-500 font-medium">שלוחות שייווצרו</div>
                <div id="statTotalExtensions" class="text-2xl font-bold text-blue-600 mt-1 font-mono">49</div>
                <div class="text-[11px] text-slate-400 mt-0.5">סדרים + דפי ניווט + מסכתות</div>
              </div>
            </div>
      
            <!-- Tabs Header -->
            <div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
              <div class="flex border-b border-slate-200 bg-slate-50/70 p-1.5 gap-1.5 text-xs font-semibold">
                <button id="tabTreeBtn" class="tab-btn px-4 py-2 rounded-lg bg-white text-indigo-700 shadow-sm border border-slate-200/80">
                  🌳 עץ השלוחות והמסכתות המלא
                </button>
                <button id="tabSimulatorBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📞 סימולטור ניווט קולי
                </button>
                <button id="tabPayloadBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📄 קבצי ini ו-TTS שיישלחו
                </button>
                <button id="tabLogsBtn" class="tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50">
                  📋 יומן פעולות (Logs)
                </button>
              </div>
      
              <div id="tabTreeContent" class="p-5">
                <div class="flex flex-col sm:flex-row justify-between sm:items-center gap-2 mb-4">
                  <div>
                    <h3 class="font-bold text-slate-800 text-sm">מבנה השלוחות המתוכנן לפי סדר הש"ס</h3>
                    <p class="text-xs text-slate-500">לחיצה על סדר או שלוחה תציג את הפירוט, התפריטים וקובצי ההקראה</p>
                  </div>
                  <div class="flex gap-2">
                    <button id="expandAllTreeBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded border border-slate-300">פתח הכל</button>
                    <button id="collapseAllTreeBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-700 rounded border border-slate-300">סגור הכל</button>
                  </div>
                </div>
      
                <div id="treeContainer" class="space-y-3 font-mono text-xs">
                  <!-- Dynamically populated -->
                </div>
              </div>
      
              <div id="tabSimulatorContent" class="p-5 hidden">
                <div class="max-w-xl mx-auto bg-slate-900 text-white rounded-3xl p-6 shadow-2xl border border-slate-800">
                  <div class="flex items-center justify-between pb-4 border-b border-slate-800 mb-4">
                    <div class="flex items-center gap-2">
                      <span class="w-3 h-3 rounded-full bg-emerald-500 animate-pulse"></span>
                      <span class="text-xs font-semibold text-slate-300">סימולטור ימות המשיח בזמן אמת</span>
                    </div>
                    <span id="simCurrentPath" class="text-xs font-mono text-amber-300 bg-slate-800 px-2 py-0.5 rounded">שלוחה ראשית /</span>
                  </div>
      
                  <!-- Screen: Speech Bubble of IVR TTS -->
                  <div class="bg-slate-800/80 rounded-2xl p-4 border border-slate-700 mb-5 min-h-[120px] flex flex-col justify-between">
                    <div>
                      <span class="text-[11px] font-bold uppercase tracking-wider text-amber-400 block mb-1">📢 הקראה קולית (M0000.TTS):</span>
                      <p id="simVoiceText" class="text-sm text-slate-100 font-sans leading-relaxed">ברוכים הבאים לש"ס בבלי. לבחירת סדר: הקישו 1 לזרעים, 2 למועד, 3 לנשים, 4 לנזיקין, 5 לקדשים, 6 לטהרות.</p>
                    </div>
                    <div class="mt-3 pt-2 border-t border-slate-700/60 flex items-center justify-between text-[11px] text-slate-400">
                      <span id="simExtType">סוג: תפריט (menu)</span>
                      <button id="simSpeakAudioBtn" class="text-indigo-300 hover:text-indigo-200 flex items-center gap-1">
                        <span>🔊</span> השמע בדפדפן (TTS)
                      </button>
                    </div>
                  </div>
      
                  <!-- Phone Keypad -->
                  <div class="grid grid-cols-3 gap-3 max-w-[280px] mx-auto mb-4">
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="1">1</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="2">2</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="3">3</button>
                    
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="4">4</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="5">5</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="6">6</button>
                    
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="7">7</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="8">8</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="9">9</button>
                    
                    <button class="keypad-btn bg-slate-800/50 hover:bg-slate-800 font-bold text-sm py-3 rounded-xl border border-slate-700 text-slate-400" data-key="*">* (חזרה)</button>
                    <button class="keypad-btn bg-slate-800 hover:bg-slate-700 active:bg-amber-400 active:text-slate-950 font-bold text-lg py-3 rounded-xl border border-slate-700 transition" data-key="0">0</button>
                    <button class="keypad-btn bg-slate-800/50 hover:bg-slate-800 font-bold text-sm py-3 rounded-xl border border-slate-700 text-slate-400" data-key="#"># (אישור)</button>
                  </div>
      
                  <div class="flex justify-between items-center text-xs pt-2 border-t border-slate-800 text-slate-400">
                    <button id="simResetBtn" class="hover:text-amber-300">🔄 חזרה לראש התפריט</button>
                    <span>מקש 0 יעביר למסכתות נוספות בסדר</span>
                  </div>
                </div>
              </div>
      
              <div id="tabPayloadContent" class="p-5 hidden">
                <div class="flex justify-between items-center mb-3">
                  <h3 class="font-bold text-slate-800 text-sm">סקירת קבצים ופקודות שיישלחו למערכת</h3>
                  <span class="text-xs text-slate-500">קבצי ext.ini וקובצי M0000.TTS</span>
                </div>
      
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                  <div class="border border-slate-200 rounded-xl p-3 bg-slate-50 max-h-[460px] overflow-y-auto space-y-1" id="payloadList">
                    <!-- Dynamically populated list of files -->
                  </div>
                  <div class="md:col-span-2 border border-slate-200 rounded-xl p-4 bg-slate-900 text-slate-100 flex flex-col">
                    <div class="flex justify-between items-center border-b border-slate-800 pb-2 mb-2 text-xs">
                      <span id="payloadFileTitle" class="font-mono text-amber-300 font-bold">בחר קובץ מהרשימה</span>
                      <span id="payloadFilePath" class="text-slate-400 font-mono text-[11px]"></span>
                    </div>
                    <pre id="payloadFileContent" class="font-mono text-xs overflow-auto flex-1 p-2 bg-slate-950/60 rounded text-emerald-400 leading-relaxed whitespace-pre-wrap select-all">בחר קובץ מימין לצפייה בתוכנו המדויק</pre>
                  </div>
                </div>
              </div>
      
              <div id="tabLogsContent" class="p-5 hidden">
                <div class="flex justify-between items-center mb-3">
                  <h3 class="font-bold text-slate-800 text-sm">יומן בקשות ותשובות API</h3>
                  <button id="clearLogsBtn" class="text-xs px-2.5 py-1 bg-slate-100 hover:bg-slate-200 text-slate-600 rounded">נקה יומן</button>
                </div>
                <div id="logsConsole" class="bg-slate-950 text-slate-200 rounded-xl p-4 font-mono text-xs h-96 overflow-y-auto space-y-1.5 border border-slate-800">
                  <div class="text-slate-500">// יומן הפעולות מוכן. לחץ על 'בדוק חיבור' או 'צור שלוחות'.</div>
                </div>
              </div>
      
            </div>
      
          </div>
      
        </main>
      
        <footer class="bg-slate-100 border-t border-slate-200 text-slate-500 text-xs py-4 text-center">
          <div class="max-w-7xl mx-auto px-4">
            מערכת ניהול ובניית שלוחות ש"ס למרכזיית ימות המשיח • תואם API 2026 • מודול השמעת קבצים והקראה אוטומטית (TTS)
          </div>
        </footer>
      
        <script>
          /*
           * נתוני ששת סדרי משנה וש"ס בבלי
           * הסדר המדויק, המספר הסידורי, וכמות הדפים עפ"י הקובץ המצורף (42389.pdf)
           */
          const SEDER_DATA = [
            {
              id: 1,
              key: "1",
              name: "זרעים",
              icon: "🌾",
              title: "סדר זרעים",
              masechtot: [
                { order: 1, name: "ברכות", pages: 63 }
              ]
            },
            {
              id: 2,
              key: "2",
              name: "מועד",
              icon: "📜",
              title: "סדר מועד",
              masechtot: [
                { order: 2, name: "שבת", pages: 156 },
                { order: 3, name: "עירובין", pages: 104 },
                { order: 4, name: "פסחים", pages: 120 },
                { order: 5, name: "שקלים", pages: 21 },
                { order: 6, name: "ראש השנה", pages: 34 },
                { order: 7, name: "יומא", pages: 87 },
                { order: 8, name: "סוכה", pages: 55 },
                { order: 9, name: "ביצה", pages: 39 },
                { order: 10, name: "תענית", pages: 30 },
                { order: 11, name: "מגילה", pages: 31 },
                { order: 12, name: "מועד קטן", pages: 28 },
                { order: 13, name: "חגיגה", pages: 26 }
              ]
            },
            {
              id: 3,
              key: "3",
              name: "נשים",
              icon: "💍",
              title: "סדר נשים",
              masechtot: [
                { order: 14, name: "יבמות", pages: 121 },
                { order: 15, name: "כתובות", pages: 111 },
                { order: 16, name: "נדרים", pages: 90 },
                { order: 17, name: "נזיר", pages: 65 },
                { order: 18, name: "סוטה", pages: 48 },
                { order: 19, name: "גיטין", pages: 89 },
                { order: 20, name: "קידושין", pages: 81 }
              ]
            },
            {
              id: 4,
              key: "4",
              name: "נזיקין",
              icon: "⚖️",
              title: "סדר נזיקין",
              masechtot: [
                { order: 21, name: "בבא קמא", pages: 118 },
                { order: 22, name: "בבא מציעא", pages: 118 },
                { order: 23, name: "בבא בתרא", pages: 175 },
                { order: 24, name: "סנהדרין", pages: 112 },
                { order: 25, name: "מכות", pages: 23 },
                { order: 26, name: "שבועות", pages: 48 },
                { order: 27, name: "עבודה זרה", pages: 75 },
                { order: 28, name: "הוריות", pages: 13 }
              ]
            },
            {
              id: 5,
              key: "5",
              name: "קדשים",
              icon: "🏛️",
              title: "סדר קדשים",
              masechtot: [
                { order: 29, name: "זבחים", pages: 119 },
                { order: 30, name: "מנחות", pages: 109 },
                { order: 31, name: "חולין", pages: 141 },
                { order: 32, name: "בכורות", pages: 60 },
                { order: 33, name: "ערכין", pages: 33 },
                { order: 34, name: "תמורה", pages: 33 },
                { order: 35, name: "כריתות", pages: 27 },
                { order: 36, name: "מעילה", pages: 20 },
                { order: 37, name: "קינים", pages: 3 },
                { order: 38, name: "תמיד", pages: 9 },
                { order: 39, name: "מידות", pages: 4 }
              ]
            },
            {
              id: 6,
              key: "6",
              name: "טהרות",
              icon: "✨",
              title: "סדר טהרות",
              masechtot: [
                { order: 40, name: "נדה", pages: 72 }
              ]
            }
          ];
      
          const State = {
            apiToken: '',
            basePath: '1',
            generatedPlan: [],
            filesMap: new Map(), // path -> { type, content, desc }
            simState: {
              currentPath: '',
              history: []
            }
          };
      
          /**
           * פונקציה לבניית מבנה העץ המלא של השלוחות, התפריטים, קבצי ה-ext.ini וקבצי ה-TTS
           * בהתאם לדרישת הלקוח:
           * - תפריט סדרים
           * - בכל סדר תפריט מסכתות מפוצל לדפים (1-9 מסכתות, מקש 0 מעביר לעוד מסכתות)
           * - בשלוחת 0 האחרונה: הודעה שזו השלוחה האחרונה
           * - שלוחות מסכת מסוג השמעת קבצים (playfile) עם קובץ TTS
           */
          function generateFullIvrPlan() {
            const rootPath = (State.basePath.trim() === '' || State.basePath.trim() === '/') ? '' : State.basePath.trim().replace(/^\/+|\/+$/g, '');
            const formatPath = (...parts) => parts.filter(p => p !== '' && p !== undefined).join('/');
      
            const plan = [];
            State.filesMap.clear();
      
            // 1. שלוחת הש"ס הראשית (תפריט סדרים)
            const rootIvrPath = rootPath === '' ? '' : rootPath;
            const rootTTS = "ברוכים הבאים לש\"ס בבלי. " +
              SEDER_DATA.map(s => `לסדר ${s.name} הקישו ${s.key}`).join(", ") + ".";
      
            const rootIni = [
              "type=menu",
              "title=ש\"ס בבלי - בחירת סדר",
              ...SEDER_DATA.map(s => `${s.key}=${formatPath(rootIvrPath, s.key)}`)
            ].join("\n");
      
            plan.push({
              path: rootIvrPath || '/',
              title: 'ש"ס בבלי - ראשי',
              type: 'menu',
              description: 'תפריט בחירת סדר',
              ini: rootIni,
              tts: rootTTS,
              ttsFileName: 'M0000.TTS'
            });
      
            // רישום קבצים
            State.filesMap.set(formatPath(rootIvrPath, 'ext.ini') || 'ext.ini', {
              title: 'הגדרות שלוחה ראשית',
              content: rootIni
            });
            State.filesMap.set(formatPath(rootIvrPath, 'M0000.TTS') || 'M0000.TTS', {
              title: 'הקראת תפריט סדרים',
              content: rootTTS
            });
      
            // 2. מעבר על כל סדר
            SEDER_DATA.forEach(seder => {
              const sederBasePath = formatPath(rootIvrPath, seder.key);
              const masechtot = seder.masechtot;
      
              // אם יש עד 9 מסכתות - דף יחיד. אם יותר - חלוקה לקבוצות של 9
              // מקשים 1-9 הם למסכתות, מקש 0 הוא מעבר לקבוצה הבאה (או השלוחה האחרונה)
              const CHUNK_SIZE = 9;
              const totalPages = Math.ceil(masechtot.length / CHUNK_SIZE) || 1;
      
              for (let pageIdx = 0; pageIdx < totalPages; pageIdx++) {
                const isFirstPage = (pageIdx === 0);
                // נתיב הדף: דף ראשון בסדר הוא סדר.קיי, דף הבא הוא תחת שלוחה 0 וכך הלאה
                let pagePath = sederBasePath;
                for (let p = 0; p < pageIdx; p++) {
                  pagePath = formatPath(pagePath, '0');
                }
      
                const startIdx = pageIdx * CHUNK_SIZE;
                const pageMasechtot = masechtot.slice(startIdx, startIdx + CHUNK_SIZE);
                const hasNextPage = (pageIdx + 1 < totalPages);
      
                // בניית טקסט TTS והגדרות ext.ini לתפריט המסכתות
                let pageTtsParts = [`סדר ${seder.name}, חלק ${pageIdx + 1}.`];
                const pageIniLines = [
                  "type=menu",
                  `title=סדר ${seder.name} - דף ${pageIdx + 1}`
                ];
      
                pageMasechtot.forEach((m, idx) => {
                  const digit = (idx + 1).toString();
                  const masechetPath = formatPath(pagePath, digit);
                  pageTtsParts.push(`למסכת ${m.name} הקישו ${digit}`);
                  pageIniLines.push(`${digit}=${masechetPath}`);
      
                  // 3. יצירת שלוחת המסכת (השמעת קבצים)
                  const masechetIni = buildPlayfileIni(m.name, seder.name, m.pages);
                  const masechetTts = buildMasechetTts(m.name, m.pages);
      
                  plan.push({
                    path: masechetPath,
                    title: `מסכת ${m.name}`,
                    type: 'playfile',
                    description: `שלוחת השמעת קבצים - מסכת ${m.name} (${m.pages} דפים)`,
                    ini: masechetIni,
                    tts: masechetTts,
                    ttsFileName: 'M0000.TTS',
                    sederName: seder.name,
                    masechetName: m.name,
                    pages: m.pages
                  });
      
                  State.filesMap.set(formatPath(masechetPath, 'ext.ini'), {
                    title: `הגדרות שלוחה: מסכת ${m.name}`,
                    content: masechetIni
                  });
                  State.filesMap.set(formatPath(masechetPath, 'M0000.TTS'), {
                    title: `הקראת פתיחה: מסכת ${m.name}`,
                    content: masechetTts
                  });
                });
      
                // טיפול במקש 0:
                if (hasNextPage) {
                  const nextPagePath = formatPath(pagePath, '0');
                  pageTtsParts.push(`למסכתות נוספות בסדר ${seder.name} הקישו 0`);
                  pageIniLines.push(`0=${nextPagePath}`);
                } else {
                  // זהו הדף האחרון בסדר זה
                  // שלוחה 0 תודיע שזו השלוחה האחרונה בסדר
                  const lastZeroPath = formatPath(pagePath, '0');
                  const endSederTts = `זוהי השלוחה האחרונה לסדר ${seder.name}.`;
                  const endSederIni = [
                    "type=playfile",
                    `title=סיום סדר ${seder.name}`,
                    "playfile_end_goto=.."
                  ].join("\n");
      
                  pageTtsParts.push(`למסכתות נוספות בסדר ${seder.name} הקישו 0`);
                  pageIniLines.push(`0=${lastZeroPath}`);
      
                  plan.push({
                    path: lastZeroPath,
                    title: `סיום סדר ${seder.name}`,
                    type: 'playfile',
                    description: `שלוחה 0 - סיום סדר ${seder.name}`,
                    ini: endSederIni,
                    tts: endSederTts,
                    ttsFileName: 'M0000.TTS',
                    isLastZero: true
                  });
      
                  State.filesMap.set(formatPath(lastZeroPath, 'ext.ini'), {
                    title: `הגדרות שלוחה 0 (סוף סדר ${seder.name})`,
                    content: endSederIni
                  });
                  State.filesMap.set(formatPath(lastZeroPath, 'M0000.TTS'), {
                    title: `הקראת סוף סדר ${seder.name}`,
                    content: endSederTts
                  });
                }
      
                const pageTts = pageTtsParts.join(", ") + ".";
                const pageIni = pageIniLines.join("\n");
      
                plan.push({
                  path: pagePath,
                  title: `סדר ${seder.name} - תפריט ${pageIdx + 1}`,
                  type: 'menu',
                  description: `תפריט מסכתות ${pageIdx * CHUNK_SIZE + 1} עד ${Math.min((pageIdx + 1) * CHUNK_SIZE, masechtot.length)}`,
                  ini: pageIni,
                  tts: pageTts,
                  ttsFileName: 'M0000.TTS',
                  sederName: seder.name,
                  pageIdx: pageIdx + 1
                });
      
                State.filesMap.set(formatPath(pagePath, 'ext.ini'), {
                  title: `הגדרות תפריט: סדר ${seder.name} (חלק ${pageIdx + 1})`,
                  content: pageIni
                });
                State.filesMap.set(formatPath(pagePath, 'M0000.TTS'), {
                  title: `הקראת תפריט: סדר ${seder.name} (חלק ${pageIdx + 1})`,
                  content: pageTts
                });
              }
            });
      
            State.generatedPlan = plan;
            return plan;
          }
      
          function buildPlayfileIni(masechetName, sederName, pages) {
            const order = document.getElementById('settingOrder')?.value || 'asc';
            const autoPlay = document.getElementById('settingAutoPlay')?.value || 'yes';
            const remember = document.getElementById('settingRemember')?.value || 'yes';
            const extra = document.getElementById('settingExtraIni')?.value.trim() || '';
      
            const lines = [
              "type=playfile",
              `title=מסכת ${masechetName} - סדר ${sederName} (${pages} דפים)`,
              `playfile_order=${order}`,
              `playfile_auto_play=${autoPlay}`,
              `playfile_remember_location=${remember}`
            ];
      
            if (extra) {
              lines.push(extra);
            }
            return lines.join("\n");
          }
      
          function buildMasechetTts(masechetName, pages) {
            const template = document.getElementById('massechetTtsTemplate')?.value || 'מסכת {name}, {pages} דפים';
            return template.replace('{name}', masechetName).replace('{pages}', pages);
          }
      
          const YemotAPI = {
            getBaseUrl() {
              return "https://www.call2all.co.il/ym/api/";
            },
      
            wrapUrl(endpoint, params = {}) {
              const token = State.apiToken.trim();
              const fullParams = new URLSearchParams({ token, ...params });
              const targetUrl = `${this.getBaseUrl()}${endpoint}?${fullParams.toString()}`;
      
              const proxyMode = document.getElementById('corsProxySelect').value;
              if (proxyMode === 'corsproxy') {
                return `https://corsproxy.io/?${encodeURIComponent(targetUrl)}`;
              } else if (proxyMode === 'allorigins') {
                return `https://api.allorigins.win/raw?url=${encodeURIComponent(targetUrl)}`;
              } else if (proxyMode === 'custom') {
                const custom = document.getElementById('customProxyInput').value.trim();
                return `${custom}${encodeURIComponent(targetUrl)}`;
              }
              return targetUrl;
            },
      
            async getSession() {
              const url = this.wrapUrl('GetSession');
              log('API', `בודק חיבור עם GetSession...`);
              const res = await fetch(url);
              if (!res.ok) throw new Error(`HTTP Error: ${res.status}`);
              return await res.json();
            },
      
            async updateExtension(path, iniObject) {
              // UpdateExtension?token=...&path=ivr2:1&type=menu...
              const formattedPath = path.startsWith('ivr2:') ? path : `ivr2:${path === '/' ? '' : path}`;
              const params = {
                path: formattedPath,
                ...iniObject
              };
              const url = this.wrapUrl('UpdateExtension', params);
              const res = await fetch(url);
              if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
              return await res.json();
            },
      
            async uploadTextFile(fullIvrPath, contents) {
              // UploadTextFile?token=...&what=ivr2:1/ext.ini&contents=...
              const what = fullIvrPath.startsWith('ivr2:') ? fullIvrPath : `ivr2:${fullIvrPath}`;
              const url = this.wrapUrl('UploadTextFile');
              
              // POST עם FormData כדי למנוע חיתוך טקסט עברי ארוך ב-URL
              const formData = new FormData();
              formData.append('token', State.apiToken.trim());
              formData.append('what', what);
              formData.append('contents', contents);
      
              const res = await fetch(url, {
                method: 'POST',
                body: formData
              });
      
              if (!res.ok) throw new Error(`HTTP Error ${res.status}`);
              return await res.json();
            }
          };
      
          function log(type, msg, data = null) {
            const logsConsole = document.getElementById('logsConsole');
            const time = new Date().toLocaleTimeString('he-IL');
            const el = document.createElement('div');
            
            let badgeColor = 'bg-slate-700 text-slate-300';
            if (type === 'SUCCESS') badgeColor = 'bg-emerald-900/80 text-emerald-300 border border-emerald-700';
            if (type === 'ERROR') badgeColor = 'bg-rose-900/80 text-rose-300 border border-rose-700';
            if (type === 'WARN') badgeColor = 'bg-amber-900/80 text-amber-300 border border-amber-700';
            if (type === 'API') badgeColor = 'bg-blue-900/80 text-blue-300 border border-blue-700';
      
            el.innerHTML = `
              <span class="text-slate-500">[${time}]</span>
              <span class="px-1.5 py-0.5 rounded text-[10px] font-bold ${badgeColor}">${type}</span>
              <span class="text-slate-200">${escapeHtml(msg)}</span>
              ${data ? `<pre class="text-[10px] text-slate-400 mt-0.5 pl-4 overflow-x-auto">${escapeHtml(typeof data === 'object' ? JSON.stringify(data, null, 2) : String(data))}</pre>` : ''}
            `;
            logsConsole.appendChild(el);
            logsConsole.scrollTop = logsConsole.scrollHeight;
          }
      
          function escapeHtml(str) {
            if (!str) return '';
            return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
          }
      
          function renderTree() {
            const container = document.getElementById('treeContainer');
            container.innerHTML = '';
      
            const plan = State.generatedPlan;
            document.getElementById('statTotalExtensions').textContent = plan.length;
      
            // קיבוץ לפי סדרים
            SEDER_DATA.forEach(seder => {
              const sederItems = plan.filter(p => p.sederName === seder.name || (p.path.includes(`/${seder.key}`) || p.path === `${State.basePath}/${seder.key}`));
              
              const card = document.createElement('div');
              card.className = "border border-slate-200 rounded-xl bg-white overflow-hidden shadow-sm";
              
              const header = document.createElement('div');
              header.className = "bg-slate-50 px-4 py-3 flex items-center justify-between cursor-pointer hover:bg-slate-100 transition-colors border-b border-slate-100";
              header.innerHTML = `
                <div class="flex items-center gap-2">
                  <span class="text-lg">${seder.icon}</span>
                  <span class="font-bold text-slate-800 text-sm font-sans">${seder.title}</span>
                  <span class="bg-indigo-100 text-indigo-800 px-2 py-0.5 rounded-full text-[10px] font-mono font-bold">${seder.masechtot.length} מסכתות</span>
                </div>
                <div class="flex items-center gap-3">
                  <span class="text-slate-400 text-xs font-mono">מקש ${seder.key}</span>
                  <span class="transform transition-transform text-slate-400 text-xs" id="chevron-${seder.id}">▼</span>
                </div>
              `;
      
              const content = document.createElement('div');
              content.className = "p-4 space-y-3 bg-white";
              content.id = `seder-content-${seder.id}`;
      
              // מסכתות הסדר הזה
              const masechtotInSeder = plan.filter(p => p.sederName === seder.name && p.type === 'playfile' && !p.isLastZero);
              const menusInSeder = plan.filter(p => p.sederName === seder.name && p.type === 'menu');
              const lastZero = plan.find(p => p.sederName === seder.name && p.isLastZero);
      
              let html = '';
      
              // דפי תפריט
              html += `<div class="mb-3">
                <div class="text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-1.5 font-sans">תפריטי ניווט ודפי מקש 0:</div>
                <div class="grid grid-cols-1 sm:grid-cols-2 gap-2">`;
              
              menusInSeder.forEach(m => {
                html += `
                  <div class="bg-indigo-50/60 border border-indigo-100 rounded-lg p-2.5">
                    <div class="flex items-center justify-between font-bold text-indigo-900">
                      <span>📁 שלוחה ${m.path}</span>
                      <span class="text-[10px] bg-indigo-200/70 text-indigo-800 px-1.5 py-0.2 rounded font-sans">תפריט דף ${m.pageIdx || 1}</span>
                    </div>
                    <div class="text-[11px] text-slate-600 font-sans mt-1"><strong>TTS:</strong> "${m.tts}"</div>
                  </div>`;
              });
      
              if (lastZero) {
                html += `
                  <div class="bg-amber-50/70 border border-amber-200 rounded-lg p-2.5">
                    <div class="flex items-center justify-between font-bold text-amber-900">
                      <span>🔔 שלוחה 0 (סוף סדר): ${lastZero.path}</span>
                      <span class="text-[10px] bg-amber-200 text-amber-800 px-1.5 py-0.2 rounded font-sans">הודעת סיום</span>
                    </div>
                    <div class="text-[11px] text-slate-700 font-sans mt-1"><strong>הודעה:</strong> "${lastZero.tts}"</div>
                  </div>`;
              }
              html += `</div></div>`;
      
              // רשימת המסכתות
              html += `<div>
                <div class="text-[11px] font-bold text-slate-500 uppercase tracking-wider mb-1.5 font-sans">שלוחות השמעת קבצים (מסכתות):</div>
                <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2">`;
      
              masechtotInSeder.forEach(m => {
                html += `
                  <div class="border border-slate-200 rounded-lg p-2.5 bg-slate-50 hover:bg-slate-100/80 transition-colors">
                    <div class="flex justify-between items-start">
                      <span class="font-bold text-slate-800 font-sans text-xs">מסכת ${m.masechetName}</span>
                      <span class="bg-emerald-100 text-emerald-800 text-[10px] px-1.5 py-0.5 rounded font-mono font-bold">${m.pages} דף</span>
                    </div>
                    <div class="text-[11px] text-slate-500 font-mono mt-1">נתיב: ${m.path}</div>
                    <div class="text-[10px] text-slate-400 font-sans mt-0.5 truncate">TTS: ${m.tts}</div>
                  </div>`;
              });
              html += `</div></div>`;
      
              content.innerHTML = html;
              header.addEventListener('click', () => {
                content.classList.toggle('hidden');
                const chevron = document.getElementById(`chevron-${seder.id}`);
                chevron.textContent = content.classList.contains('hidden') ? '◀' : '▼';
              });
      
              card.appendChild(header);
              card.appendChild(content);
              container.appendChild(card);
            });
      
            renderPayloadsList();
          }
      
          function renderPayloadsList() {
            const list = document.getElementById('payloadList');
            list.innerHTML = '';
      
            let first = true;
            State.filesMap.forEach((fileObj, path) => {
              const item = document.createElement('div');
              item.className = "px-2.5 py-2 rounded-lg cursor-pointer text-xs font-mono truncate hover:bg-slate-200/70 border border-transparent transition-colors flex items-center justify-between";
              
              const isIni = path.endsWith('.ini');
              const icon = isIni ? '⚙️' : '🔊';
              item.innerHTML = `
                <span class="truncate" title="${path}">${icon} ${path}</span>
                <span class="text-[10px] text-slate-400 font-sans">${isIni ? 'ini' : 'TTS'}</span>
              `;
      
              item.addEventListener('click', () => {
                document.querySelectorAll('#payloadList > div').forEach(el => el.classList.remove('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold'));
                item.classList.add('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold');
                showPayload(path, fileObj);
              });
      
              list.appendChild(item);
      
              if (first) {
                item.classList.add('bg-indigo-100', 'border-indigo-300', 'text-indigo-900', 'font-bold');
                showPayload(path, fileObj);
                first = false;
              }
            });
          }
      
          function showPayload(path, fileObj) {
            document.getElementById('payloadFileTitle').textContent = fileObj.title || path;
            document.getElementById('payloadFilePath').textContent = `ivr2:${path}`;
            document.getElementById('payloadFileContent').textContent = fileObj.content;
          }
      
          const Simulator = {
            currentPlanNode: null,
      
            init() {
              this.goToRoot();
              this.setupKeypad();
            },
      
            goToRoot() {
              const rootPath = (State.basePath.trim() === '' || State.basePath.trim() === '/') ? '' : State.basePath.trim().replace(/^\/+|\/+$/g, '');
              const target = rootPath === '' ? '/' : rootPath;
              this.navigateTo(target);
            },
      
            navigateTo(path) {
              const plan = State.generatedPlan;
              let node = plan.find(p => p.path === path);
              if (!node && path === '/') {
                node = plan.find(p => p.path === '/' || p.path === '');
              }
      
              if (!node) {
                this.setScreen(path, "השלוחה אינה קיימת במערכת", "שגיאה");
                return;
              }
      
              this.currentPlanNode = node;
              State.simState.currentPath = node.path;
              
              let displayPath = node.path === '' ? '/' : `/${node.path}`;
              let speech = node.tts;
              if (node.type === 'playfile') {
                speech = `שלוחת השמעת קבצים: ${node.title}. (${node.tts}). מתחיל השמעת שיעורים...`;
              }
      
              this.setScreen(displayPath, speech, `סוג: ${node.type} | ${node.title}`);
            },
      
            setScreen(path, text, type) {
              document.getElementById('simCurrentPath').textContent = `שלוחה: ${path}`;
              document.getElementById('simVoiceText').textContent = text;
              document.getElementById('simExtType').textContent = type;
            },
      
            pressKey(key) {
              if (!this.currentPlanNode) return;
              
              // כפתור כוכבית לחזרה אחורה
              if (key === '*') {
                const parts = this.currentPlanNode.path.split('/').filter(Boolean);
                if (parts.length > 0) {
                  parts.pop();
                  const parent = parts.join('/') || '/';
                  this.navigateTo(parent);
                } else {
                  this.navigateTo('/');
                }
                return;
              }
      
              // אם זו שלוחת תפריט
              if (this.currentPlanNode.type === 'menu') {
                const ini = this.currentPlanNode.ini;
                const match = ini.split('\n').find(line => line.startsWith(`${key}=`));
                if (match) {
                  const targetPath = match.split('=')[1].trim();
                  this.navigateTo(targetPath);
                } else {
                  this.setScreen(this.currentPlanNode.path, `הקשה ${key} שגויה. ${this.currentPlanNode.tts}`, `סוג: menu`);
                }
              } else if (this.currentPlanNode.type === 'playfile') {
                if (key === '0') {
                  this.setScreen(this.currentPlanNode.path, `בשלוחת השמעת קבצים לא הוגדר מעבר על מקש 0.`, `סוג: playfile`);
                }
              }
            },
      
            setupKeypad() {
              document.querySelectorAll('.keypad-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                  const key = btn.getAttribute('data-key');
                  this.pressKey(key);
                });
              });
      
              document.getElementById('simResetBtn').addEventListener('click', () => {
                this.goToRoot();
              });
      
              // כפתור דיבור TTS של הדפדפן לבדיקת שמיעה
              document.getElementById('simSpeakAudioBtn').addEventListener('click', () => {
                const text = document.getElementById('simVoiceText').textContent;
                if ('speechSynthesis' in window) {
                  window.speechSynthesis.cancel();
                  const utterance = new SpeechSynthesisUtterance(text);
                  utterance.lang = 'he-IL';
                  utterance.rate = 0.95;
                  window.speechSynthesis.speak(utterance);
                } else {
                  alert('הדפדפן אינו תומך בהקראת קולית מובנית');
                }
              });
            }
          };
      
          async function startBuildingExtensions() {
            const token = document.getElementById('apiTokenInput').value.trim();
            if (!token) {
              alert('אנא הזן טוקן API של ימות המשיח כדי לבצע את ההקמה');
              document.getElementById('apiTokenInput').focus();
              return;
            }
            State.apiToken = token;
            State.basePath = document.getElementById('basePathInput').value.trim();
      
            // רענון התוכנית
            generateFullIvrPlan();
            const plan = State.generatedPlan;
      
            if (!confirm(`האם להתחיל בהקמת ${plan.length} שלוחות ש"ס וקבצי TTS במערכת ימות המשיח שלך?`)) {
              return;
            }
      
            const progressContainer = document.getElementById('buildProgressContainer');
            const progressBar = document.getElementById('progressBar');
            const progressText = document.getElementById('progressText');
            const progressPercent = document.getElementById('progressPercent');
            const startBtn = document.getElementById('startBuildBtn');
      
            progressContainer.classList.remove('hidden');
            startBtn.disabled = true;
            startBtn.classList.add('opacity-50', 'cursor-not-allowed');
      
            log('INFO', `מתחיל תהליך הקמה מלא של ${plan.length} שלוחות ש"ס...`);
      
            let completed = 0;
            let errors = 0;
      
            for (let i = 0; i < plan.length; i++) {
              const item = plan[i];
              const percent = Math.round(((i + 1) / plan.length) * 100);
              progressBar.style.width = `${percent}%`;
              progressPercent.textContent = `${percent}%`;
              progressText.textContent = `יוצר: ${item.title} (${item.path || '/'})`;
      
              try {
                // שלב 1: העלאת קובץ ext.ini באמצעות UploadTextFile
                const iniPath = item.path === '/' || item.path === '' ? 'ext.ini' : `${item.path}/ext.ini`;
                log('API', `מעלה הגדרות: ${iniPath}`);
                await YemotAPI.uploadTextFile(iniPath, item.ini);
      
                // שלב 2: העלאת קובץ M0000.TTS עם טקסט ההקראה האוטומטי
                const ttsPath = item.path === '/' || item.path === '' ? item.ttsFileName : `${item.path}/${item.ttsFileName}`;
                log('API', `מעלה קובץ הקראה: ${ttsPath}`);
                await YemotAPI.uploadTextFile(ttsPath, item.tts);
      
                completed++;
                log('SUCCESS', `הושלמה שלוחה: ${item.title} [${item.path}]`);
                
                // השהייה קלה למניעת הצפת ה-API
                await new Promise(r => setTimeout(r, 200));
      
              } catch (err) {
                errors++;
                log('ERROR', `נכשלה הקמת שלוחה ${item.path}: ${err.message}`, err);
                // ממשיך לשלוחה הבאה
              }
            }
      
            progressBar.style.width = '100%';
            progressPercent.textContent = '100%';
            progressText.textContent = 'ההקמה הסתיימה!';
            startBtn.disabled = false;
            startBtn.classList.remove('opacity-50', 'cursor-not-allowed');
      
            if (errors === 0) {
              log('SUCCESS', `כל ${completed} השלוחות הוקמו בהצלחה מלאה!`);
              alert(`ההקמה הסתיימה בהצלחה!\nנוצרו ${completed} שלוחות, תפריטי ניווט וקובצי TTS.`);
            } else {
              log('WARN', `ההקמה הסתיימה עם ${errors} שגיאות מתוך ${plan.length} שלוחות.`);
              alert(`ההקמה הסתיימה.\nהצליחו: ${completed}\nנכשלו: ${errors}\nבדוק את לשונית היומן לפרטים.`);
            }
          }
      
          function initUI() {
            // Tab switching
            const tabs = [
              { btn: 'tabTreeBtn', content: 'tabTreeContent' },
              { btn: 'tabSimulatorBtn', content: 'tabSimulatorContent' },
              { btn: 'tabPayloadBtn', content: 'tabPayloadContent' },
              { btn: 'tabLogsBtn', content: 'tabLogsContent' }
            ];
      
            tabs.forEach(t => {
              document.getElementById(t.btn).addEventListener('click', () => {
                tabs.forEach(other => {
                  const b = document.getElementById(other.btn);
                  const c = document.getElementById(other.content);
                  if (other.btn === t.btn) {
                    b.className = "tab-btn px-4 py-2 rounded-lg bg-white text-indigo-700 shadow-sm border border-slate-200/80 font-bold";
                    c.classList.remove('hidden');
                  } else {
                    b.className = "tab-btn px-4 py-2 rounded-lg text-slate-600 hover:text-slate-900 hover:bg-white/50";
                    c.classList.add('hidden');
                  }
                });
              });
            });
      
            // Expand / Collapse all
            document.getElementById('expandAllTreeBtn').addEventListener('click', () => {
              SEDER_DATA.forEach(s => {
                document.getElementById(`seder-content-${s.id}`).classList.remove('hidden');
                document.getElementById(`chevron-${s.id}`).textContent = '▼';
              });
            });
      
            document.getElementById('collapseAllTreeBtn').addEventListener('click', () => {
              SEDER_DATA.forEach(s => {
                document.getElementById(`seder-content-${s.id}`).classList.add('hidden');
                document.getElementById(`chevron-${s.id}`).textContent = '◀';
              });
            });
      
            // Proxy custom input toggle
            document.getElementById('corsProxySelect').addEventListener('change', (e) => {
              const customInput = document.getElementById('customProxyInput');
              if (e.target.value === 'custom') {
                customInput.classList.remove('hidden');
              } else {
                customInput.classList.add('hidden');
              }
            });
      
            // Inputs change -> regenerate
            ['basePathInput', 'settingOrder', 'settingAutoPlay', 'settingRemember', 'settingExtraIni', 'massechetTtsTemplate'].forEach(id => {
              const el = document.getElementById(id);
              if (el) {
                el.addEventListener('input', () => {
                  State.basePath = document.getElementById('basePathInput').value.trim();
                  generateFullIvrPlan();
                  renderTree();
                  Simulator.goToRoot();
                });
              }
            });
      
            // Test Connection Button
            document.getElementById('testConnectionBtn').addEventListener('click', async () => {
              const token = document.getElementById('apiTokenInput').value.trim();
              const msgEl = document.getElementById('connectionMsg');
              const badge = document.getElementById('apiStatusBadge');
      
              if (!token) {
                msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                msgEl.textContent = "אנא הזן טוקן API";
                msgEl.classList.remove('hidden');
                return;
              }
      
              State.apiToken = token;
              msgEl.className = "text-xs p-2.5 rounded-lg border bg-slate-100 border-slate-300 text-slate-700";
              msgEl.textContent = "בודק חיבור מול ימות המשיח...";
              msgEl.classList.remove('hidden');
      
              try {
                const res = await YemotAPI.getSession();
                if (res.responseStatus === 'OK') {
                  msgEl.className = "text-xs p-2.5 rounded-lg border bg-emerald-50 border-emerald-200 text-emerald-800";
                  msgEl.innerHTML = `<strong>חיבור תקין!</strong> מערכת: ${res.username || ''} | שם: ${res.name || 'ללא'} | יחידות: ${res.units || 0}`;
                  badge.className = "inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium bg-emerald-950 text-emerald-300 border border-emerald-700";
                  badge.innerHTML = `<span class="w-2 h-2 rounded-full bg-emerald-400"></span> מחובר למערכת`;
                  log('SUCCESS', 'התחברות הצליחה מול שרת ימות המשיח', res);
                } else {
                  msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                  msgEl.textContent = `שגיאה מהשרת: ${res.message || res.responseStatus}`;
                  log('ERROR', 'שגיאת התחברות', res);
                }
              } catch (err) {
                msgEl.className = "text-xs p-2.5 rounded-lg border bg-rose-50 border-rose-200 text-rose-800";
                msgEl.innerHTML = `שגיאת רשת / CORS: ${err.message}.<br><span class="text-[11px]">אם אתה משתמש ישירות בדפדפן, מומלץ לבחור באפשרות 'CORS Proxy' או להריץ מתוך שרת מקומי.</span>`;
                log('ERROR', 'שגיאת רשת בבדיקת חיבור', err.message);
              }
            });
      
            // Clear logs
            document.getElementById('clearLogsBtn').addEventListener('click', () => {
              document.getElementById('logsConsole').innerHTML = '';
            });
      
            // Start Build
            document.getElementById('startBuildBtn').addEventListener('click', startBuildingExtensions);
      
            // Download backup JSON
            document.getElementById('downloadZipBtn').addEventListener('click', () => {
              const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(State.generatedPlan, null, 2));
              const dlAnchor = document.createElement('a');
              dlAnchor.setAttribute("href", dataStr);
              dlAnchor.setAttribute("download", `yemot_shas_plan_${Date.now()}.json`);
              document.body.appendChild(dlAnchor);
              dlAnchor.click();
              dlAnchor.remove();
              log('INFO', 'קובץ תוכנית השלוחות הורד למחשב המשתמש');
            });
      
            // View Session Details Modal / Toast
            document.getElementById('viewSessionBtn').addEventListener('click', async () => {
              const token = document.getElementById('apiTokenInput').value.trim();
              if (!token) return alert('נא להזין טוקן תחילה');
              try {
                const res = await YemotAPI.getSession();
                alert(`פרטי מערכת:\nמספר: ${res.username || 'לא זמין'}\nשם: ${res.name || 'לא זמין'}\nיתרת יחידות: ${res.units || 0}\nתוקף יחידות: ${res.unitsExpireDate || 'ללא תפוגה'}`);
              } catch (e) {
                alert(`שגיאה בקבלת פרטים: ${e.message}`);
              }
            });
      
            // Initial generation & render
            generateFullIvrPlan();
            renderTree();
            Simulator.init();
            log('INFO', 'מערכת בניית שלוחות ש"ס מוכנה לפעולה');
          }
      
          // Startup
          window.addEventListener('DOMContentLoaded', initUI);
        </script>
      </body>
      </html>```
      פורסם בהסברים מסודרים ממשתמשים
      ל
      לומד''עס
    • RE: שליחת מספר אל הצג

      @R-E-T-מערכות אתה בנית אותה מאפס?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ל
      לומד''עס
    • RE: שליחת מספר אל הצג

      @איל-משולש בגדול כל בעל מרכזיה יכול לעשות את זה

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ל
      לומד''עס
    • RE: שיתוף מי זה פון חדש עם מאגר עדכני של עשרות אלפי מספרים

      @kol כתב בשיתוף מי זה פון חדש עם מאגר עדכני של עשרות אלפי מספרים:

      מי זה פון הוותיק 0799300400 חזר לפעול

      לא באותה צורה עדין אי"א לשמוע מי חיפש אותך

      פורסם בטיפים עצות והדגמות מהמשתמשים
      ל
      לומד''עס
    • שיתוף מי זה פון חדש עם מאגר עדכני של עשרות אלפי מספרים

      מועדים לשמחה
      לאחר תקלות חוזרות ונשנות במי זה פון הקיים
      ולאחר בקשות רבות
      בס''ד עלה בידי לפתח מי זה פון ממש דומה ומקביל למי זה פון הקודם שהיה

      המעלות בו הם כדלהלן

      • מאגר ענק ועדכני

      • קריינות מעולה

      • שמיעת איות השם בצורה תקינה

      • אפשרות לשנות את השם מכפי שמופיע במערכת

      • בדיקת כמות הפעמים שחיפשו את המספר שלך

      • שמיעה חוזרת של השם

      • מספר פשוט וקליט

      0799-400-900

      המערכת עברה בדיקת תקינות וכרגע היא פעילה ומצוינת אשמח להערות הארות

      פורסם בטיפים עצות והדגמות מהמשתמשים
      ל
      לומד''עס
    • השמעת כמה זמן נותר עד תאריך ושעה מסויימים

      יש אופציה כזאת ?

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • שמיעת הערך הישן לפני מחיקת החדש

      תגובה: שלוחה להוספת שם לקובץ EnterIDValName בהקשה

      כאן

      https://f2.freeivr.co.il/post/101671

      עדין לא משמיע את הערך הישן עובר ישר לכתיבת הערך החדש

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • RE: היכן ניתן להשיג את הטופס מימות בפנייה לוועדת הרבנים - לפתיחת מספר?

      @פלוס כתב בהיכן ניתן להשיג את הטופס מימות בפנייה לוועדת הרבנים - לפתיחת מספר?:

      אשמח אם יעלוהו כאן או יתנו קישור,
      תודה רבה.

      אוכל לפתוח לך את המספר בלי כל התהליך תשלח פרטים למייל

      3636.bl@gmail.com

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • RE: אימות מספר טלפון ע"י שיחה

      @10110000 כתב באימות מספר טלפון ע"י שיחה:

      אני בונה אתר הצורך באימות טלפון (כלומר שיתקשרו אליו ויגידו לו קוד והוא יצרך לכתוב אותו באתר).
      עם איזה שירות אני צריך להשתמש? הודעה קולית? ואז להשתמש עם הAPI ו פקודת sendtts?
      תודה מראש

      תבקש מימות יש להם שירות כזה מובנה

      פורסם בשאלות ועזרה הדדית
      ל
      לומד''עס
    • RE: יציאת ומניעת כניסה לרשימה X אחר הצטרפות לרשימה Y

      @יצחק-SH זה לא עוזר לך
      https://f2.freeivr.co.il/topic/94

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ל
      לומד''עס