• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות
    1. דף הבית
    2. הדיבל המעופף
    ה מנותק
    • פרופיל
    • עוקב אחרי 0
    • עוקבים 4
    • נושאים 39
    • פוסטים 888
    • קבוצות 0

    הדיבל המעופף

    @הדיבל המעופף

    262
    מוניטין
    209
    צפיות בפרופיל
    888
    פוסטים
    4
    עוקבים
    0
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה
    מיקום ניתן להשאיר הודעה כאן https://ha-diyal.a775529301.workers.dev/

    הדיבל המעופף הפסקת מעקב מעקב

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

    • מודל הגדרת שלוחות דרך הטלפון !!! כולל שאלות תתי שאלות ועוד !!!!

      ההגדרות בשלוחה

      type=api
      api_link=https://configuring-extensions.a775529301.workers.dev/
      api_add_0=token=טוקן
      api_hangup_send=no
      

      ‏‏ממשק ההגדרות בשלוחה למחוק "מיותר"

      תגובות פה

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

      api_add_X=token=[טוקן]
      

      המודל ממיר את" X" אוטומטית למספר סידורי (מה שנחוץ במיוחד לתתי שאלות)

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

      קרדיט ל @אריה-ai על היוזמה !!!

      הסבר AI על המודל

      שלוחת API להגדרת שלוחות בקו אחר – ישירות מהטלפון

      מה זה?

      שלוחת API (Cloudflare Worker) שמאפשרת להגדיר שלוחות בכל קו בימות המשיח, רק על ידי חיוג לשלוחה אחת. המאזין מקיש את פרטי הקו, בוחר את השלוחה, עונה על כמה שאלות, וההגדרות נכתבות לקובץ ext.ini של השלוחה. אין צורך להיכנס לממשק הניהול.

      את השאלות ואת ההגדרות שנכתבות אתם קובעים בעצמכם, בקובץ אחד בשם Settings.ini. כך אפשר להקים שלוחת "אשף הגדרות" לכל סוג שלוחה שרוצים, למשל שלוחת API עם כתובת וטוקן מוכנים מראש.

      איך זה עובד

      1. המאזין מקיש מספר קו וסיסמת ניהול. הוורקר מרכיב מהם טוקן במבנה 077222770:123456 ובודק אותו מול ימות.
      2. המאזין מקיש מספר שלוחה. כוכבית מומרת לסלש, כי אין סלש בטלפון: 1*2 הופך ל-1/2.
      3. הוורקר קורא את Settings.ini משלוחת ה-API שלו, ומחיל קודם את הגדרות הבסיס.
      4. אחר כך הוא שואל את המאזין את השאלות, וכל תשובה מוסיפה הגדרות.
      5. בסוף יש אישור וכתיבה ל-ext.ini של השלוחה שהוקשה.
        כל הקשה מוקראת על ידי ימות עצמה (סוג Digits) ואחריה נשאלים "לאישור הקישו 1, להקשה מחדש הקישו 2". זו ברירת המחדל של ימות, והוורקר לא משמיע את ההקשות בעצמו.

      התקנה

      שלב 1: יוצרים שלוחת API בקו שלך

      ב-ext.ini של השלוחה:

      type=api
      api_link=https://<השם-שלך>.workers.dev
      api_add_0=token=<קו-שלך>:<סיסמת-ניהול-שלך>
      api_add_1=key=<אותו-ערך-כמו-ACCESS_KEY>
      api_log=no
      api_hangup_send=no
      

      api_log=no חשוב. בלעדיו הסיסמאות שמוקשות נשמרות בלוג של ימות.

      שלב 2: מעלים Settings.ini לאותה שלוחה

      אפשר להעלות ידנית, או לערוך בעורך החזותי שמצורף (פירוט בהמשך).

      מבנה Settings.ini

      הגדרות בסיס

      נכתבות תמיד לשלוחה שהוקשה, לפי סדר המספרים:

      בסיס_0= type=api
      בסיס_1= api_link=https://listener-management.a775529301.workers.dev
      בסיס_2= api_add_X=token=[טוקן]
      

      שאלות ותשובות

      כל שאלה נוסחה כהוראה למאזין, ולכל תשובה מגדירים מה ייכתב:

      שאלה_1=לשליחה בשיטת פוסט הקישו 1, לגט הקישו 2, להשארת ברירת המחדל הקישו 3
      שאלה_1_1= api_url_post=yes
      שאלה_1_2= api_url_post=no
      שאלה_1_3= כלום
      
      • כלום אומר שלא מוסיפים שום הגדרה.
      • אפשר לכתוב כמה שורות באותו מפתח, ואז כל התשובה מוסיפה כמה הגדרות.

      תתי-שאלות

      תת-שאלה נשאלת רק אם נבחרה תשובה מסוימת:

      תת_1_1_שאלה_1=להוספת מזהה המתקשר הקישו 1, לא להוסיף הקישו 2
      תת_1_1_שאלה_1_1= api_add_X=phone=yes
      תת_1_1_שאלה_1_2= כלום
      

      תת_1_1_ אומר: ענו 1 על שאלה 1. ואפשר להמשיך לרמה נוספת, למשל תת_1_1_1_שאלה_1.

      תגיות בתוך ההגדרות

      תגית מה היא עושה
      [טוקן] מוצבת בה הטוקן שהוקש, למשל 077222770:123456
      [הקשה=אנא הקישו את השלוחה] הוורקר מקריא את הטקסט, וההקשה נכנסת להגדרה. כוכבית הופכת לסלש
      [קו], [סיסמה], [שלוחה] הקו, הסיסמה והשלוחה שהוקשו

      התגיות עובדות גם בהגדרות בסיס וגם בתוך תשובות.

      מספור סדרתי

      מודול ה-API דורש הגדרות סדרתיות (api_add_0, api_add_1…), וכשיש שאלות לא ידוע מראש כמה הגדרות ייכתבו. לכן כותבים api_add_X, והוורקר ממספר אותן לפי הסדר שבו הן נכנסו בפועל. גם api_add_0 עם מספר קבוע ימוספר מחדש. אותו דבר חל על api_X, שמקבל מספר בן שלוש ספרות.

      הגדרות כלליות (רשות)

      כללי_אישור=לא            # בלי אישור סופי לפני השמירה
      כללי_אישור_הקשות=לא      # בלי בקשת אישור על כל הקשה בשאלות ובערכי [הקשה=...]
      כללי_דריסה=לא | כן | שאלה
      

      מיזוג או דריסה

      כשהשלוחה כבר קיימת יש שתי אפשרויות:

      • מיזוג (ברירת מחדל): מעדכנים רק את ההגדרות החדשות ושומרים את השאר. ה-api_add הישנים נמחקים כדי שלא יישארו "זנבות" שמפרים את הרצף.
      • דריסה: כל ה-ext.ini נמחק ונכתב מחדש עם ההגדרות החדשות בלבד. קבצים אחרים בשלוחה לא נפגעים.
        עם כללי_דריסה=שאלה, המאזין בוחר בעצמו בטלפון. במצב דריסה האישור הסופי מזהיר אותו שכל ההגדרות הקיימות יימחקו.

      עורך חזותי ל-Settings.ini

      קיים גם דף HTML שמתחבר ישירות לימות וקורא ושומר את Settings.ini:

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

      אבטחה

      • הוורקר מקבל טוקן של קו אחר, ולכן כדאי להגדיר ACCESS_KEY כדי שרק השלוחה שלך תוכל לקרוא לו.
      • אחרי 3 ניסיונות כושלים השיחה מתנתקת.
      • הסיסמה לא נשמרת בשום מקום בוורקר. api_log=no מונע שמירה שלה בלוג של ימות.
      • הסיסמה מוקראת בקול כמו שאר ההקשות. להשתקה: api_add_X=pass_readback=no.

      מגבלות

      • הסיסמה נקלטת כספרות בלבד.
      • בקו עם אימות דו-שלבי ההתחברות בשיטת קו:סיסמה עלולה להיחסם.
      • אחרי שמירה מוצלחת השיחה מתנתקת, כדי להימנע מנתונים ישנים שנשארים בשיחה.
      פורסם בהסברים מסודרים ממשתמשים
      ה
      הדיבל המעופף
    • קבלת נתונים בסיסית בAPI עם תמלול חינם - גירסת בטא

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

      אז אם אתם חושבים שזה דבר נחוץ כתבו פה

      גירסת בטא למטה

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • עוזר AI מדויק עבור ימות המשיח! בדף html! גירסה 2

      עוזר AI מדויק עבור ימות המשיח! גירסה 2 !!ממשק משופר ואפשרות לשליחת רק חלק מההגדרות

      עדכון: ניתן לבחור שלוחות מסוימות שישלחו לגמיניי, וכך גם חוסכים טוקנים, וגם גמיניי הרבה יותר ממוקד!!!

      קרדיט ענק ל @perfect על קובץ הזיפ המסודר!!!

      הדף עובד על מפתח API גמיני שבכל שיחה שולח קובץ זיפ עם כל הגדרות המערכת!

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

      קוד דף הhtml המעודכן !! בספוילר

      [4aa0e732-fa68-443f-9c42-b120b1dd5273-מומחה לימות.html](/assets/uploads/files/1786649972515-4aa0e732-fa68-443f-9c42-b120b1dd5273-מומחה-לימות.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>מומחה AI ימות המשיח - סביבת פיתוח מתקדמת</title>
          <!-- Fonts & Icons -->
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
          <link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
          <!-- JSZip for dynamic zip extraction and folder repackaging -->
          <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
          <!-- Marked.js for Markdown -->
          <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
          <!-- Highlight.js for Syntax Highlighting -->
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/tokyo-night-dark.min.css">
          <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/ini.min.js"></script>
      
          <style>
              /* Theme Palettes */
              :root, body[data-theme="cyberpunk"] {
                  --bg-dark: #070913;
                  --bg-gradient: radial-gradient(circle at 50% -20%, #1e1b4b 0%, #0c1022 50%, #070913 100%);
                  --panel-bg: rgba(18, 24, 43, 0.7);
                  --panel-border: rgba(99, 102, 241, 0.18);
                  --panel-border-hover: rgba(99, 102, 241, 0.4);
                  --user-msg: linear-gradient(135deg, #2563eb, #1d4ed8);
                  --ai-msg: rgba(26, 34, 56, 0.75);
                  --accent-glow: #38bdf8;
                  --accent-blue: #3b82f6;
                  --accent-purple: #818cf8;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.2);
                  --text-main: #f8fafc;
                  --text-muted: #94a3b8;
                  --code-bg: #0d1322;
                  --analysis-bg: rgba(28, 25, 68, 0.7);
                  --analysis-border: #6366f1;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.35);
              }
      
              body[data-theme="obsidian"] {
                  --bg-dark: #000000;
                  --bg-gradient: radial-gradient(circle at 50% 0%, #141414 0%, #000000 80%);
                  --panel-bg: rgba(18, 18, 18, 0.85);
                  --panel-border: #262626;
                  --panel-border-hover: #404040;
                  --user-msg: linear-gradient(135deg, #27272a, #18181b);
                  --ai-msg: rgba(20, 20, 20, 0.85);
                  --accent-glow: #10b981;
                  --accent-blue: #10b981;
                  --accent-purple: #059669;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.15);
                  --text-main: #ffffff;
                  --text-muted: #a3a3a3;
                  --code-bg: #080808;
                  --analysis-bg: #121212;
                  --analysis-border: #2e2e2e;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.7);
              }
      
              body[data-theme="espresso"] {
                  --bg-dark: #140d08;
                  --bg-gradient: radial-gradient(circle at 50% -10%, #382417 0%, #1c120b 60%, #140d08 100%);
                  --panel-bg: rgba(38, 25, 17, 0.8);
                  --panel-border: rgba(217, 119, 6, 0.2);
                  --panel-border-hover: rgba(217, 119, 6, 0.45);
                  --user-msg: linear-gradient(135deg, #b45309, #78350f);
                  --ai-msg: rgba(48, 31, 22, 0.8);
                  --accent-glow: #fcd34d;
                  --accent-blue: #d97706;
                  --accent-purple: #b45309;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.2);
                  --text-main: #fffbeb;
                  --text-muted: #d6b89e;
                  --code-bg: #1b1008;
                  --analysis-bg: rgba(69, 42, 28, 0.7);
                  --analysis-border: #d97706;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.5);
              }
      
              body[data-theme="daylight"] {
                  --bg-dark: #f8fafc;
                  --bg-gradient: radial-gradient(circle at 50% -20%, #e0e7ff 0%, #f1f5f9 60%, #f8fafc 100%);
                  --panel-bg: rgba(255, 255, 255, 0.85);
                  --panel-border: #cbd5e1;
                  --panel-border-hover: #94a3b8;
                  --user-msg: linear-gradient(135deg, #2563eb, #1d4ed8);
                  --ai-msg: rgba(241, 245, 249, 0.9);
                  --accent-glow: #2563eb;
                  --accent-blue: #2563eb;
                  --accent-purple: #4f46e5;
                  --success: #059669;
                  --success-glow: rgba(5, 150, 105, 0.15);
                  --text-main: #0f172a;
                  --text-muted: #64748b;
                  --code-bg: #ffffff;
                  --analysis-bg: #eef2ff;
                  --analysis-border: #818cf8;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.06);
              }
      
              /* Font Sizes */
              body[data-font="small"] { font-size: 14px; }
              body[data-font="medium"] { font-size: 15.5px; }
              body[data-font="large"] { font-size: 17.5px; }
              body[data-font="huge"] { font-size: 19.5px; }
      
              * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Rubik', sans-serif; }
              body { background-color: var(--bg-dark); color: var(--text-main); height: 100vh; display: flex; flex-direction: column; overflow: hidden; background-image: var(--bg-gradient); transition: background 0.3s ease, color 0.3s ease; }
      
              /* Custom Scrollbar */
              ::-webkit-scrollbar { width: 6px; height: 6px; }
              ::-webkit-scrollbar-track { background: transparent; }
              ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 10px; }
              ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
      
              /* Navigation Header */
              header { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  border-bottom: 1px solid var(--panel-border); 
                  padding: 10px 24px; 
                  display: flex; 
                  justify-content: space-between; 
                  align-items: center; 
                  z-index: 20; 
                  box-shadow: var(--shadow-subtle);
                  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); 
              }
      
              .logo-area { display: flex; align-items: center; gap: 16px; }
              .logo { 
                  font-size: 1.35rem; 
                  font-weight: 800; 
                  background: linear-gradient(135deg, var(--accent-glow), var(--accent-purple)); 
                  -webkit-background-clip: text; 
                  -webkit-text-fill-color: transparent; 
                  display: flex; 
                  align-items: center; 
                  gap: 10px; 
                  letter-spacing: -0.5px;
              }
      
              /* Topbar Status Badge */
              .topbar-status-badge {
                  display: inline-flex;
                  align-items: center;
                  gap: 7px;
                  padding: 4px 12px;
                  border-radius: 20px;
                  font-size: 0.78rem;
                  font-weight: 600;
                  background: rgba(255,255,255,0.04);
                  border: 1px solid var(--panel-border);
                  color: var(--text-muted);
              }
              .topbar-status-badge.active {
                  background: var(--success-glow);
                  border-color: rgba(16, 185, 129, 0.4);
                  color: var(--success);
              }
              .status-dot { width: 7px; height: 7px; border-radius: 50%; background: #64748b; }
              .topbar-status-badge.active .status-dot { background: var(--success); box-shadow: 0 0 8px var(--success); animation: pulse-dot 2s infinite; }
      
              @keyframes pulse-dot { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.3); opacity: 0.7; } }
      
              .header-actions { display: flex; align-items: center; gap: 10px; }
              .tabs { display: flex; gap: 8px; }
              .tab-btn { 
                  background: rgba(255,255,255,0.03); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  padding: 7px 16px; 
                  border-radius: 10px; 
                  cursor: pointer; 
                  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); 
                  font-size: 0.9rem; 
                  font-weight: 600; 
                  display: flex; 
                  align-items: center; 
                  gap: 8px; 
              }
              .tab-btn.active { 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border-color: transparent; 
                  color: #fff; 
                  box-shadow: 0 4px 15px rgba(59, 130, 246, 0.35); 
              }
              .tab-btn:hover:not(.active) { background: rgba(255,255,255,0.08); border-color: var(--panel-border-hover); }
      
              /* Workspace Grid */
              main { flex: 1; overflow: hidden; position: relative; }
              .tab-content { display: none; height: 100%; padding: 16px 20px; }
              .tab-content.active { display: flex; gap: 16px; }
      
              /* Sidebar Sessions */
              .history-sidebar { 
                  width: 280px; 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  border: 1px solid var(--panel-border); 
                  border-radius: 18px; 
                  padding: 16px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 12px; 
                  box-shadow: var(--shadow-subtle);
                  transition: all 0.3s ease; 
              }
              .new-chat-btn { 
                  width: 100%; 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border: none; 
                  color: white; 
                  padding: 11px 16px; 
                  border-radius: 12px; 
                  cursor: pointer; 
                  font-weight: 700; 
                  font-size: 0.92rem; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
                  gap: 8px; 
                  transition: all 0.2s ease; 
                  box-shadow: 0 4px 14px rgba(59, 130, 246, 0.3); 
              }
              .new-chat-btn:hover { opacity: 0.95; transform: translateY(-1.5px); box-shadow: 0 6px 18px rgba(59, 130, 246, 0.45); }
              
              .history-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; padding-left: 4px; }
              .history-item { 
                  background: rgba(255,255,255,0.02); 
                  border: 1px solid transparent; 
                  padding: 9px 12px; 
                  border-radius: 10px; 
                  cursor: pointer; 
                  transition: all 0.2s; 
                  display: flex; 
                  justify-content: space-between; 
                  align-items: center; 
                  font-size: 0.86rem; 
                  color: var(--text-muted); 
              }
              .history-item:hover, .history-item.active { 
                  background: rgba(99, 102, 241, 0.12); 
                  border-color: rgba(99, 102, 241, 0.3); 
                  color: var(--text-main); 
              }
              .history-item .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
              .history-item .delete-btn { opacity: 0; transition: opacity 0.2s; color: #ef4444; padding: 2px 6px; }
              .history-item:hover .delete-btn { opacity: 1; }
      
              /* Main Chat Window */
              .chat-main { flex: 1; display: flex; flex-direction: column; height: 100%; position: relative; }
              .chat-container { 
                  flex: 1; 
                  overflow-y: auto; 
                  padding: 10px 10px 30px 10px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 18px; 
                  scroll-behavior: smooth; 
              }
              .message { 
                  max-width: 85%; 
                  padding: 16px 22px; 
                  border-radius: 18px; 
                  line-height: 1.7; 
                  word-wrap: break-word; 
                  box-shadow: var(--shadow-subtle); 
                  backdrop-filter: blur(14px); 
                  animation: messageIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);
              }
      
              @keyframes messageIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
      
              .message.user { align-self: flex-start; background: var(--user-msg); color: white; border-bottom-right-radius: 3px; }
              .message.model { align-self: flex-end; background: var(--ai-msg); border-bottom-left-radius: 3px; border: 1px solid var(--panel-border); }
              
              .message-attachments { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
              .msg-att-chip { background: rgba(0,0,0,0.25); border: 1px solid rgba(255,255,255,0.15); padding: 3px 10px; border-radius: 6px; font-size: 0.78rem; display: flex; align-items: center; gap: 6px; }
      
              /* Loading UI Animation */
              .loading-shimmer { display: inline-flex; align-items: center; gap: 10px; font-weight: 500; color: var(--accent-glow); }
              .loading-shimmer i { font-size: 1.1rem; animation: spin 1s linear infinite; }
              @keyframes spin { 100% { transform: rotate(360deg); } }
      
              /* Analysis Component */
              .analysis-box { background: var(--analysis-bg); border: 1px solid var(--analysis-border); border-radius: 14px; margin-bottom: 14px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.15); }
              .analysis-header { padding: 11px 16px; background: rgba(99, 102, 241, 0.22); cursor: pointer; font-weight: 700; font-size: 0.9rem; color: var(--accent-glow); display: flex; justify-content: space-between; align-items: center; user-select: none; }
              .analysis-content { padding: 16px; font-size: 0.9rem; color: var(--text-main); display: none; border-top: 1px solid var(--analysis-border); white-space: pre-wrap; line-height: 1.6; }
              .analysis-box.open .analysis-content { display: block; }
      
              /* Attachments Bar */
              .attachments-preview { display: flex; gap: 8px; flex-wrap: wrap; padding: 0 4px; }
              .att-chip { background: rgba(56, 189, 248, 0.12); border: 1px solid var(--accent-glow); padding: 5px 12px; border-radius: 8px; font-size: 0.82rem; display: flex; align-items: center; gap: 8px; color: var(--text-main); }
              .att-chip .remove-att { cursor: pointer; color: #ef4444; font-weight: bold; }
      
              /* Input Controls Area */
              .input-area { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  padding: 14px 18px; 
                  border: 1px solid var(--panel-border); 
                  border-radius: 18px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 10px; 
                  margin-top: 8px; 
                  box-shadow: var(--shadow-subtle);
              }
              .controls-row { display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem; color: var(--text-muted); flex-wrap: wrap; gap: 10px; }
              .input-wrapper { display: flex; gap: 8px; align-items: center; }
              textarea { 
                  flex: 1; 
                  background: rgba(0, 0, 0, 0.25); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  padding: 12px 16px; 
                  border-radius: 14px; 
                  resize: none; 
                  height: 60px; 
                  outline: none; 
                  font-size: 0.98rem; 
                  transition: all 0.2s ease; 
              }
              textarea:focus { border-color: var(--accent-glow); box-shadow: 0 0 12px rgba(56, 189, 248, 0.2); }
              
              .btn-action { 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border: none; 
                  color: white; 
                  padding: 0 22px; 
                  height: 60px; 
                  border-radius: 14px; 
                  cursor: pointer; 
                  font-weight: 700; 
                  font-size: 1rem; 
                  transition: all 0.2s ease; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
                  gap: 8px; 
                  box-shadow: 0 4px 14px rgba(59, 130, 246, 0.3);
              }
              .btn-action:hover { opacity: 0.95; transform: translateY(-1.5px); box-shadow: 0 6px 20px rgba(59, 130, 246, 0.45); }
              .btn-action:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
              
              .btn-icon-ctl { 
                  background: rgba(255,255,255,0.03); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  width: 60px; 
                  height: 60px; 
                  border-radius: 14px; 
                  cursor: pointer; 
                  font-size: 1.2rem; 
                  transition: all 0.2s ease; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
              }
              .btn-icon-ctl:hover { background: rgba(255,255,255,0.08); border-color: var(--accent-glow); transform: translateY(-1px); }
              .btn-icon-ctl.recording { background: #ef4444; color: white; animation: pulse 1.2s infinite; }
      
              @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } 70% { box-shadow: 0 0 0 12px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } }
      
              /* Code Block Styling */
              .code-container { position: relative; margin: 14px 0; border-radius: 12px; overflow: hidden; border: 1px solid var(--panel-border); background: var(--code-bg); box-shadow: 0 6px 20px rgba(0,0,0,0.3); }
              .code-header { background: rgba(255,255,255,0.03); padding: 9px 16px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--panel-border); font-size: 0.82rem; color: var(--text-muted); }
              .code-actions { display: flex; gap: 6px; }
              .code-btn { background: rgba(255,255,255,0.05); border: 1px solid var(--panel-border); color: var(--text-main); padding: 4px 10px; border-radius: 6px; font-size: 0.78rem; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 5px; font-weight: 500; }
              .code-btn:hover { background: var(--accent-blue); color: white; border-color: transparent; }
              pre { margin: 0 !important; }
              pre code { font-family: 'JetBrains Mono', monospace !important; font-size: 0.92rem; padding: 16px !important; line-height: 1.55; }
      
              /* Settings Panels */
              .settings-card { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  padding: 24px; 
                  border-radius: 18px; 
                  border: 1px solid var(--panel-border); 
                  margin-bottom: 18px; 
                  width: 100%; 
                  max-width: 820px; 
                  box-shadow: var(--shadow-subtle);
              }
              .settings-card h3 { margin-bottom: 14px; color: var(--accent-glow); display: flex; align-items: center; gap: 10px; font-size: 1.15rem; font-weight: 700; }
              .input-field { width: 100%; padding: 11px 16px; background: rgba(0, 0, 0, 0.25); border: 1px solid var(--panel-border); color: var(--text-main); border-radius: 10px; margin-bottom: 12px; font-size: 0.95rem; outline: none; transition: 0.2s; }
              .input-field:focus { border-color: var(--accent-glow); }
              textarea.keys-input { height: 100px; font-family: 'JetBrains Mono', monospace; direction: ltr; }
              select { background: var(--panel-bg); color: var(--text-main); border: 1px solid var(--panel-border); padding: 6px 12px; border-radius: 8px; outline: none; font-weight: 500; }
      
              /* ZIP Management & Dynamic Folder Selector */
              .zip-status-container { 
                  border: 1px dashed var(--panel-border); 
                  border-radius: 14px; 
                  padding: 20px; 
                  background: rgba(0,0,0,0.15); 
                  display: flex; 
                  flex-direction: column; 
                  gap: 14px; 
                  transition: all 0.2s ease; 
              }
              .zip-status-container.has-file { 
                  border-style: solid; 
                  border-color: rgba(16, 185, 129, 0.4); 
                  background: var(--success-glow); 
              }
              .zip-info-row { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
              .zip-info-details { display: flex; align-items: center; gap: 12px; }
              .zip-icon { font-size: 2rem; color: var(--success); }
              .zip-info-text .title { font-weight: 700; font-size: 0.98rem; }
              .zip-info-text .sub { font-size: 0.82rem; color: var(--text-muted); }
              .zip-actions { display: flex; gap: 8px; }
      
              /* Folder Picker Grid in Settings */
              .folder-picker-section { border-top: 1px solid var(--panel-border); padding-top: 14px; }
              .folder-picker-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
              .folder-picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; max-height: 180px; overflow-y: auto; padding: 4px; }
              .folder-checkbox-item { background: rgba(0,0,0,0.25); border: 1px solid var(--panel-border); padding: 7px 12px; border-radius: 8px; display: flex; align-items: center; gap: 8px; font-size: 0.86rem; cursor: pointer; user-select: none; transition: 0.2s; }
              .folder-checkbox-item:hover { border-color: var(--accent-glow); background: rgba(56, 189, 248, 0.08); }
              .folder-checkbox-item input { cursor: pointer; }
      
              /* Zen Mode */
              body.zen-mode header { transform: translateY(-100%); height: 0; padding: 0; overflow: hidden; }
              body.zen-mode .history-sidebar { display: none; }
              body.zen-mode .tab-content { padding: 10px 14%; }
              .exit-zen-btn { display: none; position: fixed; top: 16px; left: 16px; background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; padding: 9px 16px; border-radius: 10px; cursor: pointer; z-index: 1000; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.5); }
              body.zen-mode .exit-zen-btn { display: flex; align-items: center; gap: 6px; }
      
              /* Jump Button */
              .jump-bottom-btn { position: absolute; bottom: 130px; left: 50%; transform: translateX(-50%) translateY(20px); background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; padding: 8px 18px; border-radius: 20px; cursor: pointer; font-size: 0.84rem; font-weight: bold; box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4); display: flex; align-items: center; gap: 8px; opacity: 0; pointer-events: none; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); z-index: 50; }
              .jump-bottom-btn.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
              .jump-bottom-btn:hover { transform: translateX(-50%) scale(1.05); }
          </style>
      </head>
      <body data-theme="cyberpunk" data-font="medium">
      
          <!-- Exit Zen Mode Button -->
          <button class="exit-zen-btn" onclick="toggleZenMode()"><i class="fa-solid fa-compress"></i> צא ממצב פוקוס</button>
      
          <header>
              <div class="logo-area">
                  <div class="logo">
                      <i class="fa-solid fa-headset"></i>
                      <span>מומחה AI ימות המשיח</span>
                  </div>
                  <!-- Live Topbar Context Indicator -->
                  <div class="topbar-status-badge" id="topbarZipBadge" title="סטטוס קובץ תיעוד ימות המשיח">
                      <div class="status-dot"></div>
                      <span id="topbarZipText">בודק תיעוד...</span>
                  </div>
              </div>
      
              <div class="header-actions">
                  <button class="tab-btn" onclick="toggleZenMode()" title="מצב פוקוס נקי (Zen Mode)"><i class="fa-solid fa-expand"></i> פוקוס</button>
                  <div class="tabs">
                      <button class="tab-btn active" onclick="switchTab('chatTab', event)"><i class="fa-solid fa-comments"></i> צ'אט וסביבת עבודה</button>
                      <button class="tab-btn" onclick="switchTab('settingsTab', event)"><i class="fa-solid fa-sliders"></i> הגדרות ותיעוד</button>
                  </div>
              </div>
          </header>
      
          <main>
              <!-- Chat Tab with History Sidebar -->
              <div id="chatTab" class="tab-content active">
                  <div class="history-sidebar">
                      <button class="new-chat-btn" onclick="createNewSession()"><i class="fa-solid fa-plus"></i> שיחה חדשה</button>
                      <div style="font-size: 0.78rem; color: var(--text-muted); font-weight: 700; margin-top: 6px; letter-spacing: 0.5px;">שיחות אחרונות</div>
                      <div class="history-list" id="historyList"></div>
                  </div>
      
                  <div class="chat-main">
                      <div class="chat-container" id="chatContainer" onscroll="handleChatScroll()">
                          <div class="message model">
                              שלום! אני עוזר ה-AI הבכיר שלך לבניית מרכזיות, שלוחות ו-Webhooks ב'ימות המשיח'.<br>
                              כעת ניתן <b>לבחור תיקיות ספציפיות</b> מתוך קובץ ה-ZIP של התיעוד לשליחה ממוקדת ומהירה!
                          </div>
                      </div>
                      
                      <button class="jump-bottom-btn" id="jumpBottomBtn" onclick="scrollToBottom(true)">
                          <i class="fa-solid fa-arrow-down"></i> קפוץ לקוד האחרון / למטה
                      </button>
      
                      <div class="input-area">
                          <div class="attachments-preview" id="attachmentsPreview"></div>
      
                          <div class="controls-row">
                              <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
                                  <div>
                                      <i class="fa-solid fa-robot"></i> מודל:
                                      <select id="modelSelect"></select>
                                  </div>
      
                                  <!-- Fast In-Chat Folder Scope Selector -->
                                  <div id="chatFolderScopeContainer" style="display: none;">
                                      <i class="fa-solid fa-folder-tree"></i> תיקיית תיעוד:
                                      <select id="chatFolderSelect" onchange="syncSelectedFolderFromChat(this.value)"></select>
                                  </div>
                              </div>
      
                              <div>
                                  <label style="cursor: pointer;">
                                      <input type="checkbox" id="deepAnalysisToggle" checked> דרוש ניתוח מעמיק (&lt;analysis&gt;)
                                  </label>
                              </div>
                          </div>
                          <div class="input-wrapper">
                              <input type="file" id="genericFileInput" multiple style="display: none;" onchange="handleGenericFileUpload(event)">
                              <button class="btn-icon-ctl" onclick="document.getElementById('genericFileInput').click()" title="צרף קבצים (קוד, תמונות, שמע, PDF, ZIP...)">
                                  <i class="fa-solid fa-paperclip"></i>
                              </button>
      
                              <button class="btn-icon-ctl" id="micBtn" onclick="toggleVoiceInput()" title="הקלטה קולית בעברית">
                                  <i class="fa-solid fa-microphone"></i>
                              </button>
                              
                              <textarea id="userInput" placeholder="תאר את השלוחה או שאל שאלה (ניתן לגרור קבצים לכאן)..." onkeydown="handleKeyDown(event)"></textarea>
                              <button class="btn-action" id="sendBtn" onclick="sendMessage()"><i class="fa-solid fa-paper-plane"></i> שלח</button>
                          </div>
                      </div>
                  </div>
              </div>
      
              <!-- Settings & UI Customization Tab -->
              <div id="settingsTab" class="tab-content" style="flex-direction: column; overflow-y: auto;">
                  
                  <!-- Dynamic ZIP & Folder Management Card -->
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-file-zipper"></i> תיעוד ימות המשיח ובחירת תיקיות (Folder Filtering)</h3>
                      <p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 14px;">קובץ ה-ZIP של התיעוד מנותח אוטומטית. תוכל לבחור לשלוח את כל התיעוד או רק תיקיות ספציפיות לקבלת מענה ממוקד ומהיר.</p>
                      
                      <input type="file" id="zipFileInput" accept=".zip" style="display: none;" onchange="handleZipUpload(event)">
                      
                      <div class="zip-status-container" id="zipStatusBox"></div>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-key"></i> שרשור מפתחות API (Gemini Key Rotation)</h3>
                      <textarea id="apiKeysInput" class="input-field keys-input" placeholder="הכנס מפתחות API (מפתח בכל שורה)..."></textarea>
                      <button class="tab-btn active" onclick="saveKeys()"><i class="fa-solid fa-floppy-disk"></i> שמור מפתחות וטען מודלים</button>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-palette"></i> התאמת מראה וסביבת עבודה (Themes & UI)</h3>
                      <div style="display: flex; gap: 16px; flex-wrap: wrap;">
                          <div style="flex: 1;">
                              <label style="display: block; margin-bottom: 6px; color: var(--text-muted); font-size: 0.88rem;">ערכת נושא (Theme):</label>
                              <select id="themeSelect" class="input-field" onchange="changeTheme(this.value)">
                                  <option value="cyberpunk">🌌 Midnight Cyberpunk (ניאון כהה)</option>
                                  <option value="obsidian">🖤 Obsidian Pure Black (שחור מוחלט)</option>
                                  <option value="espresso">☕ Warm Espresso (חום קפה חמים)</option>
                                  <option value="daylight">☀️ Clean Daylight (מצב יום בהיר)</option>
                              </select>
                          </div>
                          <div style="flex: 1;">
                              <label style="display: block; margin-bottom: 6px; color: var(--text-muted); font-size: 0.88rem;">גודל גופן (Font Size):</label>
                              <select id="fontSelect" class="input-field" onchange="changeFontSize(this.value)">
                                  <option value="small">קטן (14px)</option>
                                  <option value="medium" selected>בינוני (15.5px)</option>
                                  <option value="large">גדול (17.5px)</option>
                                  <option value="huge">ענק (19.5px)</option>
                              </select>
                          </div>
                      </div>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-cloud-arrow-up"></i> חיבור ישיר ל-API ימות המשיח</h3>
                      <input type="text" id="ymUsername" class="input-field" placeholder="מספר מערכת (System Username)">
                      <input type="password" id="ymPassword" class="input-field" placeholder="סיסמת מערכת (System Password)">
                      <button class="tab-btn active" onclick="saveYemotCredentials()"><i class="fa-solid fa-link"></i> שמור פרטי התחברות למערכת</button>
                  </div>
              </div>
          </main>
      
          <script>
              const SYSTEM_INSTRUCTION = `אתה מתכנת בכיר ומומחה לשלוחות, מרכזיות ופיתוח ב'ימות המשיח'. תפקידך לבנות קובצי ext.ini מדויקים, תפריטי IVR, ושרתי Webhook.
      לרשותך מצורף קובץ ZIP המכיל את התיעוד וההגדרות של ימות המשיח (בהתאם לתיקיות שנבחרו), לצד כל קובץ שהמשתמש מעלה ומצרף. עליך להתבסס על התיעוד באופן מוחלט בכל תשובה.
      
      הנחיות חובה:
      1. אם המשתמש ביקש ניתוח מעמיק (או כברירת מחדל), עליך לפתוח את תגובתך בניתוח מקיף ומעמיק של הדרישה, הפורמט, הלוגיקה, והפרמטרים הנדרשים עטוף כולו בתוך תגיות <analysis> ו-</analysis>.
      2. רק לאחר התגית הסוגרת </analysis> תציג את התשובה והקוד המלאים והסופיים.
      3. הקפד על כתיבת קוד ext.ini תקני, כולל הערות והסברים ברורים בעברית.`;
      
              const LOCAL_STORAGE_KEYS = {
                  API_KEYS: 'yemot_v8_gemini_api_keys',
                  ZIP_BASE64: 'yemot_v8_zip_base64',
                  ZIP_META: 'yemot_v8_zip_meta',
                  ZIP_FOLDERS: 'yemot_v8_zip_folders',
                  SELECTED_FOLDERS: 'yemot_v8_selected_folders',
                  SESSIONS: 'yemot_v8_chat_sessions',
                  YM_USER: 'yemot_v8_ym_username',
                  YM_PASS: 'yemot_v8_ym_password',
                  THEME: 'yemot_v8_theme',
                  FONT_SIZE: 'yemot_v8_font_size'
              };
      
              // App State
              let apiKeys = [];
              let zipBase64 = localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_BASE64) || null;
              let zipMeta = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_META) || 'null');
              let zipFolders = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS) || '[]');
              let selectedFolders = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS) || '[]');
              let sessions = [];
              let currentSessionId = null;
              let isRecording = false;
              let recognition = null;
              let pendingAttachments = [];
      
              window.onload = async () => {
                  loadThemePreferences();
                  loadKeys();
                  loadYemotCredentials();
                  initSpeechRecognition();
                  initDragAndDrop();
                  loadSessions();
                  updateZipStatusUI();
                  if (apiKeys.length > 0) await fetchModels();
              };
      
              function switchTab(tabId, event) {
                  document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
                  document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
                  document.getElementById(tabId).classList.add('active');
                  if(event) event.currentTarget.classList.add('active');
              }
      
              // --- Dynamic ZIP & Folder Analysis ---
              async function analyzeZipFolders(base64Data) {
                  try {
                      const zip = new JSZip();
                      await zip.loadAsync(base64Data, { base64: true });
                      
                      const folderSet = new Set();
                      zip.forEach((relativePath, file) => {
                          const parts = relativePath.split('/');
                          if (parts.length > 1 && parts[0].trim().length > 0) {
                              folderSet.add(parts[0]);
                          }
                      });
      
                      zipFolders = Array.from(folderSet).sort();
                      selectedFolders = [...zipFolders]; // Default: select all
                      localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS, JSON.stringify(zipFolders));
                      localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  } catch (err) {
                      console.error("Error analyzing ZIP folders:", err);
                      zipFolders = [];
                      selectedFolders = [];
                  }
              }
      
              function updateZipStatusUI() {
                  const statusBox = document.getElementById('zipStatusBox');
                  const topbarBadge = document.getElementById('topbarZipBadge');
                  const topbarText = document.getElementById('topbarZipText');
                  const chatFolderContainer = document.getElementById('chatFolderScopeContainer');
                  const chatFolderSelect = document.getElementById('chatFolderSelect');
      
                  if (zipBase64 && zipMeta) {
                      let folderCheckboxesHTML = '';
                      if (zipFolders.length > 0) {
                          folderCheckboxesHTML = `
                              <div class="folder-picker-section">
                                  <div class="folder-picker-header">
                                      <span style="font-weight: 700; font-size: 0.9rem;"><i class="fa-solid fa-folder-tree"></i> בחר תיקיות לשליחה כהקשר (${selectedFolders.length}/${zipFolders.length}):</span>
                                      <div style="display: flex; gap: 8px;">
                                          <button class="tab-btn" style="padding: 3px 10px; font-size: 0.78rem;" onclick="selectAllFolders(true)">בחר הכל</button>
                                          <button class="tab-btn" style="padding: 3px 10px; font-size: 0.78rem;" onclick="selectAllFolders(false)">נקה הכל</button>
                                      </div>
                                  </div>
                                  <div class="folder-picker-grid">
                                      ${zipFolders.map(folder => `
                                          <label class="folder-checkbox-item">
                                              <input type="checkbox" value="${escapeHtml(folder)}" ${selectedFolders.includes(folder) ? 'checked' : ''} onchange="toggleFolderSelection('${escapeHtml(folder)}')">
                                              <i class="fa-regular fa-folder" style="color: var(--accent-glow);"></i>
                                              <span>${escapeHtml(folder)}</span>
                                          </label>
                                      `).join('')}
                                  </div>
                              </div>
                          `;
                      }
      
                      statusBox.className = 'zip-status-container has-file';
                      statusBox.innerHTML = `
                          <div class="zip-info-row">
                              <div class="zip-info-details">
                                  <i class="fa-solid fa-circle-check zip-icon"></i>
                                  <div class="zip-info-text">
                                      <div class="title">קובץ תיעוד פעיל: ${escapeHtml(zipMeta.name)}</div>
                                      <div class="sub">גודל: ${zipMeta.size} | סה"כ ${zipFolders.length} תיקיות זוהו</div>
                                  </div>
                              </div>
                              <div class="zip-actions">
                                  <button class="tab-btn active" onclick="document.getElementById('zipFileInput').click()">
                                      <i class="fa-solid fa-arrows-rotate"></i> החלף ZIP
                                  </button>
                                  <button class="tab-btn" style="color: #ef4444; border-color: rgba(239, 68, 68, 0.3);" onclick="removeZipFile()">
                                      <i class="fa-solid fa-trash"></i> הסר
                                  </button>
                              </div>
                          </div>
                          ${folderCheckboxesHTML}
                      `;
      
                      topbarBadge.className = 'topbar-status-badge active';
                      const folderCountText = selectedFolders.length === zipFolders.length ? 'כל התיקיות' : `${selectedFolders.length} תיקיות`;
                      topbarText.innerText = `תיעוד פעיל (${folderCountText})`;
      
                      // Update In-Chat Quick Selector
                      if (zipFolders.length > 0) {
                          chatFolderContainer.style.display = 'block';
                          chatFolderSelect.innerHTML = `<option value="__ALL__">כל התיקיות (${zipFolders.length})</option>` + 
                              zipFolders.map(f => `<option value="${escapeHtml(f)}" ${selectedFolders.length === 1 && selectedFolders[0] === f ? 'selected' : ''}>📁 ${escapeHtml(f)}</option>`).join('');
                      } else {
                          chatFolderContainer.style.display = 'none';
                      }
      
                  } else {
                      statusBox.className = 'zip-status-container';
                      statusBox.innerHTML = `
                          <div class="zip-info-row" style="justify-content: center; text-align: center; padding: 10px 0;">
                              <div>
                                  <i class="fa-solid fa-cloud-arrow-up" style="font-size: 2.2rem; color: var(--text-muted); margin-bottom: 8px;"></i>
                                  <div style="font-weight: 700; margin-bottom: 4px;">טרם נטען קובץ ZIP של תיעוד ימות המשיח</div>
                                  <div style="font-size: 0.84rem; color: var(--text-muted); margin-bottom: 12px;">העלה את קובץ ההגדרות (~688 KB) כדי לאפשר בחירת תיקיות ממוקדת</div>
                                  <button class="tab-btn active" onclick="document.getElementById('zipFileInput').click()" style="margin: 0 auto;">
                                      <i class="fa-solid fa-upload"></i> בחר קובץ ZIP להעלאה
                                  </button>
                              </div>
                          </div>
                      `;
      
                      topbarBadge.className = 'topbar-status-badge';
                      topbarText.innerText = 'אין תיעוד ZIP טעון';
                      chatFolderContainer.style.display = 'none';
                  }
              }
      
              async function handleZipUpload(event) {
                  const file = event.target.files[0];
                  if (!file) return;
                  const reader = new FileReader();
                  reader.onload = async function(e) {
                      zipBase64 = e.target.result.split(',')[1];
                      zipMeta = {
                          name: file.name,
                          size: (file.size / 1024).toFixed(1) + ' KB'
                      };
                      
                      await analyzeZipFolders(zipBase64);
      
                      try {
                          localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_BASE64, zipBase64);
                          localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_META, JSON.stringify(zipMeta));
                      } catch (err) {
                          console.warn('Zip stored in runtime memory.');
                      }
                      updateZipStatusUI();
                  };
                  reader.readAsDataURL(file);
                  event.target.value = '';
              }
      
              function toggleFolderSelection(folder) {
                  if (selectedFolders.includes(folder)) {
                      selectedFolders = selectedFolders.filter(f => f !== folder);
                  } else {
                      selectedFolders.push(folder);
                  }
                  localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  updateZipStatusUI();
              }
      
              function selectAllFolders(selectAll) {
                  selectedFolders = selectAll ? [...zipFolders] : [];
                  localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  updateZipStatusUI();
              }
      
              function syncSelectedFolderFromChat(folderVal) {
                  if (folderVal === '__ALL__') {
                      selectedFolders = [...zipFolders];
                  } else {
                      selectedFolders = [folderVal];
                  }
                  localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  updateZipStatusUI();
              }
      
              function removeZipFile() {
                  if (!confirm('האם להסיר את קובץ ה-ZIP של התיעוד?')) return;
                  zipBase64 = null;
                  zipMeta = null;
                  zipFolders = [];
                  selectedFolders = [];
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_BASE64);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_META);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS);
                  updateZipStatusUI();
              }
      
              // --- Generate Filtered ZIP Payload On-The-Fly ---
              async function getFilteredZipBase64() {
                  if (!zipBase64) return null;
                  if (zipFolders.length === 0 || selectedFolders.length === zipFolders.length) {
                      return zipBase64; // Send full ZIP
                  }
                  if (selectedFolders.length === 0) {
                      return null; // No folders selected
                  }
      
                  try {
                      const sourceZip = new JSZip();
                      await sourceZip.loadAsync(zipBase64, { base64: true });
      
                      const filteredZip = new JSZip();
                      const promises = [];
      
                      sourceZip.forEach((relativePath, file) => {
                          const topFolder = relativePath.split('/')[0];
                          if (selectedFolders.includes(topFolder) && !file.dir) {
                              const p = file.async('uint8array').then(content => {
                                  filteredZip.file(relativePath, content);
                              });
                              promises.push(p);
                          }
                      });
      
                      await Promise.all(promises);
                      return await filteredZip.generateAsync({ type: 'base64' });
                  } catch (err) {
                      console.error("Error generating filtered ZIP:", err);
                      return zipBase64;
                  }
              }
      
              // --- File Uploads & Drag-and-Drop ---
              function handleGenericFileUpload(event) {
                  const files = Array.from(event.target.files);
                  processFiles(files);
                  event.target.value = '';
              }
      
              function initDragAndDrop() {
                  const textarea = document.getElementById('userInput');
                  ['dragenter', 'dragover'].forEach(eventName => {
                      textarea.addEventListener(eventName, (e) => {
                          e.preventDefault();
                          textarea.style.borderColor = 'var(--accent-glow)';
                      }, false);
                  });
                  ['dragleave', 'drop'].forEach(eventName => {
                      textarea.addEventListener(eventName, (e) => {
                          e.preventDefault();
                          textarea.style.borderColor = 'var(--panel-border)';
                      }, false);
                  });
                  textarea.addEventListener('drop', (e) => {
                      const dt = e.dataTransfer;
                      const files = Array.from(dt.files);
                      if (files.length > 0) processFiles(files);
                  });
              }
      
              function processFiles(files) {
                  files.forEach(file => {
                      const reader = new FileReader();
                      reader.onload = function(e) {
                          const base64Data = e.target.result.split(',')[1];
                          let mimeType = file.type || getFallbackMime(file.name);
                          pendingAttachments.push({
                              name: file.name,
                              mimeType: mimeType,
                              data: base64Data,
                              size: (file.size / 1024).toFixed(1) + ' KB'
                          });
                          renderAttachmentsPreview();
                      };
                      reader.readAsDataURL(file);
                  });
              }
      
              function getFallbackMime(filename) {
                  const ext = filename.split('.').pop().toLowerCase();
                  const map = {
                      'ini': 'text/plain', 'txt': 'text/plain', 'wav': 'audio/wav',
                      'mp3': 'audio/mp3', 'pdf': 'application/pdf', 'csv': 'text/csv',
                      'json': 'application/json', 'zip': 'application/zip',
                      'js': 'text/javascript', 'php': 'text/x-php', 'png': 'image/png',
                      'jpg': 'image/jpeg', 'jpeg': 'image/jpeg'
                  };
                  return map[ext] || 'application/octet-stream';
              }
      
              function removeAttachment(index) {
                  pendingAttachments.splice(index, 1);
                  renderAttachmentsPreview();
              }
      
              function renderAttachmentsPreview() {
                  const previewEl = document.getElementById('attachmentsPreview');
                  previewEl.innerHTML = '';
                  pendingAttachments.forEach((att, idx) => {
                      const chip = document.createElement('div');
                      chip.className = 'att-chip';
                      chip.innerHTML = `
                          <i class="fa-solid fa-file"></i>
                          <span>${escapeHtml(att.name)} (${att.size})</span>
                          <span class="remove-att" onclick="removeAttachment(${idx})">&times;</span>
                      `;
                      previewEl.appendChild(chip);
                  });
              }
      
              // --- Zen & Themes ---
              function toggleZenMode() {
                  document.body.classList.toggle('zen-mode');
              }
      
              document.addEventListener('keydown', (e) => {
                  if (e.key === 'Escape' && document.body.classList.contains('zen-mode')) {
                      toggleZenMode();
                  }
              });
      
              function changeTheme(theme) {
                  document.body.setAttribute('data-theme', theme);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.THEME, theme);
              }
      
              function changeFontSize(size) {
                  document.body.setAttribute('data-font', size);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.FONT_SIZE, size);
              }
      
              function loadThemePreferences() {
                  const savedTheme = localStorage.getItem(LOCAL_STORAGE_KEYS.THEME) || 'cyberpunk';
                  const savedFont = localStorage.getItem(LOCAL_STORAGE_KEYS.FONT_SIZE) || 'medium';
                  changeTheme(savedTheme);
                  changeFontSize(savedFont);
                  document.getElementById('themeSelect').value = savedTheme;
                  document.getElementById('fontSelect').value = savedFont;
              }
      
              function handleChatScroll() {
                  const container = document.getElementById('chatContainer');
                  const jumpBtn = document.getElementById('jumpBottomBtn');
                  const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
                  if (distanceFromBottom > 200) {
                      jumpBtn.classList.add('show');
                  } else {
                      jumpBtn.classList.remove('show');
                  }
              }
      
              function scrollToBottom(smooth = true) {
                  const container = document.getElementById('chatContainer');
                  container.scrollTo({ top: container.scrollHeight, behavior: smooth ? 'smooth' : 'auto' });
              }
      
              // --- Session Engine ---
              function loadSessions() {
                  try {
                      const saved = localStorage.getItem(LOCAL_STORAGE_KEYS.SESSIONS);
                      sessions = saved ? JSON.parse(saved) : [];
                  } catch (e) { sessions = []; }
      
                  if (sessions.length === 0) {
                      createNewSession();
                  } else {
                      currentSessionId = sessions[0].id;
                      renderSessionsList();
                      renderCurrentSessionChat();
                  }
              }
      
              function saveSessions() {
                  try {
                      localStorage.setItem(LOCAL_STORAGE_KEYS.SESSIONS, JSON.stringify(sessions));
                  } catch (e) { console.warn('LocalStorage limit reached.'); }
                  renderSessionsList();
              }
      
              function createNewSession() {
                  const newSession = {
                      id: Date.now().toString(),
                      title: 'שיחה חדשה',
                      timestamp: new Date().toLocaleTimeString('he-IL', {hour: '2-digit', minute:'2-digit'}),
                      history: []
                  };
                  sessions.unshift(newSession);
                  currentSessionId = newSession.id;
                  saveSessions();
                  renderCurrentSessionChat();
              }
      
              function switchSession(id) {
                  currentSessionId = id;
                  renderSessionsList();
                  renderCurrentSessionChat();
              }
      
              function deleteSession(id, event) {
                  event.stopPropagation();
                  sessions = sessions.filter(s => s.id !== id);
                  if (sessions.length === 0) {
                      createNewSession();
                  } else {
                      if (currentSessionId === id) currentSessionId = sessions[0].id;
                      saveSessions();
                      renderCurrentSessionChat();
                  }
              }
      
              function renderSessionsList() {
                  const listEl = document.getElementById('historyList');
                  listEl.innerHTML = '';
                  sessions.forEach(s => {
                      const item = document.createElement('div');
                      item.className = `history-item ${s.id === currentSessionId ? 'active' : ''}`;
                      item.onclick = () => switchSession(s.id);
                      item.innerHTML = `
                          <span class="title"><i class="fa-regular fa-message"></i> ${escapeHtml(s.title)}</span>
                          <i class="fa-solid fa-trash delete-btn" onclick="deleteSession('${s.id}', event)" title="מחק שיחה"></i>
                      `;
                      listEl.appendChild(item);
                  });
              }
      
              function getCurrentSession() {
                  return sessions.find(s => s.id === currentSessionId);
              }
      
              function renderCurrentSessionChat() {
                  const container = document.getElementById('chatContainer');
                  container.innerHTML = `
                      <div class="message model">
                          שלום! אני עוזר ה-AI הבכיר שלך לבניית מרכזיות, שלוחות ו-Webhooks ב'ימות המשיח'.<br>
                          העלה קבצים, שאל שאלות, או סנן תיקיות תיעוד ספציפיות לקבלת קובצי <code>ext.ini</code> מדויקים!
                      </div>`;
      
                  const session = getCurrentSession();
                  if (!session) return;
      
                  session.history.forEach(msg => {
                      if (msg.role === 'user') {
                          appendMessageUI('user', msg.text, msg.attachedNames || [], false);
                      } else if (msg.role === 'model') {
                          renderModelResponseUI(msg.text);
                      }
                  });
                  scrollToBottom(false);
              }
      
              // --- Keys & Storage ---
              function loadKeys() {
                  const saved = localStorage.getItem(LOCAL_STORAGE_KEYS.API_KEYS);
                  if (saved) {
                      apiKeys = JSON.parse(saved);
                      document.getElementById('apiKeysInput').value = apiKeys.join('\n');
                  }
              }
              async function saveKeys() {
                  const lines = document.getElementById('apiKeysInput').value.split('\n').map(k => k.trim()).filter(k => k.length > 0);
                  apiKeys = lines;
                  localStorage.setItem(LOCAL_STORAGE_KEYS.API_KEYS, JSON.stringify(apiKeys));
                  alert('מפתחות ה-API נשמרו בהצלחה!');
                  await fetchModels();
              }
      
              function saveYemotCredentials() {
                  localStorage.setItem(LOCAL_STORAGE_KEYS.YM_USER, document.getElementById('ymUsername').value);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.YM_PASS, document.getElementById('ymPassword').value);
                  alert('פרטי החיבור נשמרו!');
              }
              function loadYemotCredentials() {
                  document.getElementById('ymUsername').value = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_USER) || '';
                  document.getElementById('ymPassword').value = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_PASS) || '';
              }
      
              // --- API Rotation & Models ---
              async function makeGeminiApiCall(fetchFunction) {
                  if (apiKeys.length === 0) {
                      alert('אנא הוסף לפחות מפתח API אחד בלשונית ההגדרות.');
                      switchTab('settingsTab');
                      throw new Error('No API keys configured');
                  }
                  let lastError = null;
                  for (let i = 0; i < apiKeys.length; i++) {
                      try {
                          return await fetchFunction(apiKeys[i]);
                      } catch (err) {
                          console.warn(`Key ${i + 1} failed, trying next...`, err);
                          lastError = err;
                      }
                  }
                  throw new Error(`כל המפתחות נכשלו: ${lastError?.message}`);
              }
      
              async function fetchModels() {
                  try {
                      const data = await makeGeminiApiCall(async (key) => {
                          const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${key}`);
                          return await res.json();
                      });
                      
                      let models = (data.models || [])
                          .filter(m => m.supportedGenerationMethods?.includes('generateContent'))
                          .map(m => m.name.replace('models/', ''));
      
                      if (!models.includes('gemini-3.6-flash')) {
                          models.unshift('gemini-3.6-flash');
                      }
      
                      const select = document.getElementById('modelSelect');
                      select.innerHTML = '';
      
                      models.forEach(m => {
                          const opt = document.createElement('option');
                          opt.value = m; opt.textContent = m;
                          select.appendChild(opt);
                      });
                      
                      select.value = 'gemini-3.6-flash';
      
                  } catch(e) { 
                      console.error(e);
                      const select = document.getElementById('modelSelect');
                      select.innerHTML = '<option value="gemini-3.6-flash">gemini-3.6-flash</option><option value="gemini-1.5-flash">gemini-1.5-flash</option>';
                      select.value = 'gemini-3.6-flash';
                  }
              }
      
              // --- Speech Input ---
              function initSpeechRecognition() {
                  if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
                      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
                      recognition = new SpeechRecognition();
                      recognition.lang = 'he-IL';
                      recognition.onresult = (event) => {
                          const transcript = event.results[0][0].transcript;
                          const inputEl = document.getElementById('userInput');
                          inputEl.value += (inputEl.value ? ' ' : '') + transcript;
                          toggleVoiceInput();
                      };
                      recognition.onerror = () => toggleVoiceInput();
                  } else {
                      document.getElementById('micBtn').style.display = 'none';
                  }
              }
      
              function toggleVoiceInput() {
                  if (!recognition) return;
                  const micBtn = document.getElementById('micBtn');
                  if (!isRecording) {
                      recognition.start();
                      isRecording = true;
                      micBtn.classList.add('recording');
                  } else {
                      recognition.stop();
                      isRecording = false;
                      micBtn.classList.remove('recording');
                  }
              }
      
              // --- Message Dispatcher with Dynamic ZIP Folder Filtering ---
              async function sendMessage() {
                  const inputEl = document.getElementById('userInput');
                  const text = inputEl.value.trim();
                  if (!text && pendingAttachments.length === 0) return;
      
                  const sendBtn = document.getElementById('sendBtn');
                  const deepAnalysis = document.getElementById('deepAnalysisToggle').checked;
                  const selectedModel = document.getElementById('modelSelect').value || 'gemini-3.6-flash';
      
                  const session = getCurrentSession();
                  if (session.history.length === 0) {
                      session.title = text ? (text.substring(0, 25) + '...') : (pendingAttachments[0]?.name || 'קובץ מצורף');
                  }
      
                  const currentTurnAttachments = [...pendingAttachments];
                  const attachedNames = currentTurnAttachments.map(a => a.name);
      
                  appendMessageUI('user', text || '(קבצים מצורפים)', attachedNames, false);
                  inputEl.value = '';
                  pendingAttachments = [];
                  renderAttachmentsPreview();
                  sendBtn.disabled = true;
                  scrollToBottom();
      
                  session.history.push({ role: 'user', text: text || '(קבצים מצורפים)', attachedNames: attachedNames });
                  saveSessions();
      
                  const loadingDiv = appendMessageUI('model', '<div class="loading-shimmer"><i class="fa-solid fa-spinner"></i> <span>אורז תיקיות נבחרות ומנתח תיעוד...</span></div>', [], true);
                  scrollToBottom();
      
                  try {
                      // Dynamically prepare filtered ZIP payload based on selected folders
                      const effectiveZipBase64 = await getFilteredZipBase64();
      
                      let contentsPayload = [];
      
                      session.history.forEach((msg, idx) => {
                          let parts = [];
                          
                          // Inject filtered ZIP documentation context on the first user turn
                          if (idx === 0 && msg.role === 'user' && effectiveZipBase64) {
                              parts.push({
                                  inline_data: {
                                      mime_type: "application/zip",
                                      data: effectiveZipBase64
                                  }
                              });
                          }
      
                          if (idx === session.history.length - 1 && msg.role === 'user' && currentTurnAttachments.length > 0) {
                              currentTurnAttachments.forEach(att => {
                                  parts.push({
                                      inline_data: {
                                      mime_type: att.mimeType,
                                          data: att.data
                                      }
                                  });
                              });
                          }
      
                          let promptText = msg.text;
                          if (idx === session.history.length - 1 && msg.role === 'user' && deepAnalysis) {
                              promptText = `[הוראת חובה לניתוח מעמיק של הפורמט]:
      לפני מתן התשובה והקוד הסופיים, עליך לבצע ניתוח מקיף ומעמיק של הדרישה, הפורמט המבוקש (כגון ext.ini, HTML, Webhook, תפריטי IVR), לוגיקת הרכיבים, בדיקת קצה ומבנה הנתונים.
      חובה לעטוף את *כל* הניתוח המעמיק בתוך תגיות <analysis> ו-</analysis>.
      רק לאחר התגית הסוגרת </analysis> תציג את התשובה והקוד המלאים והסופיים.
      
      בקשת המשתמש:
      ${msg.text}`;
                          }
      
                          parts.push({ text: promptText });
                          contentsPayload.push({ role: msg.role, parts: parts });
                      });
      
                      const responseData = await makeGeminiApiCall(async (key) => {
                          const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${key}`, {
                              method: 'POST',
                              headers: { 'Content-Type': 'application/json' },
                              body: JSON.stringify({
                                  system_instruction: { parts: [{ text: SYSTEM_INSTRUCTION }] },
                                  contents: contentsPayload
                              })
                          });
                          if (!res.ok) {
                              const errBody = await res.text();
                              throw new Error(`HTTP ${res.status}: ${errBody}`);
                          }
                          return await res.json();
                      });
      
                      const replyText = responseData?.candidates?.[0]?.content?.parts?.[0]?.text;
                      if (!replyText) throw new Error('התקבלה תשובה ריקה מהמודל.');
      
                      session.history.push({ role: 'model', text: replyText });
                      saveSessions();
      
                      loadingDiv.remove();
                      renderModelResponseUI(replyText);
                      scrollToBottom();
      
                  } catch (err) {
                      loadingDiv.innerHTML = `<span style="color:#ef4444;"><i class="fa-solid fa-circle-exclamation"></i> שגיאה: ${err.message}</span>`;
                  } finally {
                      sendBtn.disabled = false;
                  }
              }
      
              // Render AI Model Response
              function renderModelResponseUI(text) {
                  const container = document.getElementById('chatContainer');
                  const msgDiv = document.createElement('div');
                  msgDiv.className = 'message model';
      
                  let analysisHTML = '';
                  let mainContent = text;
      
                  const analysisMatch = text.match(/<analysis>([\s\S]*?)<\/analysis>/);
                  if (analysisMatch) {
                      const analysisText = analysisMatch[1].trim();
                      mainContent = text.replace(/<analysis>[\s\S]*?<\/analysis>/, '').trim();
      
                      analysisHTML = `
                          <div class="analysis-box">
                              <div class="analysis-header" onclick="this.parentElement.classList.toggle('open')">
                                  <span><i class="fa-solid fa-brain"></i> ניתוח מעמיק של הפורמט והלוגיקה (לחץ לפתיחה)</span>
                                  <i class="fa-solid fa-chevron-down"></i>
                              </div>
                              <div class="analysis-content">${escapeHtml(analysisText)}</div>
                          </div>
                      `;
                  }
      
                  msgDiv.innerHTML = analysisHTML + marked.parse(mainContent);
                  container.appendChild(msgDiv);
      
                  // Enhance Code Blocks
                  msgDiv.querySelectorAll('pre code').forEach((codeBlock) => {
                      const rawCode = codeBlock.innerText;
                      const parentPre = codeBlock.parentNode;
      
                      const wrapper = document.createElement('div');
                      wrapper.className = 'code-container';
      
                      const header = document.createElement('div');
                      header.className = 'code-header';
                      header.innerHTML = `<span><i class="fa-solid fa-code"></i> קוד תצורה (ext.ini)</span>`;
      
                      const actions = document.createElement('div');
                      actions.className = 'code-actions';
      
                      const copyBtn = document.createElement('button');
                      copyBtn.className = 'code-btn';
                      copyBtn.innerHTML = '<i class="fa-solid fa-copy"></i> העתק';
                      copyBtn.onclick = () => { navigator.clipboard.writeText(rawCode); copyBtn.innerHTML = '<i class="fa-solid fa-check"></i> הועתק!'; setTimeout(() => copyBtn.innerHTML = '<i class="fa-solid fa-copy"></i> העתק', 2000); };
      
                      const dlBtn = document.createElement('button');
                      dlBtn.className = 'code-btn';
                      dlBtn.innerHTML = '<i class="fa-solid fa-download"></i> הורד ext.ini';
                      dlBtn.onclick = () => downloadCodeFile(rawCode, 'ext.ini');
      
                      const uploadBtn = document.createElement('button');
                      uploadBtn.className = 'code-btn';
                      uploadBtn.style.color = '#10b981';
                      uploadBtn.innerHTML = '<i class="fa-solid fa-cloud-arrow-up"></i> העלה למרכזיה';
                      uploadBtn.onclick = () => uploadDirectToYemot(rawCode);
      
                      const geminiTtsBtn = document.createElement('button');
                      geminiTtsBtn.className = 'code-btn';
                      geminiTtsBtn.style.color = '#38bdf8';
                      geminiTtsBtn.innerHTML = '<i class="fa-solid fa-wand-magic-sparkles"></i> שמע Gemini AI';
                      geminiTtsBtn.onclick = () => generateGeminiAudio(rawCode);
      
                      actions.appendChild(copyBtn);
                      actions.appendChild(dlBtn);
                      actions.appendChild(geminiTtsBtn);
                      actions.appendChild(uploadBtn);
      
                      header.appendChild(actions);
                      wrapper.appendChild(header);
      
                      parentPre.parentNode.insertBefore(wrapper, parentPre);
                      wrapper.appendChild(parentPre);
      
                      hljs.highlightElement(codeBlock);
                  });
              }
      
              // Audio TTS
              async function generateGeminiAudio(code) {
                  let textToRead = prompt('הכנס את הטקסט שברצונך שה-Gemini AI יכין ויקריא עבור המרכזייה:', 'ברוכים הבאים למערכת שלנו');
                  if (!textToRead) return;
      
                  const selectedModel = document.getElementById('modelSelect').value || 'gemini-3.6-flash';
      
                  try {
                      const responseData = await makeGeminiApiCall(async (key) => {
                          const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${key}`, {
                              method: 'POST',
                              headers: { 'Content-Type': 'application/json' },
                              body: JSON.stringify({
                                  contents: [{
                                      parts: [{ text: `עבד והכשר את הניסוח הבא לקריאה קולית נקייה ומדויקת בלבד במרכזיה (ללא סימני פיסוק או הערות נוספות):\n"${textToRead}"` }]
                                  }]
                              })
                          });
                          return await res.json();
                      });
      
                      const speechText = responseData?.candidates?.[0]?.content?.parts?.[0]?.text || textToRead;
                      
                      window.speechSynthesis.cancel();
                      const utterance = new SpeechSynthesisUtterance(speechText);
                      utterance.lang = 'he-IL';
                      utterance.rate = 0.95;
                      window.speechSynthesis.speak(utterance);
      
                  } catch (err) {
                      window.speechSynthesis.cancel();
                      const utterance = new SpeechSynthesisUtterance(textToRead);
                      utterance.lang = 'he-IL';
                      window.speechSynthesis.speak(utterance);
                  }
              }
      
              // Direct Yemot Push
              async function uploadDirectToYemot(code) {
                  const username = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_USER);
                  const password = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_PASS);
      
                  if (!username || !password) {
                      alert('אנא הגדר פרטי התחברות לימות המשיח בלשונית ההגדרות.');
                      switchTab('settingsTab');
                      return;
                  }
      
                  const path = prompt('הכנס את נתיב השלוחה להעלאה (לדוגמה: 1/2 או 0 לראשית):', '1');
                  if (!path) return;
      
                  try {
                      const loginRes = await fetch(`https://www.call2all.co.il/ym/api/Login?username=${username}&password=${password}`);
                      const loginData = await loginRes.json();
      
                      if (loginData.responseStatus !== 'OK') {
                          throw new Error('התחברות למערכת ימות המשיח נכשלה.');
                      }
      
                      const token = loginData.token;
                      const formData = new FormData();
                      const blob = new Blob([code], { type: 'text/plain' });
                      formData.append('file', blob, 'ext.ini');
      
                      const uploadRes = await fetch(`https://www.call2all.co.il/ym/api/UploadFile?token=${token}&path=ivr2:${path}/ext.ini`, {
                          method: 'POST',
                          body: formData
                      });
                      const uploadData = await uploadRes.json();
      
                      if (uploadData.responseStatus === 'OK') {
                          alert(`הקוד הועלה בהצלחה לשלוחה ${path}!`);
                      } else {
                          alert(`שגיאה: ${uploadData.message || 'תקלה לא ידועה'}`);
                      }
      
                  } catch (err) {
                      alert(`שגיאה: ${err.message}`);
                  }
              }
      
              function downloadCodeFile(code, filename) {
                  const blob = new Blob([code], { type: 'text/plain;charset=utf-8' });
                  const link = document.createElement('a');
                  link.href = URL.createObjectURL(blob);
                  link.download = filename;
                  link.click();
              }
      
              function appendMessageUI(role, content, attachedNames = [], isHTML = false) {
                  const container = document.getElementById('chatContainer');
                  const msgDiv = document.createElement('div');
                  msgDiv.className = `message ${role}`;
                  
                  let attachmentsHTML = '';
                  if (attachedNames && attachedNames.length > 0) {
                      attachmentsHTML = '<div class="message-attachments">' + 
                          attachedNames.map(n => `<span class="msg-att-chip"><i class="fa-solid fa-paperclip"></i> ${escapeHtml(n)}</span>`).join('') + 
                          '</div>';
                  }
      
                  const bodyContent = isHTML ? content : `<div>${escapeHtml(content)}</div>`;
                  msgDiv.innerHTML = attachmentsHTML + bodyContent;
                  container.appendChild(msgDiv);
                  return msgDiv;
              }
      
              function handleKeyDown(e) {
                  if (e.key === 'Enter' && !e.shiftKey) {
                      e.preventDefault();
                      sendMessage();
                  }
              }
      
              function escapeHtml(text) {
                  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
              }
          </script>
      </body>
      </html>
      

      כמו כן ניתן להוריד את הקובץ יש להוסיף את האות l בסוף הסיומת
      ‏‏מומחה לימות.htm

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

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

      קוד הדף בספוילר

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>מומחה AI ימות המשיח - סביבת פיתוח מתקדמת (GAS Proxy)</title>
          <!-- Fonts & Icons -->
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
          <link href="https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
          <!-- JSZip for dynamic zip extraction and folder repackaging -->
          <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
          <!-- Marked.js for Markdown -->
          <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
          <!-- Highlight.js for Syntax Highlighting -->
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/tokyo-night-dark.min.css">
          <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/ini.min.js"></script>
      
          <style>
              /* Theme Palettes */
              :root, body[data-theme="cyberpunk"] {
                  --bg-dark: #070913;
                  --bg-gradient: radial-gradient(circle at 50% -20%, #1e1b4b 0%, #0c1022 50%, #070913 100%);
                  --panel-bg: rgba(18, 24, 43, 0.7);
                  --panel-border: rgba(99, 102, 241, 0.18);
                  --panel-border-hover: rgba(99, 102, 241, 0.4);
                  --user-msg: linear-gradient(135deg, #2563eb, #1d4ed8);
                  --ai-msg: rgba(26, 34, 56, 0.75);
                  --accent-glow: #38bdf8;
                  --accent-blue: #3b82f6;
                  --accent-purple: #818cf8;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.2);
                  --text-main: #f8fafc;
                  --text-muted: #94a3b8;
                  --code-bg: #0d1322;
                  --analysis-bg: rgba(28, 25, 68, 0.7);
                  --analysis-border: #6366f1;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.35);
              }
      
              body[data-theme="obsidian"] {
                  --bg-dark: #000000;
                  --bg-gradient: radial-gradient(circle at 50% 0%, #141414 0%, #000000 80%);
                  --panel-bg: rgba(18, 18, 18, 0.85);
                  --panel-border: #262626;
                  --panel-border-hover: #404040;
                  --user-msg: linear-gradient(135deg, #27272a, #18181b);
                  --ai-msg: rgba(20, 20, 20, 0.85);
                  --accent-glow: #10b981;
                  --accent-blue: #10b981;
                  --accent-purple: #059669;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.15);
                  --text-main: #ffffff;
                  --text-muted: #a3a3a3;
                  --code-bg: #080808;
                  --analysis-bg: #121212;
                  --analysis-border: #2e2e2e;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.7);
              }
      
              body[data-theme="espresso"] {
                  --bg-dark: #140d08;
                  --bg-gradient: radial-gradient(circle at 50% -10%, #382417 0%, #1c120b 60%, #140d08 100%);
                  --panel-bg: rgba(38, 25, 17, 0.8);
                  --panel-border: rgba(217, 119, 6, 0.2);
                  --panel-border-hover: rgba(217, 119, 6, 0.45);
                  --user-msg: linear-gradient(135deg, #b45309, #78350f);
                  --ai-msg: rgba(48, 31, 22, 0.8);
                  --accent-glow: #fcd34d;
                  --accent-blue: #d97706;
                  --accent-purple: #b45309;
                  --success: #10b981;
                  --success-glow: rgba(16, 185, 129, 0.2);
                  --text-main: #fffbeb;
                  --text-muted: #d6b89e;
                  --code-bg: #1b1008;
                  --analysis-bg: rgba(69, 42, 28, 0.7);
                  --analysis-border: #d97706;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.5);
              }
      
              body[data-theme="daylight"] {
                  --bg-dark: #f8fafc;
                  --bg-gradient: radial-gradient(circle at 50% -20%, #e0e7ff 0%, #f1f5f9 60%, #f8fafc 100%);
                  --panel-bg: rgba(255, 255, 255, 0.85);
                  --panel-border: #cbd5e1;
                  --panel-border-hover: #94a3b8;
                  --user-msg: linear-gradient(135deg, #2563eb, #1d4ed8);
                  --ai-msg: rgba(241, 245, 249, 0.9);
                  --accent-glow: #2563eb;
                  --accent-blue: #2563eb;
                  --accent-purple: #4f46e5;
                  --success: #059669;
                  --success-glow: rgba(5, 150, 105, 0.15);
                  --text-main: #0f172a;
                  --text-muted: #64748b;
                  --code-bg: #ffffff;
                  --analysis-bg: #eef2ff;
                  --analysis-border: #818cf8;
                  --shadow-subtle: 0 10px 30px rgba(0, 0, 0, 0.06);
              }
      
              /* Font Sizes */
              body[data-font="small"] { font-size: 14px; }
              body[data-font="medium"] { font-size: 15.5px; }
              body[data-font="large"] { font-size: 17.5px; }
              body[data-font="huge"] { font-size: 19.5px; }
      
              * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Rubik', sans-serif; }
              body { background-color: var(--bg-dark); color: var(--text-main); height: 100vh; display: flex; flex-direction: column; overflow: hidden; background-image: var(--bg-gradient); transition: background 0.3s ease, color 0.3s ease; }
      
              /* Custom Scrollbar */
              ::-webkit-scrollbar { width: 6px; height: 6px; }
              ::-webkit-scrollbar-track { background: transparent; }
              ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 10px; }
              ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.22); }
      
              /* Navigation Header */
              header { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  border-bottom: 1px solid var(--panel-border); 
                  padding: 10px 24px; 
                  display: flex; 
                  justify-content: space-between; 
                  align-items: center; 
                  z-index: 20; 
                  box-shadow: var(--shadow-subtle);
                  transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); 
              }
      
              .logo-area { display: flex; align-items: center; gap: 16px; }
              .logo { 
                  font-size: 1.35rem; 
                  font-weight: 800; 
                  background: linear-gradient(135deg, var(--accent-glow), var(--accent-purple)); 
                  -webkit-background-clip: text; 
                  -webkit-text-fill-color: transparent; 
                  display: flex; 
                  align-items: center; 
                  gap: 10px; 
                  letter-spacing: -0.5px;
              }
      
              /* Topbar Status Badge */
              .topbar-status-badge {
                  display: inline-flex;
                  align-items: center;
                  gap: 7px;
                  padding: 4px 12px;
                  border-radius: 20px;
                  font-size: 0.78rem;
                  font-weight: 600;
                  background: rgba(255,255,255,0.04);
                  border: 1px solid var(--panel-border);
                  color: var(--text-muted);
              }
              .topbar-status-badge.active {
                  background: var(--success-glow);
                  border-color: rgba(16, 185, 129, 0.4);
                  color: var(--success);
              }
              .status-dot { width: 7px; height: 7px; border-radius: 50%; background: #64748b; }
              .topbar-status-badge.active .status-dot { background: var(--success); box-shadow: 0 0 8px var(--success); animation: pulse-dot 2s infinite; }
      
              @keyframes pulse-dot { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.3); opacity: 0.7; } }
      
              .header-actions { display: flex; align-items: center; gap: 10px; }
              .tabs { display: flex; gap: 8px; }
              .tab-btn { 
                  background: rgba(255,255,255,0.03); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  padding: 7px 16px; 
                  border-radius: 10px; 
                  cursor: pointer; 
                  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); 
                  font-size: 0.9rem; 
                  font-weight: 600; 
                  display: flex; 
                  align-items: center; 
                  gap: 8px; 
              }
              .tab-btn.active { 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border-color: transparent; 
                  color: #fff; 
                  box-shadow: 0 4px 15px rgba(59, 130, 246, 0.35); 
              }
              .tab-btn:hover:not(.active) { background: rgba(255,255,255,0.08); border-color: var(--panel-border-hover); }
      
              /* Workspace Grid */
              main { flex: 1; overflow: hidden; position: relative; }
              .tab-content { display: none; height: 100%; padding: 16px 20px; }
              .tab-content.active { display: flex; gap: 16px; }
      
              /* Sidebar Sessions */
              .history-sidebar { 
                  width: 280px; 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  border: 1px solid var(--panel-border); 
                  border-radius: 18px; 
                  padding: 16px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 12px; 
                  box-shadow: var(--shadow-subtle);
                  transition: all 0.3s ease; 
              }
              .new-chat-btn { 
                  width: 100%; 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border: none; 
                  color: white; 
                  padding: 11px 16px; 
                  border-radius: 12px; 
                  cursor: pointer; 
                  font-weight: 700; 
                  font-size: 0.92rem; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
                  gap: 8px; 
                  transition: all 0.2s ease; 
                  box-shadow: 0 4px 14px rgba(59, 130, 246, 0.3); 
              }
              .new-chat-btn:hover { opacity: 0.95; transform: translateY(-1.5px); box-shadow: 0 6px 18px rgba(59, 130, 246, 0.45); }
              
              .history-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 6px; padding-left: 4px; }
              .history-item { 
                  background: rgba(255,255,255,0.02); 
                  border: 1px solid transparent; 
                  padding: 9px 12px; 
                  border-radius: 10px; 
                  cursor: pointer; 
                  transition: all 0.2s; 
                  display: flex; 
                  justify-content: space-between; 
                  align-items: center; 
                  font-size: 0.86rem; 
                  color: var(--text-muted); 
              }
              .history-item:hover, .history-item.active { 
                  background: rgba(99, 102, 241, 0.12); 
                  border-color: rgba(99, 102, 241, 0.3); 
                  color: var(--text-main); 
              }
              .history-item .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
              .history-item .delete-btn { opacity: 0; transition: opacity 0.2s; color: #ef4444; padding: 2px 6px; }
              .history-item:hover .delete-btn { opacity: 1; }
      
              /* Main Chat Window */
              .chat-main { flex: 1; display: flex; flex-direction: column; height: 100%; position: relative; }
              .chat-container { 
                  flex: 1; 
                  overflow-y: auto; 
                  padding: 10px 10px 30px 10px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 18px; 
                  scroll-behavior: smooth; 
              }
              .message { 
                  max-width: 85%; 
                  padding: 16px 22px; 
                  border-radius: 18px; 
                  line-height: 1.7; 
                  word-wrap: break-word; 
                  box-shadow: var(--shadow-subtle); 
                  backdrop-filter: blur(14px); 
                  animation: messageIn 0.25s cubic-bezier(0.4, 0, 0.2, 1);
              }
      
              @keyframes messageIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
      
              .message.user { align-self: flex-start; background: var(--user-msg); color: white; border-bottom-right-radius: 3px; }
              .message.model { align-self: flex-end; background: var(--ai-msg); border-bottom-left-radius: 3px; border: 1px solid var(--panel-border); }
              
              .message-attachments { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
              .msg-att-chip { background: rgba(0,0,0,0.25); border: 1px solid rgba(255,255,255,0.15); padding: 3px 10px; border-radius: 6px; font-size: 0.78rem; display: flex; align-items: center; gap: 6px; }
      
              /* Loading UI Animation */
              .loading-shimmer { display: inline-flex; align-items: center; gap: 10px; font-weight: 500; color: var(--accent-glow); }
              .loading-shimmer i { font-size: 1.1rem; animation: spin 1s linear infinite; }
              @keyframes spin { 100% { transform: rotate(360deg); } }
      
              /* Analysis Component */
              .analysis-box { background: var(--analysis-bg); border: 1px solid var(--analysis-border); border-radius: 14px; margin-bottom: 14px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.15); }
              .analysis-header { padding: 11px 16px; background: rgba(99, 102, 241, 0.22); cursor: pointer; font-weight: 700; font-size: 0.9rem; color: var(--accent-glow); display: flex; justify-content: space-between; align-items: center; user-select: none; }
              .analysis-content { padding: 16px; font-size: 0.9rem; color: var(--text-main); display: none; border-top: 1px solid var(--analysis-border); white-space: pre-wrap; line-height: 1.6; }
              .analysis-box.open .analysis-content { display: block; }
      
              /* Attachments Bar */
              .attachments-preview { display: flex; gap: 8px; flex-wrap: wrap; padding: 0 4px; }
              .att-chip { background: rgba(56, 189, 248, 0.12); border: 1px solid var(--accent-glow); padding: 5px 12px; border-radius: 8px; font-size: 0.82rem; display: flex; align-items: center; gap: 8px; color: var(--text-main); }
              .att-chip .remove-att { cursor: pointer; color: #ef4444; font-weight: bold; }
      
              /* Input Controls Area */
              .input-area { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  padding: 14px 18px; 
                  border: 1px solid var(--panel-border); 
                  border-radius: 18px; 
                  display: flex; 
                  flex-direction: column; 
                  gap: 10px; 
                  margin-top: 8px; 
                  box-shadow: var(--shadow-subtle);
                  position: relative;
              }
              .controls-row { display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem; color: var(--text-muted); flex-wrap: wrap; gap: 10px; }
              .input-wrapper { display: flex; gap: 8px; align-items: center; }
              textarea { 
                  flex: 1; 
                  background: rgba(0, 0, 0, 0.25); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  padding: 12px 16px; 
                  border-radius: 14px; 
                  resize: none; 
                  height: 60px; 
                  outline: none; 
                  font-size: 0.98rem; 
                  transition: all 0.2s ease; 
              }
              textarea:focus { border-color: var(--accent-glow); box-shadow: 0 0 12px rgba(56, 189, 248, 0.2); }
              
              .btn-action { 
                  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); 
                  border: none; 
                  color: white; 
                  padding: 0 22px; 
                  height: 60px; 
                  border-radius: 14px; 
                  cursor: pointer; 
                  font-weight: 700; 
                  font-size: 1rem; 
                  transition: all 0.2s ease; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
                  gap: 8px; 
                  box-shadow: 0 4px 14px rgba(59, 130, 246, 0.3);
              }
              .btn-action:hover { opacity: 0.95; transform: translateY(-1.5px); box-shadow: 0 6px 20px rgba(59, 130, 246, 0.45); }
              .btn-action:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
              
              .btn-icon-ctl { 
                  background: rgba(255,255,255,0.03); 
                  border: 1px solid var(--panel-border); 
                  color: var(--text-main); 
                  width: 60px; 
                  height: 60px; 
                  border-radius: 14px; 
                  cursor: pointer; 
                  font-size: 1.2rem; 
                  transition: all 0.2s ease; 
                  display: flex; 
                  align-items: center; 
                  justify-content: center; 
              }
              .btn-icon-ctl:hover { background: rgba(255,255,255,0.08); border-color: var(--accent-glow); transform: translateY(-1px); }
              .btn-icon-ctl.recording { background: #ef4444; color: white; animation: pulse 1.2s infinite; }
      
              @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); } 70% { box-shadow: 0 0 0 12px rgba(239, 68, 68, 0); } 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); } }
      
              /* Fast In-Chat Folder Selector Popover & Checkboxes */
              .chat-folder-picker-wrapper { position: relative; display: inline-block; }
              .chat-folder-btn {
                  background: rgba(255, 255, 255, 0.04);
                  border: 1px solid var(--panel-border);
                  color: var(--text-main);
                  padding: 5px 12px;
                  border-radius: 8px;
                  cursor: pointer;
                  font-size: 0.82rem;
                  font-weight: 600;
                  display: flex;
                  align-items: center;
                  gap: 7px;
                  transition: all 0.2s ease;
              }
              .chat-folder-btn:hover { border-color: var(--accent-glow); background: rgba(56, 189, 248, 0.1); }
              
              .chat-folder-popover {
                  position: absolute;
                  bottom: calc(100% + 8px);
                  right: 0;
                  width: 320px;
                  max-height: 280px;
                  background: var(--panel-bg);
                  backdrop-filter: blur(25px);
                  border: 1px solid var(--panel-border);
                  border-radius: 14px;
                  padding: 12px;
                  box-shadow: 0 10px 30px rgba(0,0,0,0.5);
                  z-index: 100;
                  display: none;
                  flex-direction: column;
                  gap: 10px;
              }
              .chat-folder-popover.open { display: flex; }
              .chat-folder-popover-header {
                  display: flex;
                  justify-content: space-between;
                  align-items: center;
                  font-size: 0.82rem;
                  font-weight: 700;
                  color: var(--accent-glow);
                  border-bottom: 1px dashed var(--panel-border);
                  padding-bottom: 6px;
              }
              .chat-folder-grid {
                  display: flex;
                  flex-direction: column;
                  gap: 6px;
                  max-height: 200px;
                  overflow-y: auto;
                  padding-left: 4px;
              }
      
              /* Code Block Styling */
              .code-container { position: relative; margin: 14px 0; border-radius: 12px; overflow: hidden; border: 1px solid var(--panel-border); background: var(--code-bg); box-shadow: 0 6px 20px rgba(0,0,0,0.3); }
              .code-header { background: rgba(255,255,255,0.03); padding: 9px 16px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--panel-border); font-size: 0.82rem; color: var(--text-muted); }
              .code-actions { display: flex; gap: 6px; }
              .code-btn { background: rgba(255,255,255,0.05); border: 1px solid var(--panel-border); color: var(--text-main); padding: 4px 10px; border-radius: 6px; font-size: 0.78rem; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 5px; font-weight: 500; }
              .code-btn:hover { background: var(--accent-blue); color: white; border-color: transparent; }
              pre { margin: 0 !important; }
              pre code { font-family: 'JetBrains Mono', monospace !important; font-size: 0.92rem; padding: 16px !important; line-height: 1.55; }
      
              /* Settings Panels */
              .settings-card { 
                  background: var(--panel-bg); 
                  backdrop-filter: blur(20px); 
                  padding: 24px; 
                  border-radius: 18px; 
                  border: 1px solid var(--panel-border); 
                  margin-bottom: 18px; 
                  width: 100%; 
                  max-width: 820px; 
                  box-shadow: var(--shadow-subtle);
              }
              .settings-card h3 { margin-bottom: 14px; color: var(--accent-glow); display: flex; align-items: center; gap: 10px; font-size: 1.15rem; font-weight: 700; }
              .input-field { width: 100%; padding: 11px 16px; background: rgba(0, 0, 0, 0.25); border: 1px solid var(--panel-border); color: var(--text-main); border-radius: 10px; margin-bottom: 12px; font-size: 0.95rem; outline: none; transition: 0.2s; }
              .input-field:focus { border-color: var(--accent-glow); }
              textarea.keys-input { height: 100px; font-family: 'JetBrains Mono', monospace; direction: ltr; }
              select { background: var(--panel-bg); color: var(--text-main); border: 1px solid var(--panel-border); padding: 6px 12px; border-radius: 8px; outline: none; font-weight: 500; }
      
              /* ZIP Management & Dynamic Folder Selector */
              .zip-status-container { 
                  border: 1px dashed var(--panel-border); 
                  border-radius: 14px; 
                  padding: 20px; 
                  background: rgba(0,0,0,0.15); 
                  display: flex; 
                  flex-direction: column; 
                  gap: 14px; 
                  transition: all 0.2s ease; 
              }
              .zip-status-container.has-file { 
                  border-style: solid; 
                  border-color: rgba(16, 185, 129, 0.4); 
                  background: var(--success-glow); 
              }
              .zip-info-row { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px; }
              .zip-info-details { display: flex; align-items: center; gap: 12px; }
              .zip-icon { font-size: 2rem; color: var(--success); }
              .zip-info-text .title { font-weight: 700; font-size: 0.98rem; }
              .zip-info-text .sub { font-size: 0.82rem; color: var(--text-muted); }
              .zip-actions { display: flex; gap: 8px; }
      
              /* Folder Picker Grid in Settings */
              .folder-picker-section { border-top: 1px solid var(--panel-border); padding-top: 14px; }
              .folder-picker-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
              .folder-picker-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; max-height: 180px; overflow-y: auto; padding: 4px; }
              .folder-checkbox-item { background: rgba(0,0,0,0.25); border: 1px solid var(--panel-border); padding: 7px 12px; border-radius: 8px; display: flex; align-items: center; gap: 8px; font-size: 0.86rem; cursor: pointer; user-select: none; transition: 0.2s; }
              .folder-checkbox-item:hover { border-color: var(--accent-glow); background: rgba(56, 189, 248, 0.08); }
              .folder-checkbox-item input { cursor: pointer; }
      
              /* Zen Mode */
              body.zen-mode header { transform: translateY(-100%); height: 0; padding: 0; overflow: hidden; }
              body.zen-mode .history-sidebar { display: none; }
              body.zen-mode .tab-content { padding: 10px 14%; }
              .exit-zen-btn { display: none; position: fixed; top: 16px; left: 16px; background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; padding: 9px 16px; border-radius: 10px; cursor: pointer; z-index: 1000; font-weight: bold; box-shadow: 0 4px 15px rgba(0,0,0,0.5); }
              body.zen-mode .exit-zen-btn { display: flex; align-items: center; gap: 6px; }
      
              /* Jump Button */
              .jump-bottom-btn { position: absolute; bottom: 130px; left: 50%; transform: translateX(-50%) translateY(20px); background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; padding: 8px 18px; border-radius: 20px; cursor: pointer; font-size: 0.84rem; font-weight: bold; box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4); display: flex; align-items: center; gap: 8px; opacity: 0; pointer-events: none; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); z-index: 50; }
              .jump-bottom-btn.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
              .jump-bottom-btn:hover { transform: translateX(-50%) scale(1.05); }
      
              .code-snippet-box {
                  background: #080808;
                  border: 1px solid var(--panel-border);
                  border-radius: 10px;
                  padding: 12px;
                  font-family: 'JetBrains Mono', monospace;
                  font-size: 0.82rem;
                  color: #38bdf8;
                  white-space: pre;
                  overflow-x: auto;
                  direction: ltr;
                  margin: 10px 0;
              }
          </style>
      </head>
      <body data-theme="cyberpunk" data-font="medium">
      
          <!-- Exit Zen Mode Button -->
          <button class="exit-zen-btn" onclick="toggleZenMode()"><i class="fa-solid fa-compress"></i> צא ממצב פוקוס</button>
      
          <header>
              <div class="logo-area">
                  <div class="logo">
                      <i class="fa-solid fa-headset"></i>
                      <span>מומחה AI ימות המשיח</span>
                  </div>
                  <!-- Live Topbar Context Indicator -->
                  <div class="topbar-status-badge" id="topbarZipBadge" title="סטטוס קובץ תיעוד ימות המשיח">
                      <div class="status-dot"></div>
                      <span id="topbarZipText">בודק תיעוד...</span>
                  </div>
              </div>
      
              <div class="header-actions">
                  <button class="tab-btn" onclick="toggleZenMode()" title="מצב פוקוס נקי (Zen Mode)"><i class="fa-solid fa-expand"></i> פוקוס</button>
                  <div class="tabs">
                      <button class="tab-btn active" onclick="switchTab('chatTab', event)"><i class="fa-solid fa-comments"></i> צ'אט וסביבת עבודה</button>
                      <button class="tab-btn" id="settingsTabBtn" onclick="switchTab('settingsTab', event)"><i class="fa-solid fa-sliders"></i> הגדרות וגוגל סקריפט</button>
                  </div>
              </div>
          </header>
      
          <main>
              <!-- Chat Tab with History Sidebar -->
              <div id="chatTab" class="tab-content active">
                  <div class="history-sidebar">
                      <button class="new-chat-btn" onclick="createNewSession()"><i class="fa-solid fa-plus"></i> שיחה חדשה</button>
                      <div style="font-size: 0.78rem; color: var(--text-muted); font-weight: 700; margin-top: 6px; letter-spacing: 0.5px;">שיחות אחרונות</div>
                      <div class="history-list" id="historyList"></div>
                  </div>
      
                  <div class="chat-main">
                      <div class="chat-container" id="chatContainer" onscroll="handleChatScroll()">
                          <div class="message model">
                              שלום! אני עוזר ה-AI הבכיר שלך לבניית מרכזיות, שלוחות ו-Webhooks ב'ימות המשיח'.<br>
                              התקשורת מנותבת בבטחה דרך **Google Apps Script Proxy** כאשר <b>כל ההגדרות, המפתחות והתיעוד נשמרים בדפדפן שלך בלבד</b>!
                          </div>
                      </div>
                      
                      <button class="jump-bottom-btn" id="jumpBottomBtn" onclick="scrollToBottom(true)">
                          <i class="fa-solid fa-arrow-down"></i> קפוץ לקוד האחרון / למטה
                      </button>
      
                      <div class="input-area">
                          <div class="attachments-preview" id="attachmentsPreview"></div>
      
                          <div class="controls-row">
                              <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
                                  <div>
                                      <i class="fa-solid fa-robot"></i> מודל:
                                      <select id="modelSelect"></select>
                                  </div>
      
                                  <!-- Fast In-Chat Folder Scope Selector with Checkboxes Popover -->
                                  <div id="chatFolderScopeContainer" class="chat-folder-picker-wrapper" style="display: none;">
                                      <button class="chat-folder-btn" onclick="toggleChatFolderPopover(event)">
                                          <i class="fa-solid fa-folder-tree" style="color: var(--accent-glow);"></i>
                                          <span>תיקיות לשליחה (<b id="chatFolderCountBadge">0</b>)</span>
                                          <i class="fa-solid fa-chevron-up" style="font-size: 0.75rem;"></i>
                                      </button>
      
                                      <div class="chat-folder-popover" id="chatFolderPopover" onclick="event.stopPropagation()">
                                          <div class="chat-folder-popover-header">
                                              <span><i class="fa-solid fa-check-double"></i> בחירת תיקיות בודדות לשליחה</span>
                                              <div style="display: flex; gap: 6px;">
                                                  <button class="tab-btn" style="padding: 2px 6px; font-size: 0.72rem;" onclick="selectAllFolders(true)">הכל</button>
                                                  <button class="tab-btn" style="padding: 2px 6px; font-size: 0.72rem;" onclick="selectAllFolders(false)">נקה</button>
                                              </div>
                                          </div>
                                          <div class="chat-folder-grid" id="chatFolderCheckboxesGrid"></div>
                                      </div>
                                  </div>
                              </div>
      
                              <div>
                                  <label style="cursor: pointer;">
                                      <input type="checkbox" id="deepAnalysisToggle" checked> דרוש ניתוח מעמיק (&lt;analysis&gt;)
                                  </label>
                              </div>
                          </div>
                          <div class="input-wrapper">
                              <input type="file" id="genericFileInput" multiple style="display: none;" onchange="handleGenericFileUpload(event)">
                              <button class="btn-icon-ctl" onclick="document.getElementById('genericFileInput').click()" title="צרף קבצים (קוד, תמונות, שמע, PDF, ZIP...)">
                                  <i class="fa-solid fa-paperclip"></i>
                              </button>
      
                              <button class="btn-icon-ctl" id="micBtn" onclick="toggleVoiceInput()" title="הקלטה קולית בעברית">
                                  <i class="fa-solid fa-microphone"></i>
                              </button>
                              
                              <textarea id="userInput" placeholder="תאר את השלוחה או שאל שאלה (ניתן לגרור קבצים לכאן)..." onkeydown="handleKeyDown(event)"></textarea>
                              <button class="btn-action" id="sendBtn" onclick="sendMessage()"><i class="fa-solid fa-paper-plane"></i> שלח</button>
                          </div>
                      </div>
                  </div>
              </div>
      
              <!-- Settings Tab -->
              <div id="settingsTab" class="tab-content" style="flex-direction: column; overflow-y: auto;">
                  
                  <!-- Google Apps Script Proxy Setting -->
                  <div class="settings-card">
                      <h3><i class="fa-brands fa-google"></i> הזרמת בקשות דרך Google Apps Script Proxy</h3>
                      <p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 12px;">הכנס את כתובת ה-Web App של Google Script שמשמש כצינור תיווך בלבד (כל ההגדרות והמפתחות נשארים בדפדפן):</p>
                      
                      <input type="url" id="gasUrlInput" class="input-field" placeholder="https://script.google.com/macros/s/AKfycb.../exec" dir="ltr">
                      <button class="tab-btn active" onclick="saveGasUrl()"><i class="fa-solid fa-floppy-disk"></i> שמור כתובת סקריפט גוגל</button>
      
                      <div style="margin-top: 16px; border-top: 1px dashed var(--panel-border); padding-top: 12px;">
                          <div style="font-weight: 700; font-size: 0.9rem; margin-bottom: 6px; color: var(--accent-glow);">
                              <i class="fa-solid fa-code"></i> קוד ה-Google Apps Script (העתק והדבק ב-Script Editor):
                          </div>
                          <p style="font-size: 0.82rem; color: var(--text-muted);">הקוד הבא הינו תיווך נקי. צור פרויקט ב-Google Apps Script, הדבק את הקוד, ובצע <b>Deploy -> New deployment -> Web App</b> (תחת Access שנה ל-<b>Anyone</b>):</p>
                          <div class="code-snippet-box" id="gasScriptSnippet">function doPost(e) {
        try {
          var data = JSON.parse(e.postData.contents);
          var options = {
            method: data.method || 'post',
            contentType: 'application/json',
            muteHttpExceptions: true
          };
          if (data.payload) {
            options.payload = typeof data.payload === 'string' ? data.payload : JSON.stringify(data.payload);
          }
          var response = UrlFetchApp.fetch(data.url, options);
          return ContentService.createTextOutput(response.getContentText())
            .setMimeType(ContentService.MimeType.JSON);
        } catch (err) {
          return ContentService.createTextOutput(JSON.stringify({ error: err.toString() }))
            .setMimeType(ContentService.MimeType.JSON);
        }
      }
      
      function doGet(e) {
        if (e.parameter && e.parameter.url) {
          var response = UrlFetchApp.fetch(e.parameter.url, { muteHttpExceptions: true });
          return ContentService.createTextOutput(response.getContentText())
            .setMimeType(ContentService.MimeType.JSON);
        }
        return ContentService.createTextOutput(JSON.stringify({ status: "active", message: "Yemot GAS Proxy Active" }))
          .setMimeType(ContentService.MimeType.JSON);
      }</div>
                          <button class="code-btn" onclick="copyGasScriptCode()"><i class="fa-solid fa-copy"></i> העתק קוד סקריפט גוגל</button>
                      </div>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-key"></i> שרשור מפתחות API בדפדפן (Gemini Key Rotation)</h3>
                      <p style="font-size: 0.84rem; color: var(--text-muted); margin-bottom: 8px;">המפתחות נשמרים ב-LocalStorage של הדפדפן שלך ונשלחים דרך הסקריפט המתווך בלבד:</p>
                      <textarea id="apiKeysInput" class="input-field keys-input" placeholder="הכנס מפתחות API (מפתח בכל שורה)..."></textarea>
                      <button class="tab-btn active" onclick="saveKeys()"><i class="fa-solid fa-floppy-disk"></i> שמור מפתחות בדפדפן</button>
                  </div>
      
                  <!-- Dynamic ZIP & Folder Management Card -->
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-file-zipper"></i> תיעוד ימות המשיח ובחירת תיקיות בדפדפן</h3>
                      <p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 14px;">קובץ ה-ZIP של התיעוד מנותח אוטומטית בדפדפן. תוכל לבחור לשלוח את כל התיעוד או רק תיקיות ספציפיות לקבלת מענה ממוקד ומהיר.</p>
                      
                      <input type="file" id="zipFileInput" accept=".zip" style="display: none;" onchange="handleZipUpload(event)">
                      
                      <div class="zip-status-container" id="zipStatusBox"></div>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-palette"></i> התאמת מראה וסביבת עבודה (Themes & UI)</h3>
                      <div style="display: flex; gap: 16px; flex-wrap: wrap;">
                          <div style="flex: 1;">
                              <label style="display: block; margin-bottom: 6px; color: var(--text-muted); font-size: 0.88rem;">ערכת נושא (Theme):</label>
                              <select id="themeSelect" class="input-field" onchange="changeTheme(this.value)">
                                  <option value="cyberpunk">🌌 Midnight Cyberpunk (ניאון כהה)</option>
                                  <option value="obsidian">🖤 Obsidian Pure Black (שחור מוחלט)</option>
                                  <option value="espresso">☕ Warm Espresso (חום קפה חמים)</option>
                                  <option value="daylight">☀️ Clean Daylight (מצב יום בהיר)</option>
                              </select>
                          </div>
                          <div style="flex: 1;">
                              <label style="display: block; margin-bottom: 6px; color: var(--text-muted); font-size: 0.88rem;">גודל גופן (Font Size):</label>
                              <select id="fontSelect" class="input-field" onchange="changeFontSize(this.value)">
                                  <option value="small">קטן (14px)</option>
                                  <option value="medium" selected>בינוני (15.5px)</option>
                                  <option value="large">גדול (17.5px)</option>
                                  <option value="huge">ענק (19.5px)</option>
                              </select>
                          </div>
                      </div>
                  </div>
      
                  <div class="settings-card">
                      <h3><i class="fa-solid fa-cloud-arrow-up"></i> חיבור ישיר ל-API ימות המשיח (בדפדפן)</h3>
                      <input type="text" id="ymUsername" class="input-field" placeholder="מספר מערכת (System Username)">
                      <input type="password" id="ymPassword" class="input-field" placeholder="סיסמת מערכת (System Password)">
                      <button class="tab-btn active" onclick="saveYemotCredentials()"><i class="fa-solid fa-link"></i> שמור פרטי התחברות למערכת</button>
                  </div>
              </div>
          </main>
      
          <script>
              const SYSTEM_INSTRUCTION = `אתה מתכנת בכיר ומומחה לשלוחות, מרכזיות ופיתוח ב'ימות המשיח'. תפקידך לבנות קובצי ext.ini מדויקים, תפריטי IVR, ושרתי Webhook.
      לרשותך מצורף קובץ ZIP המכיל את התיעוד וההגדרות של ימות המשיח (בהתאם לתיקיות שנבחרו), לצד כל קובץ שהמשתמש מעלה ומצרף. עליך להתבסס על התיעוד באופן מוחלט בכל תשובה.
      
      הנחיות חובה:
      1. אם המשתמש ביקש ניתוח מעמיק (או כברירת מחדל), עליך לפתוח את תגובתך בניתוח מקיף ומעמיק של הדרישה, הפורמט, הלוגיקה, והפרמטרים הנדרשים עטוף כולו בתוך תגיות <analysis> ו-</analysis>.
      2. רק לאחר התגית הסוגרת </analysis> תציג את התשובה והקוד המלאים והסופיים.
      3. הקפד על כתיבת קוד ext.ini תקני, כולל הערות והסברים ברורים בעברית.`;
      
              const LOCAL_STORAGE_KEYS = {
                  GAS_URL: 'yemot_v8_gas_script_url',
                  API_KEYS: 'yemot_v8_gemini_api_keys',
                  ZIP_BASE64: 'yemot_v8_zip_base64',
                  ZIP_META: 'yemot_v8_zip_meta',
                  ZIP_FOLDERS: 'yemot_v8_zip_folders',
                  SELECTED_FOLDERS: 'yemot_v8_selected_folders',
                  SESSIONS: 'yemot_v8_chat_sessions',
                  YM_USER: 'yemot_v8_ym_username',
                  YM_PASS: 'yemot_v8_ym_password',
                  THEME: 'yemot_v8_theme',
                  FONT_SIZE: 'yemot_v8_font_size'
              };
      
              // App State (All maintained in browser)
              let gasProxyUrl = localStorage.getItem(LOCAL_STORAGE_KEYS.GAS_URL) || '';
              let apiKeys = [];
              let zipBase64 = localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_BASE64) || null;
              let zipMeta = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_META) || 'null');
              let zipFolders = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS) || '[]');
              let selectedFolders = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS) || '[]');
              let sessions = [];
              let currentSessionId = null;
              let isRecording = false;
              let recognition = null;
              let pendingAttachments = [];
      
              window.onload = async () => {
                  loadThemePreferences();
                  loadGasUrl();
                  loadKeys();
                  loadYemotCredentials();
                  initSpeechRecognition();
                  initDragAndDrop();
                  loadSessions();
                  updateZipStatusUI();
                  populateFallbackModels();
                  if (gasProxyUrl && apiKeys.length > 0) {
                      await fetchModels();
                  }
      
                  // Close folder popover when clicking outside
                  document.addEventListener('click', () => {
                      const popover = document.getElementById('chatFolderPopover');
                      if (popover) popover.classList.remove('open');
                  });
              };
      
              function switchTab(tabId, event) {
                  document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
                  document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
                  document.getElementById(tabId).classList.add('active');
                  if(event) event.currentTarget.classList.add('active');
              }
      
              // --- GAS Proxy & Storage ---
              function loadGasUrl() {
                  gasProxyUrl = localStorage.getItem(LOCAL_STORAGE_KEYS.GAS_URL) || '';
                  document.getElementById('gasUrlInput').value = gasProxyUrl;
              }
      
              function saveGasUrl() {
                  const url = document.getElementById('gasUrlInput').value.trim();
                  gasProxyUrl = url;
                  localStorage.setItem(LOCAL_STORAGE_KEYS.GAS_URL, url);
                  alert('כתובת ה-Google Script נשמרה בדפדפן בהצלחה!');
                  if (gasProxyUrl && apiKeys.length > 0) fetchModels();
              }
      
              function copyGasScriptCode() {
                  const code = document.getElementById('gasScriptSnippet').innerText;
                  navigator.clipboard.writeText(code);
                  alert('קוד הסקריפט הועתק ללוח!');
              }
      
              // --- Generic Relay Call via GAS Proxy with Robust Error Parsing ---
              async function fetchViaGasProxy(targetUrl, method = 'POST', payloadData = null) {
                  if (!gasProxyUrl) {
                      throw new Error('MISSING_GAS_URL');
                  }
      
                  const proxyBody = {
                      url: targetUrl,
                      method: method,
                      payload: payloadData
                  };
      
                  const res = await fetch(gasProxyUrl, {
                      method: 'POST',
                      headers: { 'Content-Type': 'text/plain;charset=utf-8' },
                      body: JSON.stringify(proxyBody)
                  });
      
                  if (!res.ok) {
                      throw new Error(`Google Script Proxy returned status ${res.status}`);
                  }
      
                  const textResponse = await res.text();
                  let parsed;
                  try {
                      parsed = JSON.parse(textResponse);
                  } catch(e) {
                      throw new Error(`תגובה לא חוקית מ-Google Script. וודא שביצעת Deploy כ-Web App עם הרשאת Anyone.\nתוכן התגובה: ${textResponse.substring(0, 150)}...`);
                  }
      
                  return parsed;
              }
      
              // --- Key Rotation Engine in Client ---
              async function makeGeminiApiCall(targetUrlFactory, method = 'POST', payloadFactory = null) {
                  if (!gasProxyUrl) {
                      throw new Error('MISSING_GAS_URL');
                  }
      
                  if (apiKeys.length === 0) {
                      throw new Error('MISSING_KEYS');
                  }
      
                  let lastError = null;
                  for (let i = 0; i < apiKeys.length; i++) {
                      const key = apiKeys[i];
                      const targetUrl = targetUrlFactory(key);
                      const payload = payloadFactory ? payloadFactory(key) : null;
      
                      try {
                          const data = await fetchViaGasProxy(targetUrl, method, payload);
                          if (data.error) {
                              throw new Error(data.error.message || JSON.stringify(data.error));
                          }
                          return data;
                      } catch (err) {
                          if (err.message === 'MISSING_GAS_URL') throw err;
                          console.warn(`Key ${i + 1} failed via proxy, trying next key...`, err);
                          lastError = err;
                      }
                  }
                  throw new Error(`כל מפתחות ה-API נכשלו: ${lastError?.message || lastError}`);
              }
      
              function populateFallbackModels() {
                  const select = document.getElementById('modelSelect');
                  if (select.children.length === 0) {
                      select.innerHTML = `
                          <option value="gemini-3.6-flash" selected>gemini-3.6-flash</option>
                          <option value="gemini-2.0-flash">gemini-2.0-flash</option>
                          <option value="gemini-1.5-flash">gemini-1.5-flash</option>
                          <option value="gemini-1.5-pro">gemini-1.5-pro</option>
                      `;
                  }
              }
      
              // --- Fetch Models via GAS Proxy Safely ---
              async function fetchModels() {
                  populateFallbackModels();
                  if (!gasProxyUrl || apiKeys.length === 0) return;
      
                  try {
                      const data = await makeGeminiApiCall(
                          (key) => `https://generativelanguage.googleapis.com/v1beta/models?key=${key}`,
                          'GET'
                      );
                      
                      let models = (data.models || [])
                          .filter(m => m.supportedGenerationMethods?.includes('generateContent'))
                          .map(m => m.name.replace('models/', ''));
      
                      if (models.length > 0) {
                          const select = document.getElementById('modelSelect');
                          const currentValue = select.value || 'gemini-3.6-flash';
                          select.innerHTML = '';
                          
                          // Always guarantee gemini-3.6-flash is at the top
                          if (!models.includes('gemini-3.6-flash')) {
                              models.unshift('gemini-3.6-flash');
                          }
      
                          models.forEach(m => {
                              const opt = document.createElement('option');
                              opt.value = m; opt.textContent = m;
                              select.appendChild(opt);
                          });
      
                          select.value = models.includes(currentValue) ? currentValue : 'gemini-3.6-flash';
                      }
                  } catch(e) { 
                      console.warn("Could not fetch models dynamically, using default list with gemini-3.6-flash.", e);
                  }
              }
      
              // --- Dynamic ZIP & Folder Analysis (Client Side) ---
              async function analyzeZipFolders(base64Data) {
                  try {
                      const zip = new JSZip();
                      await zip.loadAsync(base64Data, { base64: true });
                      
                      const folderSet = new Set();
                      zip.forEach((relativePath, file) => {
                          const parts = relativePath.split('/');
                          if (parts.length > 1 && parts[0].trim().length > 0) {
                              folderSet.add(parts[0]);
                          }
                      });
      
                      zipFolders = Array.from(folderSet).sort();
                      selectedFolders = [...zipFolders];
                      localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS, JSON.stringify(zipFolders));
                      localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  } catch (err) {
                      console.error("Error analyzing ZIP folders:", err);
                      zipFolders = [];
                      selectedFolders = [];
                  }
              }
      
              function toggleChatFolderPopover(event) {
                  event.stopPropagation();
                  const popover = document.getElementById('chatFolderPopover');
                  if (popover) popover.classList.toggle('open');
              }
      
              function updateZipStatusUI() {
                  const statusBox = document.getElementById('zipStatusBox');
                  const topbarBadge = document.getElementById('topbarZipBadge');
                  const topbarText = document.getElementById('topbarZipText');
                  const chatFolderContainer = document.getElementById('chatFolderScopeContainer');
                  const chatFolderGrid = document.getElementById('chatFolderCheckboxesGrid');
                  const chatFolderCountBadge = document.getElementById('chatFolderCountBadge');
      
                  if (zipBase64 && zipMeta) {
                      let folderCheckboxesHTML = '';
                      if (zipFolders.length > 0) {
                          folderCheckboxesHTML = `
                              <div class="folder-picker-section">
                                  <div class="folder-picker-header">
                                      <span style="font-weight: 700; font-size: 0.9rem;"><i class="fa-solid fa-folder-tree"></i> בחר תיקיות לשליחה כהקשר (${selectedFolders.length}/${zipFolders.length}):</span>
                                      <div style="display: flex; gap: 8px;">
                                          <button class="tab-btn" style="padding: 3px 10px; font-size: 0.78rem;" onclick="selectAllFolders(true)">בחר הכל</button>
                                          <button class="tab-btn" style="padding: 3px 10px; font-size: 0.78rem;" onclick="selectAllFolders(false)">נקה הכל</button>
                                      </div>
                                  </div>
                                  <div class="folder-picker-grid">
                                      ${zipFolders.map(folder => `
                                          <label class="folder-checkbox-item">
                                              <input type="checkbox" value="${escapeHtml(folder)}" ${selectedFolders.includes(folder) ? 'checked' : ''} onchange="toggleFolderSelection('${escapeHtml(folder)}')">
                                              <i class="fa-regular fa-folder" style="color: var(--accent-glow);"></i>
                                              <span>${escapeHtml(folder)}</span>
                                          </label>
                                      `).join('')}
                                  </div>
                              </div>
                          `;
                      }
      
                      statusBox.className = 'zip-status-container has-file';
                      statusBox.innerHTML = `
                          <div class="zip-info-row">
                              <div class="zip-info-details">
                                  <i class="fa-solid fa-circle-check zip-icon"></i>
                                  <div class="zip-info-text">
                                      <div class="title">קובץ תיעוד פעיל בדפדפן: ${escapeHtml(zipMeta.name)}</div>
                                      <div class="sub">גודל: ${zipMeta.size} | סה"כ ${zipFolders.length} תיקיות זוהו</div>
                                  </div>
                              </div>
                              <div class="zip-actions">
                                  <button class="tab-btn active" onclick="document.getElementById('zipFileInput').click()">
                                      <i class="fa-solid fa-arrows-rotate"></i> החלף ZIP
                                  </button>
                                  <button class="tab-btn" style="color: #ef4444; border-color: rgba(239, 68, 68, 0.3);" onclick="removeZipFile()">
                                      <i class="fa-solid fa-trash"></i> הסר
                                  </button>
                              </div>
                          </div>
                          ${folderCheckboxesHTML}
                      `;
      
                      topbarBadge.className = 'topbar-status-badge active';
                      const folderCountText = selectedFolders.length === zipFolders.length ? 'כל התיקיות' : `${selectedFolders.length}/${zipFolders.length} תיקיות`;
                      topbarText.innerText = `תיעוד פעיל (${folderCountText})`;
      
                      if (zipFolders.length > 0) {
                          chatFolderContainer.style.display = 'inline-block';
                          chatFolderCountBadge.innerText = `${selectedFolders.length}/${zipFolders.length}`;
                          
                          chatFolderGrid.innerHTML = zipFolders.map(folder => `
                              <label class="folder-checkbox-item" style="padding: 5px 8px; font-size: 0.8rem;">
                                  <input type="checkbox" value="${escapeHtml(folder)}" ${selectedFolders.includes(folder) ? 'checked' : ''} onchange="toggleFolderSelection('${escapeHtml(folder)}')">
                                  <i class="fa-regular fa-folder" style="color: var(--accent-glow);"></i>
                                  <span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">${escapeHtml(folder)}</span>
                              </label>
                          `).join('');
                      } else {
                          chatFolderContainer.style.display = 'none';
                      }
      
                  } else {
                      statusBox.className = 'zip-status-container';
                      statusBox.innerHTML = `
                          <div class="zip-info-row" style="justify-content: center; text-align: center; padding: 10px 0;">
                              <div>
                                  <i class="fa-solid fa-cloud-arrow-up" style="font-size: 2.2rem; color: var(--text-muted); margin-bottom: 8px;"></i>
                                  <div style="font-weight: 700; margin-bottom: 4px;">טרם נטען קובץ ZIP של תיעוד ימות המשיח</div>
                                  <div style="font-size: 0.84rem; color: var(--text-muted); margin-bottom: 12px;">העלה את קובץ התיעוד כדי לאפשר סינון תיקיות ממוקד בדפדפן</div>
                                  <button class="tab-btn active" onclick="document.getElementById('zipFileInput').click()" style="margin: 0 auto;">
                                      <i class="fa-solid fa-upload"></i> בחר קובץ ZIP להעלאה
                                  </button>
                              </div>
                          </div>
                      `;
      
                      topbarBadge.className = 'topbar-status-badge';
                      topbarText.innerText = 'אין תיעוד ZIP טעון';
                      chatFolderContainer.style.display = 'none';
                  }
              }
      
              async function handleZipUpload(event) {
                  const file = event.target.files[0];
                  if (!file) return;
                  const reader = new FileReader();
                  reader.onload = async function(e) {
                      zipBase64 = e.target.result.split(',')[1];
                      zipMeta = {
                          name: file.name,
                          size: (file.size / 1024).toFixed(1) + ' KB'
                      };
                      
                      await analyzeZipFolders(zipBase64);
      
                      try {
                          localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_BASE64, zipBase64);
                          localStorage.setItem(LOCAL_STORAGE_KEYS.ZIP_META, JSON.stringify(zipMeta));
                      } catch (err) {
                          console.warn('Zip file size exceeds LocalStorage quota; keeping in active RAM context.');
                      }
                      updateZipStatusUI();
                  };
                  reader.readAsDataURL(file);
                  event.target.value = '';
              }
      
              function toggleFolderSelection(folder) {
                  if (selectedFolders.includes(folder)) {
                      selectedFolders = selectedFolders.filter(f => f !== folder);
                  } else {
                      selectedFolders.push(folder);
                  }
                  localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  updateZipStatusUI();
              }
      
              function selectAllFolders(selectAll) {
                  selectedFolders = selectAll ? [...zipFolders] : [];
                  localStorage.setItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS, JSON.stringify(selectedFolders));
                  updateZipStatusUI();
              }
      
              function removeZipFile() {
                  if (!confirm('האם להסיר את קובץ ה-ZIP של התיעוד?')) return;
                  zipBase64 = null;
                  zipMeta = null;
                  zipFolders = [];
                  selectedFolders = [];
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_BASE64);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_META);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.ZIP_FOLDERS);
                  localStorage.removeItem(LOCAL_STORAGE_KEYS.SELECTED_FOLDERS);
                  updateZipStatusUI();
              }
      
              // --- Generate Filtered ZIP Payload On-The-Fly ---
              async function getFilteredZipBase64() {
                  if (!zipBase64) return null;
                  if (zipFolders.length === 0 || selectedFolders.length === zipFolders.length) {
                      return zipBase64;
                  }
                  if (selectedFolders.length === 0) {
                      return null;
                  }
      
                  try {
                      const sourceZip = new JSZip();
                      await sourceZip.loadAsync(zipBase64, { base64: true });
      
                      const filteredZip = new JSZip();
                      const promises = [];
      
                      sourceZip.forEach((relativePath, file) => {
                          const topFolder = relativePath.split('/')[0];
                          if (selectedFolders.includes(topFolder) && !file.dir) {
                              const p = file.async('uint8array').then(content => {
                                  filteredZip.file(relativePath, content);
                              });
                              promises.push(p);
                          }
                      });
      
                      await Promise.all(promises);
                      return await filteredZip.generateAsync({ type: 'base64' });
                  } catch (err) {
                      console.error("Error generating filtered ZIP:", err);
                      return zipBase64;
                  }
              }
      
              // --- File Uploads & Drag-and-Drop ---
              function handleGenericFileUpload(event) {
                  const files = Array.from(event.target.files);
                  processFiles(files);
                  event.target.value = '';
              }
      
              function initDragAndDrop() {
                  const textarea = document.getElementById('userInput');
                  ['dragenter', 'dragover'].forEach(eventName => {
                      textarea.addEventListener(eventName, (e) => {
                          e.preventDefault();
                          textarea.style.borderColor = 'var(--accent-glow)';
                      }, false);
                  });
                  ['dragleave', 'drop'].forEach(eventName => {
                      textarea.addEventListener(eventName, (e) => {
                          e.preventDefault();
                          textarea.style.borderColor = 'var(--panel-border)';
                      }, false);
                  });
                  textarea.addEventListener('drop', (e) => {
                      const dt = e.dataTransfer;
                      const files = Array.from(dt.files);
                      if (files.length > 0) processFiles(files);
                  });
              }
      
              function processFiles(files) {
                  files.forEach(file => {
                      const reader = new FileReader();
                      reader.onload = function(e) {
                          const base64Data = e.target.result.split(',')[1];
                          let mimeType = file.type || getFallbackMime(file.name);
                          pendingAttachments.push({
                              name: file.name,
                              mimeType: mimeType,
                              data: base64Data,
                              size: (file.size / 1024).toFixed(1) + ' KB'
                          });
                          renderAttachmentsPreview();
                      };
                      reader.readAsDataURL(file);
                  });
              }
      
              function getFallbackMime(filename) {
                  const ext = filename.split('.').pop().toLowerCase();
                  const map = {
                      'ini': 'text/plain', 'txt': 'text/plain', 'wav': 'audio/wav',
                      'mp3': 'audio/mp3', 'pdf': 'application/pdf', 'csv': 'text/csv',
                      'json': 'application/json', 'zip': 'application/zip',
                      'js': 'text/javascript', 'php': 'text/x-php', 'png': 'image/png',
                      'jpg': 'image/jpeg', 'jpeg': 'image/jpeg'
                  };
                  return map[ext] || 'application/octet-stream';
              }
      
              function removeAttachment(index) {
                  pendingAttachments.splice(index, 1);
                  renderAttachmentsPreview();
              }
      
              function renderAttachmentsPreview() {
                  const previewEl = document.getElementById('attachmentsPreview');
                  previewEl.innerHTML = '';
                  pendingAttachments.forEach((att, idx) => {
                      const chip = document.createElement('div');
                      chip.className = 'att-chip';
                      chip.innerHTML = `
                          <i class="fa-solid fa-file"></i>
                          <span>${escapeHtml(att.name)} (${att.size})</span>
                          <span class="remove-att" onclick="removeAttachment(${idx})">&times;</span>
                      `;
                      previewEl.appendChild(chip);
                  });
              }
      
              // --- Zen & Themes ---
              function toggleZenMode() {
                  document.body.classList.toggle('zen-mode');
              }
      
              document.addEventListener('keydown', (e) => {
                  if (e.key === 'Escape' && document.body.classList.contains('zen-mode')) {
                      toggleZenMode();
                  }
              });
      
              function changeTheme(theme) {
                  document.body.setAttribute('data-theme', theme);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.THEME, theme);
              }
      
              function changeFontSize(size) {
                  document.body.setAttribute('data-font', size);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.FONT_SIZE, size);
              }
      
              function loadThemePreferences() {
                  const savedTheme = localStorage.getItem(LOCAL_STORAGE_KEYS.THEME) || 'cyberpunk';
                  const savedFont = localStorage.getItem(LOCAL_STORAGE_KEYS.FONT_SIZE) || 'medium';
                  changeTheme(savedTheme);
                  changeFontSize(savedFont);
                  document.getElementById('themeSelect').value = savedTheme;
                  document.getElementById('fontSelect').value = savedFont;
              }
      
              function handleChatScroll() {
                  const container = document.getElementById('chatContainer');
                  const jumpBtn = document.getElementById('jumpBottomBtn');
                  const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
                  if (distanceFromBottom > 200) {
                      jumpBtn.classList.add('show');
                  } else {
                      jumpBtn.classList.remove('show');
                  }
              }
      
              function scrollToBottom(smooth = true) {
                  const container = document.getElementById('chatContainer');
                  container.scrollTo({ top: container.scrollHeight, behavior: smooth ? 'smooth' : 'auto' });
              }
      
              // --- Session Engine ---
              function loadSessions() {
                  try {
                      const saved = localStorage.getItem(LOCAL_STORAGE_KEYS.SESSIONS);
                      sessions = saved ? JSON.parse(saved) : [];
                  } catch (e) { sessions = []; }
      
                  if (sessions.length === 0) {
                      createNewSession();
                  } else {
                      currentSessionId = sessions[0].id;
                      renderSessionsList();
                      renderCurrentSessionChat();
                  }
              }
      
              function saveSessions() {
                  try {
                      localStorage.setItem(LOCAL_STORAGE_KEYS.SESSIONS, JSON.stringify(sessions));
                  } catch (e) { console.warn('LocalStorage limit reached.'); }
                  renderSessionsList();
              }
      
              function createNewSession() {
                  const newSession = {
                      id: Date.now().toString(),
                      title: 'שיחה חדשה',
                      timestamp: new Date().toLocaleTimeString('he-IL', {hour: '2-digit', minute:'2-digit'}),
                      history: []
                  };
                  sessions.unshift(newSession);
                  currentSessionId = newSession.id;
                  saveSessions();
                  renderCurrentSessionChat();
              }
      
              function switchSession(id) {
                  currentSessionId = id;
                  renderSessionsList();
                  renderCurrentSessionChat();
              }
      
              function deleteSession(id, event) {
                  event.stopPropagation();
                  sessions = sessions.filter(s => s.id !== id);
                  if (sessions.length === 0) {
                      createNewSession();
                  } else {
                      if (currentSessionId === id) currentSessionId = sessions[0].id;
                      saveSessions();
                      renderCurrentSessionChat();
                  }
              }
      
              function renderSessionsList() {
                  const listEl = document.getElementById('historyList');
                  listEl.innerHTML = '';
                  sessions.forEach(s => {
                      const item = document.createElement('div');
                      item.className = `history-item ${s.id === currentSessionId ? 'active' : ''}`;
                      item.onclick = () => switchSession(s.id);
                      item.innerHTML = `
                          <span class="title"><i class="fa-regular fa-message"></i> ${escapeHtml(s.title)}</span>
                          <i class="fa-solid fa-trash delete-btn" onclick="deleteSession('${s.id}', event)" title="מחק שיחה"></i>
                      `;
                      listEl.appendChild(item);
                  });
              }
      
              function getCurrentSession() {
                  return sessions.find(s => s.id === currentSessionId);
              }
      
              function renderCurrentSessionChat() {
                  const container = document.getElementById('chatContainer');
                  container.innerHTML = `
                      <div class="message model">
                          שלום! אני עוזר ה-AI הבכיר שלך לבניית מרכזיות, שלוחות ו-Webhooks ב'ימות המשיח'.<br>
                          העלה קבצים, שאל שאלות, או סנן תיקיות תיעוד ספציפיות לקבלת קובצי <code>ext.ini</code> מדויקים!
                      </div>`;
      
                  const session = getCurrentSession();
                  if (!session) return;
      
                  session.history.forEach(msg => {
                      if (msg.role === 'user') {
                          appendMessageUI('user', msg.text, msg.attachedNames || [], false);
                      } else if (msg.role === 'model') {
                          renderModelResponseUI(msg.text);
                      }
                  });
                  scrollToBottom(false);
              }
      
              // --- Keys & Storage ---
              function loadKeys() {
                  const saved = localStorage.getItem(LOCAL_STORAGE_KEYS.API_KEYS);
                  if (saved) {
                      apiKeys = JSON.parse(saved);
                      document.getElementById('apiKeysInput').value = apiKeys.join('\n');
                  }
              }
      
              async function saveKeys() {
                  const lines = document.getElementById('apiKeysInput').value.split('\n').map(k => k.trim()).filter(k => k.length > 0);
                  apiKeys = lines;
                  localStorage.setItem(LOCAL_STORAGE_KEYS.API_KEYS, JSON.stringify(apiKeys));
                  alert('מפתחות ה-API נשמרו בדפדפן בהצלחה!');
                  if (gasProxyUrl && apiKeys.length > 0) fetchModels();
              }
      
              function saveYemotCredentials() {
                  localStorage.setItem(LOCAL_STORAGE_KEYS.YM_USER, document.getElementById('ymUsername').value);
                  localStorage.setItem(LOCAL_STORAGE_KEYS.YM_PASS, document.getElementById('ymPassword').value);
                  alert('פרטי החיבור נשמרו בדפדפן!');
              }
      
              function loadYemotCredentials() {
                  document.getElementById('ymUsername').value = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_USER) || '';
                  document.getElementById('ymPassword').value = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_PASS) || '';
              }
      
              // --- Speech Input with Complete Event Handlers ---
              function initSpeechRecognition() {
                  if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
                      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
                      recognition = new SpeechRecognition();
                      recognition.lang = 'he-IL';
                      
                      recognition.onresult = (event) => {
                          const transcript = event.results[0][0].transcript;
                          const inputEl = document.getElementById('userInput');
                          inputEl.value += (inputEl.value ? ' ' : '') + transcript;
                      };
      
                      recognition.onend = () => {
                          isRecording = false;
                          const micBtn = document.getElementById('micBtn');
                          if (micBtn) micBtn.classList.remove('recording');
                      };
      
                      recognition.onerror = () => {
                          isRecording = false;
                          const micBtn = document.getElementById('micBtn');
                          if (micBtn) micBtn.classList.remove('recording');
                      };
                  } else {
                      document.getElementById('micBtn').style.display = 'none';
                  }
              }
      
              function toggleVoiceInput() {
                  if (!recognition) return;
                  const micBtn = document.getElementById('micBtn');
                  if (!isRecording) {
                      try {
                          recognition.start();
                          isRecording = true;
                          micBtn.classList.add('recording');
                      } catch(e) { console.warn("Recognition start failed", e); }
                  } else {
                      recognition.stop();
                      isRecording = false;
                      micBtn.classList.remove('recording');
                  }
              }
      
              // --- Message Dispatcher via GAS Proxy ---
              async function sendMessage() {
                  const inputEl = document.getElementById('userInput');
                  const text = inputEl.value.trim();
                  if (!text && pendingAttachments.length === 0) return;
      
                  // Soft-check settings without blocking loops
                  if (!gasProxyUrl) {
                      appendMessageUI('model', `
                          <div style="color: #f59e0b; padding: 6px 0;">
                              <i class="fa-solid fa-triangle-exclamation" style="font-size: 1.2rem; margin-left: 6px;"></i>
                              <b>טרם הוגדרה כתובת Google Script Proxy:</b><br>
                              עבור בלשונית <a href="javascript:void(0)" onclick="switchTab('settingsTab')" style="color:#38bdf8; font-weight:bold;">הגדרות וגוגל סקריפט</a>, הדבק את כתובת ה-Web App ולחץ על "שמור".
                          </div>
                      `, [], true);
                      scrollToBottom();
                      return;
                  }
      
                  if (apiKeys.length === 0) {
                      appendMessageUI('model', `
                          <div style="color: #f59e0b; padding: 6px 0;">
                              <i class="fa-solid fa-key" style="font-size: 1.2rem; margin-left: 6px;"></i>
                              <b>חסר מפתח API:</b><br>
                              אנא הכנס לפחות מפתח Gemini API אחד בלשונית <a href="javascript:void(0)" onclick="switchTab('settingsTab')" style="color:#38bdf8; font-weight:bold;">הגדרות וגוגל סקריפט</a>.
                          </div>
                      `, [], true);
                      scrollToBottom();
                      return;
                  }
      
                  const sendBtn = document.getElementById('sendBtn');
                  const deepAnalysis = document.getElementById('deepAnalysisToggle').checked;
                  const selectedModel = document.getElementById('modelSelect').value || 'gemini-3.6-flash';
      
                  const session = getCurrentSession();
                  if (session.history.length === 0) {
                      session.title = text ? (text.substring(0, 25) + '...') : (pendingAttachments[0]?.name || 'קובץ מצורף');
                  }
      
                  const currentTurnAttachments = [...pendingAttachments];
                  const attachedNames = currentTurnAttachments.map(a => a.name);
      
                  appendMessageUI('user', text || '(קבצים מצורפים)', attachedNames, false);
                  inputEl.value = '';
                  pendingAttachments = [];
                  renderAttachmentsPreview();
                  sendBtn.disabled = true;
                  scrollToBottom();
      
                  session.history.push({ role: 'user', text: text || '(קבצים מצורפים)', attachedNames: attachedNames });
                  saveSessions();
      
                  const loadingDiv = appendMessageUI('model', '<div class="loading-shimmer"><i class="fa-solid fa-spinner"></i> <span>שולח דרך Google Script Proxy ומנתח תיעוד...</span></div>', [], true);
                  scrollToBottom();
      
                  try {
                      const effectiveZipBase64 = await getFilteredZipBase64();
      
                      let contentsPayload = [];
      
                      session.history.forEach((msg, idx) => {
                          let parts = [];
                          
                          if (idx === 0 && msg.role === 'user' && effectiveZipBase64) {
                              parts.push({
                                  inline_data: {
                                      mime_type: "application/zip",
                                      data: effectiveZipBase64
                                  }
                              });
                          }
      
                          if (idx === session.history.length - 1 && msg.role === 'user' && currentTurnAttachments.length > 0) {
                              currentTurnAttachments.forEach(att => {
                                  parts.push({
                                      inline_data: {
                                          mime_type: att.mimeType,
                                          data: att.data
                                      }
                                  });
                              });
                          }
      
                          let promptText = msg.text;
                          if (idx === session.history.length - 1 && msg.role === 'user' && deepAnalysis) {
                              promptText = `[הוראת חובה לניתוח מעמיק של הפורמט]:
      לפני מתן התשובה והקוד הסופיים, עליך לבצע ניתוח מקיף ומעמיק של הדרישה, הפורמט המבוקש (כגון ext.ini, HTML, Webhook, תפריטי IVR), לוגיקת הרכיבים, בדיקת קצה ומבנה הנתונים.
      חובה לעטוף את *כל* הניתוח המעמיק בתוך תגיות <analysis> ו-</analysis>.
      רק לאחר התגית הסוגרת </analysis> תציג את התשובה והקוד המלאים והסופיים.
      
      בקשת המשתמש:
      ${msg.text}`;
                          }
      
                          parts.push({ text: promptText });
                          contentsPayload.push({ role: msg.role, parts: parts });
                      });
      
                      const payloadBody = {
                          system_instruction: { parts: [{ text: SYSTEM_INSTRUCTION }] },
                          contents: contentsPayload
                      };
      
                      const responseData = await makeGeminiApiCall(
                          (key) => `https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${key}`,
                          'POST',
                          () => payloadBody
                      );
      
                      const replyText = responseData?.candidates?.[0]?.content?.parts?.[0]?.text;
                      if (!replyText) throw new Error('התקבלה תשובה ריקה משרת ה-Proxy.');
      
                      session.history.push({ role: 'model', text: replyText });
                      saveSessions();
      
                      loadingDiv.remove();
                      renderModelResponseUI(replyText);
                      scrollToBottom();
      
                  } catch (err) {
                      let errDesc = err.message;
                      if (err.message === 'MISSING_GAS_URL') {
                          errDesc = 'כתובת הסקריפט חסרה. אנא הגדר אותה בלשונית ההגדרות.';
                      } else if (err.message === 'MISSING_KEYS') {
                          errDesc = 'מפתח API חסר. אנא הכנס מפתח בלשונית ההגדרות.';
                      }
                      loadingDiv.innerHTML = `<span style="color:#ef4444;"><i class="fa-solid fa-circle-exclamation"></i> שגיאה: ${escapeHtml(errDesc)}</span>`;
                  } finally {
                      sendBtn.disabled = false;
                  }
              }
      
              // Render AI Model Response
              function renderModelResponseUI(text) {
                  const container = document.getElementById('chatContainer');
                  const msgDiv = document.createElement('div');
                  msgDiv.className = 'message model';
      
                  let analysisHTML = '';
                  let mainContent = text;
      
                  const analysisMatch = text.match(/<analysis>([\s\S]*?)<\/analysis>/);
                  if (analysisMatch) {
                      const analysisText = analysisMatch[1].trim();
                      mainContent = text.replace(/<analysis>[\s\S]*?<\/analysis>/, '').trim();
      
                      analysisHTML = `
                          <div class="analysis-box">
                              <div class="analysis-header" onclick="this.parentElement.classList.toggle('open')">
                                  <span><i class="fa-solid fa-brain"></i> ניתוח מעמיק של הפורמט והלוגיקה (לחץ לפתיחה)</span>
                                  <i class="fa-solid fa-chevron-down"></i>
                              </div>
                              <div class="analysis-content">${escapeHtml(analysisText)}</div>
                          </div>
                      `;
                  }
      
                  msgDiv.innerHTML = analysisHTML + marked.parse(mainContent);
                  container.appendChild(msgDiv);
      
                  // Enhance Code Blocks
                  msgDiv.querySelectorAll('pre code').forEach((codeBlock) => {
                      const rawCode = codeBlock.innerText;
                      const parentPre = codeBlock.parentNode;
      
                      const wrapper = document.createElement('div');
                      wrapper.className = 'code-container';
      
                      const header = document.createElement('div');
                      header.className = 'code-header';
                      header.innerHTML = `<span><i class="fa-solid fa-code"></i> קוד תצורה (ext.ini)</span>`;
      
                      const actions = document.createElement('div');
                      actions.className = 'code-actions';
      
                      const copyBtn = document.createElement('button');
                      copyBtn.className = 'code-btn';
                      copyBtn.innerHTML = '<i class="fa-solid fa-copy"></i> העתק';
                      copyBtn.onclick = () => { navigator.clipboard.writeText(rawCode); copyBtn.innerHTML = '<i class="fa-solid fa-check"></i> הועתק!'; setTimeout(() => copyBtn.innerHTML = '<i class="fa-solid fa-copy"></i> העתק', 2000); };
      
                      const dlBtn = document.createElement('button');
                      dlBtn.className = 'code-btn';
                      dlBtn.innerHTML = '<i class="fa-solid fa-download"></i> הורד ext.ini';
                      dlBtn.onclick = () => downloadCodeFile(rawCode, 'ext.ini');
      
                      const uploadBtn = document.createElement('button');
                      uploadBtn.className = 'code-btn';
                      uploadBtn.style.color = '#10b981';
                      uploadBtn.innerHTML = '<i class="fa-solid fa-cloud-arrow-up"></i> העלה למרכזיה';
                      uploadBtn.onclick = () => uploadDirectToYemot(rawCode);
      
                      const geminiTtsBtn = document.createElement('button');
                      geminiTtsBtn.className = 'code-btn';
                      geminiTtsBtn.style.color = '#38bdf8';
                      geminiTtsBtn.innerHTML = '<i class="fa-solid fa-wand-magic-sparkles"></i> שמע Gemini AI';
                      geminiTtsBtn.onclick = () => generateGeminiAudio(rawCode);
      
                      actions.appendChild(copyBtn);
                      actions.appendChild(dlBtn);
                      actions.appendChild(geminiTtsBtn);
                      actions.appendChild(uploadBtn);
      
                      header.appendChild(actions);
                      wrapper.appendChild(header);
      
                      parentPre.parentNode.insertBefore(wrapper, parentPre);
                      wrapper.appendChild(parentPre);
      
                      hljs.highlightElement(codeBlock);
                  });
              }
      
              // Audio TTS via Client Synthesis
              async function generateGeminiAudio(code) {
                  let textToRead = prompt('הכנס את הטקסט שברצונך שה-Gemini AI יכין ויקריא עבור המרכזייה:', 'ברוכים הבאים למערכת שלנו');
                  if (!textToRead) return;
      
                  const selectedModel = document.getElementById('modelSelect').value || 'gemini-3.6-flash';
      
                  try {
                      const responseData = await makeGeminiApiCall(
                          (key) => `https://generativelanguage.googleapis.com/v1beta/models/${selectedModel}:generateContent?key=${key}`,
                          'POST',
                          () => ({
                              contents: [{
                                  parts: [{ text: `עבד והכשר את הניסוח הבא לקריאה קולית נקייה ומדויקת בלבד במרכזיה (ללא סימני פיסוק או הערות נוספות):\n"${textToRead}"` }]
                              }]
                          })
                      );
      
                      const speechText = responseData?.candidates?.[0]?.content?.parts?.[0]?.text || textToRead;
                      
                      window.speechSynthesis.cancel();
                      const utterance = new SpeechSynthesisUtterance(speechText);
                      utterance.lang = 'he-IL';
                      utterance.rate = 0.95;
                      window.speechSynthesis.speak(utterance);
      
                  } catch (err) {
                      window.speechSynthesis.cancel();
                      const utterance = new SpeechSynthesisUtterance(textToRead);
                      utterance.lang = 'he-IL';
                      window.speechSynthesis.speak(utterance);
                  }
              }
      
              // Direct Yemot Push (Client Side)
              async function uploadDirectToYemot(code) {
                  const username = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_USER);
                  const password = localStorage.getItem(LOCAL_STORAGE_KEYS.YM_PASS);
      
                  if (!username || !password) {
                      alert('אנא הגדר פרטי התחברות לימות המשיח בלשונית ההגדרות.');
                      switchTab('settingsTab');
                      return;
                  }
      
                  const path = prompt('הכנס את נתיב השלוחה להעלאה (לדוגמה: 1/2 או 0 לראשית):', '1');
                  if (!path) return;
      
                  try {
                      const loginRes = await fetch(`https://www.call2all.co.il/ym/api/Login?username=${username}&password=${password}`);
                      const loginData = await loginRes.json();
      
                      if (loginData.responseStatus !== 'OK') {
                          throw new Error('התחברות למערכת ימות המשיח נכשלה.');
                      }
      
                      const token = loginData.token;
                      const formData = new FormData();
                      const blob = new Blob([code], { type: 'text/plain' });
                      formData.append('file', blob, 'ext.ini');
      
                      const uploadRes = await fetch(`https://www.call2all.co.il/ym/api/UploadFile?token=${token}&path=ivr2:${path}/ext.ini`, {
                          method: 'POST',
                          body: formData
                      });
                      const uploadData = await uploadRes.json();
      
                      if (uploadData.responseStatus === 'OK') {
                          alert(`הקוד הועלה בהצלחה לשלוחה ${path}!`);
                      } else {
                          alert(`שגיאה: ${uploadData.message || 'תקלה לא ידועה'}`);
                      }
      
                  } catch (err) {
                      alert(`שגיאה: ${err.message}`);
                  }
              }
      
              function downloadCodeFile(code, filename) {
                  const blob = new Blob([code], { type: 'text/plain;charset=utf-8' });
                  const link = document.createElement('a');
                  link.href = URL.createObjectURL(blob);
                  link.download = filename;
                  link.click();
              }
      
              function appendMessageUI(role, content, attachedNames = [], isHTML = false) {
                  const container = document.getElementById('chatContainer');
                  const msgDiv = document.createElement('div');
                  msgDiv.className = `message ${role}`;
                  
                  let attachmentsHTML = '';
                  if (attachedNames && attachedNames.length > 0) {
                      attachmentsHTML = '<div class="message-attachments">' + 
                          attachedNames.map(n => `<span class="msg-att-chip"><i class="fa-solid fa-paperclip"></i> ${escapeHtml(n)}</span>`).join('') + 
                          '</div>';
                  }
      
                  const bodyContent = isHTML ? content : `<div>${escapeHtml(content)}</div>`;
                  msgDiv.innerHTML = attachmentsHTML + bodyContent;
                  container.appendChild(msgDiv);
                  return msgDiv;
              }
      
              function handleKeyDown(e) {
                  if (e.key === 'Enter' && !e.shiftKey && !e.isComposing) {
                      e.preventDefault();
                      sendMessage();
                  }
              }
      
              function escapeHtml(text) {
                  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
              }
          </script>
      </body>
      </html>
      

      להורדה יש למחוק מהסיומת את "מיותר"

      ניתן להגיב כאן

      |-Gemini_Generated_Image_3rcgov3rcgov3rcg.png-|

      פורסם בהסברים מסודרים ממשתמשים בינה מלאכותית
      ה
      הדיבל המעופף
    • תגובות | עוזר AI מדויק עבור ימות המשיח! בדף html! גירסה שתיים!!

      תגובות לעוזר AI מדויק עבור ימות המשיח! בדף html!


      |-Gemini_Generated_Image_3rcgov3rcgov3rcg.png-|

      פורסם בעזרה הדדית למשתמשים מתקדמים בינה מלאכותית
      ה
      הדיבל המעופף
    • מודל שמיעת וניהול המאזינים דרך הטלפון - כולל העברת וניתוק מאזינים

      זה הקוד לשלוחה

      type=api
      api_link=https://listener-management.a775529301.workers.dev
      api_add_0=token=טוקן
      api_url_post=yes
      

      זה קוד השרת למעונינים

      /* yemot-listeners — ממשק טלפוני לניהול מאזינים בקו.
       * מאפשר: לשמוע מי מאזין כרגע לקו (שם אם קיים, אחרת מספר, ובאיזו שלוחה הוא
       * נמצא), לנתק מאזין, או להעביר אותו לשלוחה אחרת — הכל בשיחה אחת, עד
       * שהמנהל מנתק את עצמו.
       *
       * סטטלס לגמרי: אין KV, אין Durable Object, אין שום Binding. אין שום דבר
       * להגדיר מלבד להדביק את הקוד ולפרוס.
       *
       * איך זה עובד בלי אחסון:
       * ימות שולח, בכל בקשה חוזרת בתוך אותה שיחה, את כל מה שנענה עד עכשיו יחד עם
       * הבקשה. לכן, כששואלים "לאיזה מאזין?" ואז "לאיזו שלוחה?", במקום לשמור
       * באיזה מאזין בחרו — מקודדים את הזיהוי שלו (id, האם חדר ועידה, טלפון, שם)
       * בתוך *שם השדה* של השאלה השנייה, וימות מחזיר אותו בדיוק כפי שהוא. חזרה
       * לתפריט אחרי טיפול במאזין נעשית עם go_to_folder לאותה שלוחה, שמתחיל
       * "סבב API" חדש ונקי, בדיוק כמו כניסה ראשונה.
       *
       * שם מאזין: לפי תיעוד ימות, callerIdNum כולל את ה-val_name (שם משויך
       * למספר) במידה וקיים במערכת וקיים login_add_val_name=yes. אם יש שם, הוא
       * יישמע במקום המספר. אם אצלך התכונה הזו לא מופעלת, פשוט תמיד יישמע המספר.
       *
       * שלוחה נוכחית: שדה path שמוחזר מ-GetIncomingCalls (מספר שלוחה + תיאור אם
       * יש). מוקרא כטקסט (t-) כי הוא לא בהכרח מספר "נקי" (יכול לכלול / ותיאור).
       *
       * אם ידוע לך באיזו שלוחה מוגדר הסקריפט הזה (type=api), אפשר לקבע כאן את
       * הנתיב (בלי סלאש בהתחלה, למשל "9" או "5/8") כדי לחזור אליה נקי בסיום כל
       * פעולה. אם משאירים ריק, ננסה את הפרמטר ApiExtension שהמערכת שולחת
       * אוטומטית בכל בקשה (יש לוודא ש-api_extension_send לא הוגדר no).
       */
      const SELF_EXTENSION_PATH = ""; // לדוגמה: "9"
      
      function freshSuffix() {
        return `${Date.now()}_${Math.floor(Math.random() * 1e4)}`;
      }
      
      function toHex(str) {
        const bytes = new TextEncoder().encode(str);
        let hex = "";
        for (const b of bytes) hex += b.toString(16).padStart(2, "0");
        return hex;
      }
      
      function fromHex(hex) {
        const bytes = new Uint8Array((hex || "").length / 2);
        for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
        return new TextDecoder().decode(bytes);
      }
      
      // מסיר תווים שמשמשים כתוחמים בתחביר של ימות (t-/d-/./=/&) כדי שטקסט חופשי
      // (שם מאזין, תיאור שלוחה) לא ישבור את ההודעה.
      function sanitizeSpeech(s) {
        return String(s || "").replace(/[.,\-=&]/g, " ").replace(/\s+/g, " ").trim();
      }
      
      // מפריד את מספר הטלפון מתוך callerIdNum מהשם המשויך (val_name) אם יש —
      // למשל "0533165488(זיהוי מוטי קרמר)". שומר על רווחים בין מילות השם (כדי
      // לא לדבק "מוטי" ו"קרמר" למילה אחת), ומוריד את תווית "זיהוי" שבתחילתו.
      function extractPhoneAndName(raw) {
        const s = String(raw || "");
        const phone = s.replace(/\D/g, "").slice(0, 10);
        let name = s
          .replace(/\d+/g, " ")
          .replace(/[()+\-:]/g, " ")
          .replace(/\s+/g, " ")
          .trim();
        name = name.replace(/^זיהוי\s*/, "").trim();
        return { phone, name: sanitizeSpeech(name) };
      }
      
      function yemot(body, status = 200) {
        return new Response(body, {
          status,
          headers: { "Content-Type": "text/plain; charset=utf-8" },
        });
      }
      
      async function parseParams(request) {
        const p = new URL(request.url).searchParams;
        if (request.method === "POST") {
          const ct = request.headers.get("Content-Type") || "";
          try {
            if (ct.includes("application/json")) {
              const json = await request.json();
              for (const [k, v] of Object.entries(json || {})) if (!p.has(k)) p.set(k, v);
            } else {
              const form = await request.formData();
              for (const [k, v] of form.entries()) if (!p.has(k)) p.set(k, v);
            }
          } catch (_) {}
        }
        return p;
      }
      
      const YM_API_URL = "https://www.call2all.co.il/ym/api/";
      
      async function ym(token, cmd, params) {
        const res = await fetch(`${YM_API_URL}${cmd}`, {
          method: "POST",
          headers: { "Authorization": token, "Content-Type": "application/json" },
          body: JSON.stringify(params),
        });
        return { status: res.status, data: await res.json().catch(() => ({})) };
      }
      
      function extractListeners(calls) {
        const seen = new Set();
        const out = [];
        for (const call of calls || []) {
          const push = (id, callerIdNum, conf, path) => {
            if (!id || seen.has(id)) return;
            seen.add(id);
            const { phone, name } = extractPhoneAndName(callerIdNum);
            out.push({ id, phone, name, conf, path: sanitizeSpeech(path || "") });
          };
      
          const confList = Array.isArray(call.confCalls)
            ? call.confCalls
            : call.confCalls && typeof call.confCalls === "object"
            ? Object.values(call.confCalls)
            : null;
      
          if (confList) {
            for (const c of confList) {
              if (c && typeof c === "object" && c.id) push(c.id, c.callerIdNum, true, c.path || call.path);
            }
          } else {
            push(call.id, call.callerIdNum, false, call.path);
          }
        }
        return out;
      }
      
      // "t-מספרו.d-...." או "t-לא ידוע." אם אין בכלל מספר
      function whoPhrase(phone, name) {
        if (name) return `t-בשם.t-${name}.`;
        if (phone) return `t-מספרו.d-${phone}.`;
        return `t-מספר לא ידוע.`;
      }
      
      function selfReentryDirective(apiExtension) {
        const raw = SELF_EXTENSION_PATH || apiExtension || "";
        const ext = String(raw).replace(/^\/+/, "");
        return ext ? `g-/${ext}` : null;
      }
      
      /* שלב א: שליפת המאזינים + השמעת הרשימה (שם/מספר + שלוחה נוכחית) + בקשת בחירה */
      async function showMenu(token, prefix = "") {
        const { data } = await ym(token, "GetIncomingCalls", {});
        if (data.responseStatus !== "OK")
          return yemot("id_list_message=t-שגיאה בקבלת השיחות הפעילות מהמערכת");
      
        const listeners = extractListeners(data.calls);
        if (!listeners.length)
          return yemot(`id_list_message=${prefix}t-אין מאזינים בקו כרגע`);
      
        const shown = listeners.slice(0, 9);
        const hiddenCount = listeners.length - shown.length;
        const field = `Choice_${freshSuffix()}`;
      
        const menu = shown
          .map((l, i) => {
            const where = l.path ? `t-נמצא בשלוחה.t-${l.path}.` : "";
            return `t-מאזין.d-${i + 1}.${whoPhrase(l.phone, l.name)}${where}`;
          })
          .join("");
      
        const hiddenNote =
          hiddenCount > 0 ? `t-מוצגים תשעת הראשונים מתוך.d-${listeners.length}.` : "";
      
        const message =
          `${prefix}t-מספר המאזינים בקו הוא.d-${shown.length}.${hiddenNote}${menu}` +
          `t-להעברת מאזין הקישו את מספרו`;
      
        // read=<הודעה>=<שם>,<שימוש בערך קיים>,<מקס'>,<מינ'>,<שניות המתנה>,<אופן השמעה>
        return yemot(`read=${message}=${field},,1,1,,Digits`);
      }
      
      /* שלב ב: בקשת שלוחת יעד (או 0 לניתוק). כל המידע על המאזין הנבחר מקודד
         בתוך שם השדה עצמו, כדי שיחזור אלינו בבקשה הבאה בלי שנצטרך לשמור אותו. */
      function promptDest(id, conf, phone, name, prefix = "", choiceDigit = null) {
        const payload = `${toHex(id)}_${conf ? 1 : 0}_${phone || ""}_${toHex(name || "")}`;
        const field = `Dest_${payload}_${freshSuffix()}`;
      
        const who = choiceDigit
          ? `t-בחרתם במאזין.d-${choiceDigit}.${whoPhrase(phone, name)}`
          : `t-המאזין הנבחר.${whoPhrase(phone, name)}`;
      
        const msg =
          `${prefix}${who}t-הקישו את מספר השלוחה להעברה, לשלוחה מקוננת הקישו כוכבית במקום סלאש, ` +
          `לניתוק המאזין הקישו אפס, וסולמית לסיום`;
      
        // read=<הודעה>=<שם>,<שימוש בערך קיים>,<מקס'>,<מינ'>,<שניות המתנה>,<אופן השמעה>,
        //      <חסימת כוכבית>,<חסימת אפס>,<החלפת מקש> — "*/ " מחליף כוכבית בסלאש,
        //      כדי לאפשר להקיש שלוחה מקוננת כמו 5/8 (בטלפון אי אפשר להקיש סלאש עצמו).
        return yemot(`read=${msg}=${field},,10,1,15,Digits,,,*/`);
      }
      
      export default {
        async fetch(request, env) {
          try {
            return await handle(request, env);
          } catch (e) {
            const msg = String(e && e.message ? e.message : e).slice(0, 50).replace(/[.,\-=&]/g, " ");
            return yemot(`id_list_message=t-שגיאה בשרת.t-${msg}`);
          }
        },
      };
      
      async function handle(request, env) {
        if (!(request.headers.get("User-Agent") || "").startsWith("yemot-core-api"))
          return yemot("id_list_message=t-גישה נדחתה", 403);
      
        const p = await parseParams(request);
      
        // דיבוג (אופציונלי, לא דורש שום הגדרה): ?token=...&debug=yes[&secret=...]
        if (p.get("debug") === "yes") {
          if (env && env.DEBUG_SECRET && p.get("secret") !== env.DEBUG_SECRET)
            return yemot("id_list_message=t-גישה נדחתה", 403);
          const { status, data } = await ym(p.get("token") || "", "GetIncomingCalls", {});
          return new Response(JSON.stringify({ httpStatus: status, response: data }, null, 2), {
            headers: { "Content-Type": "application/json; charset=utf-8" },
          });
        }
      
        if (p.get("hangup") === "yes") return new Response("OK");
      
        const token = p.get("token") || "";
        if (!/^\d{7,}:[A-Za-z0-9]+$/.test(token))
          return yemot("id_list_message=t-שגיאת הזדהות. הטוקן חסר או לא תקין");
      
        const apiExtension = p.get("ApiExtension") || "";
      
        // שלב ג: התקבל מענה לבקשת שלוחת היעד (או 0 לניתוק).
        // המידע על המאזין (id, conf, phone, name) יושב בתוך שם השדה עצמו.
        const destKey = [...p.keys()].find((k) => k.startsWith("Dest_"));
        if (destKey) {
          const m = destKey.match(/^Dest_([0-9a-f]+)_([01])_(\d*)_([0-9a-f]*)_\d+_\d+$/);
          if (m) {
            const id = fromHex(m[1]);
            const conf = m[2] === "1";
            const phone = m[3];
            const name = fromHex(m[4]);
            const dest = p.get(destKey);
      
            // כוכבית שהוקשה הומרה כבר לסלאש ע"י ימות (ראה הגדרת read למעלה),
            // אז נתיב מקונן מגיע כאן כבר בתור "5/8" וכו'.
            if (!/^\d{1,10}(\/\d{1,10})*$/.test(dest)) {
              return promptDest(id, conf, phone, name, "t-שלוחה לא תקינה.");
            }
      
            const isHangup = dest === "0";
            const go = isHangup ? "hangup" : `/${dest}`;
      
            if (conf) {
              const r1 = await ym(token, "CallAction", { ids: id, "set:ConfbridgeNewGoto": go });
              if (r1.data.responseStatus !== "OK")
                return yemot("id_list_message=t-שגיאה בכיוון היציאה מהחדר");
              const r2 = await ym(token, "CallAction", { ids: id, action: "kick" });
              if (r2.data.responseStatus !== "OK")
                return yemot("id_list_message=t-שגיאה בהוצאה מהחדר");
            } else {
              const r = await ym(token, "CallAction", { ids: id, action: `set:GOasap=${go}` });
              if (r.data.responseStatus !== "OK")
                return yemot("id_list_message=t-שגיאה בהעברת המאזין");
            }
      
            const successText = isHangup ? "t-המאזין נותק." : `t-המאזין הועבר לשלוחה.d-${dest}.`;
            const reentry = selfReentryDirective(apiExtension);
            // חזרה לתפריט מעודכן דרך go_to_folder (סבב API חדש ונקי) — לא דרך
            // read חוזר, כי read לא יכול לשמש שוב לאותם שמות שדות.
            return yemot(reentry ? `id_list_message=${successText}${reentry}` : `id_list_message=${successText}`);
          }
        }
      
        // שלב ב: התקבל מענה לבחירת מאזין (ספרה 1-9). שולפים רשימה עדכנית
        // ובוחרים ממנה לפי המיקום - אין שום צורך לשמור את הרשימה של שלב א'.
        const choiceKey = [...p.keys()].find((k) => k.startsWith("Choice_"));
        if (choiceKey) {
          const choice = p.get(choiceKey);
          const { data } = await ym(token, "GetIncomingCalls", {});
          if (data.responseStatus !== "OK")
            return yemot("id_list_message=t-שגיאה בקבלת השיחות הפעילות מהמערכת");
          const listeners = extractListeners(data.calls).slice(0, 9);
          const idx = parseInt(choice, 10) - 1;
          const rec = listeners[idx];
          if (!rec) return showMenu(token, "t-בחירה לא תקינה.");
          return promptDest(rec.id, rec.conf, rec.phone, rec.name, "", choice);
        }
      
        // שלב א: כניסה ראשונה לשלוחה (או חזרה אליה אחרי go_to_folder)
        return showMenu(token);
      }
      
      
      פורסם בהסברים מסודרים ממשתמשים
      ה
      הדיבל המעופף
    • מודל לשמיעת דקות ויחידות שנכנסו למערכת שלכם (או במערכת אחרת) בחודש הנוכחי

      כבר פרסמתי פה באיזה שרשור אבל בכל זאת שיהיה מסודר

      type=api
      api_link=https://remaining-minutes.a775529301.workers.dev/
      api_add_0=token=טוקן מערכת
      

      תגובות

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

      ממשק חדש להגשת בקשות לפיתוח - הממשק פותח ע"י @איל-משולש האלוף !!!

      כניסה לממשק המטורף !!!

      הפוסט הישן בספוילר


      רוצים דף html לביצוע פעולות במערכת?

      רוצים גוגל סקריפט לתזמון פעולות בקו שלכם?

      רוצים מודל API משוכלל?

      בקשו פה ממשתמשי הפורום - והציבור ישמח לעזור !!!

      נ.ב בבקשה לא להספים את השרשור !!! יש לבקש אך ורק בקשות עניניות בצורה מסודרת !!!

      כדי שיהיה ברור מה הבקשות - כל בקשה יש לכתוב בתחילת הפוסט (קרדיט ל @איל-משולש על הסדר הבא)

      בקשה

      ע"י הדבקת הטקסט #anchor(בקשה) וירידת שתי שורות ואז הבקשה-|

      באדיבות גמיניי

      6e8f6544-f8ef-46c4-ba41-40b549faa70d-image.jpeg

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • RE: 🚀🔍 חדש! בדיקת API חיה – רואים בדיוק איפה התקלה

      @פתרון-AI
      ישכ"ח עצום !!
      היה לי שלוחת סקר והעלאתי סקר חדש, ופתאום זה לא עבד, הפחתי את כל העולם (על כל שרתיו) ולא מצאתי את הבעיה
      נכנסתי לזה וב10 שניות מצא את הבעיה !!!
      fc85e3fb-5a46-4ec3-b9fe-71248c60bee1-image.jpeg
      אני רואה שהצילום לא ברור - אז בתכלס הוא מצא שיש שם גרשיים וזה מפריע

      פורסם בפורום מפתחים API
      ה
      הדיבל המעופף
    • RE: משחק ילדים מהפכני חדש בטלפון הכשר!!!👍✨

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

      פורסם בבקשות לפיתוח
      ה
      הדיבל המעופף
    • RE: חיוג לחו"ל

      @השפן-הקטן
      ראיתי חיוג לארה"ב ללא עלות
      1700-077-307
      מקור

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

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

    • RE: סיכום שבועי מהפורום

      @שואל-שאלה
      מישהו יכול לשלוח לבדיקה?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • RE: לייק / דסלייק - ושכלולים נוספים!! 👍🎀🏅✨

      אפשרי. הנתונים נשמרים על השרת והוא מזהה לפי מספר הטלפון של הקו.

      פורסם בבקשות לפיתוח
      ה
      הדיבל המעופף
    • RE: הקלטה לשלוחה אחרת - הכפלה לשתי שלוחות

      אפשרי ב-2 דרכים:

      1. להגדיר שישכפל לינק לתיקייה השנייה תחפש בהגדרות המודל.
      2. בשלוחה של בחירת קובץ אפשר להגדיר שייקח את הקבצים משלוחה אחרת.

      _( _

      פורסם בשאלות ועזרה הדדית
      ה
      הדיבל המעופף
    • RE: לייק / דסלייק - ושכלולים נוספים!! 👍🎀🏅✨

      תשכח מפיתוח של ימות. אם לא תשלם, לא יהיה פיתוח.

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

      מה הכוונה נעלם משם? המשימה הזאתי או כל הדף של המשימות?

      _( _

      פורסם בבקשות לפיתוח
      ה
      הדיבל המעופף
    • RE: הודעת מערכת "שיחתכם חשובה לנו" (בשלוחת ניתוב למספר מוגדר מראש, למשל)

      האם בדקת בהגדרות מודל טור?

      _( _

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

      @חיה-שיר
      בעיקרון זה אפשרי, אבל לי אין זמן כרגע

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • RE: תגובות | מודל תזמון צינתוקים דרך הטלפון

      @אA
      (סליחה על האיחור הקטן😉) אבל... יש מודל רשמי דרך הטלפון !!! עיין כאן

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • RE: תגובות | מודל תזמון צינתוקים דרך הטלפון

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

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ה
      הדיבל המעופף
    • RE: תגובות | מודל תזמון צינתוקים דרך הטלפון

      @חיה-שיר
      את מנסה להגדיר דרך הקו?

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