דילוג לתוכן
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר
עיצובים
  • בהיר
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • כהה
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • ברירת מחדל (ללא עיצוב (ברירת מחדל))
  • ללא עיצוב (ברירת מחדל)
כיווץ
מתמחים טופ
ר

רוצה לדעת 3

@רוצה לדעת 3
הפסקת מעקב מעקב
אודות
פוסטים
168
נושאים
11
שיתופים
0
קבוצות
0
עוקבים
1
עוקב אחרי
0

פוסטים

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

  • להורדה | תוסף לכרום לזיהוי קל של סרטונים פתוחים בערוץ יוטיוב
    ר רוצה לדעת 3

    יש אפשרות לדעת האם הסרטון נחסם כבר או שהוא לא נבדק?

    נטפרי

  • שיתוף | מה חדש בבינה מלאכותית - נספח עדכוני פיצ'רים ודיונים 💬
    ר רוצה לדעת 3

    @CSS-0 בקצב של כתיבת הפוסטים בשרשור הזה לא הייתי מאשים אותו שהוא לא חשב לגלול 300 פוסטים אחורה😊

    עזרה הדדית - בינה מלאכותית

  • שיתוף | מה חדש בבינה מלאכותית - נספח עדכוני פיצ'רים ודיונים 💬
    ר רוצה לדעת 3

    @יוסף3 בבוקר חשבתי שצדקת
    אבל כרגע הוא עדיין עובד לי
    המודל טען לי שיש לי כמות קרדיטים שקיבלתי בחינם או שזה ישאר עד למכסה השבועית
    יש למישהו מושג?

    עזרה הדדית - בינה מלאכותית

  • שיתוף | מה חדש בבינה מלאכותית - נספח עדכוני פיצ'רים ודיונים 💬
    ר רוצה לדעת 3

    איך יכול להיות שFabel עדיין זמין לי דרך מנוי הפרו שלי?
    אפי' שעדיין רשום שהוא זמין עד ה19 ביולי,
    התשלום דרך קרדיטים לא מופעל לי,
    כדאי לי להשתמש או שהם יחיבו אותי על זה?

    עזרה הדדית - בינה מלאכותית

  • להורדה | תוסף לכרום לניהול והסתרת סרגלים באתרים
    ר רוצה לדעת 3

    תוסף שבניתי באמצעות ידידינו פייבל,
    המאפשר הקטנת או הסתרת הסרגלים בזמן ביקור או גלילה באתר,
    התוסף מיועד לבעלי מסך קטן שמעצבן אותם שהסרגלים העליונים באתרים תופסים חלק ניכר מהמסך,
    התוסף כולל שליטה דרך לחיצה על סמל התוסף ו\או כפתור צף המציג את האפשרויות
    רגיל-קומפקטי-מוסתר-הסתר בגלילה
    אשמח לשמוע על אתרים שבהם הוא לא הצליח לתפוס את הסרגל
    התוסף ניתן או כתוסף נפרד
    header-manager-extension.zip

    או כסקריפט לטמפרמונקי

    // ==UserScript==
    // @name         Header Manager
    // @namespace    yehuda.header-manager
    // @version      4.4.0
    // @description  שליטה בסרגלים עליונים קבועים של אתרים: רגיל / קומפקטי / מוסתר / הסתר-בגלילה
    // @match        *://*/*
    // @grant        GM_getValue
    // @grant        GM_setValue
    // @grant        GM_registerMenuCommand
    // @grant        unsafeWindow
    // @noframes
    // @run-at       document-idle
    // ==/UserScript==
    
    /*
     * ============================ מגבלות ידועות (במכוון, לא לפתור כאן) ============================
     * 1. Compensating gap: אם האתר שם padding/margin מפצה על ה-body בגובה ה-header,
     *    הסתרת הסרגל (גם ב-transform) עלולה להשאיר רווח ריק. לא מטופל בגרסה זו.
     * 2. Fullscreen API דורש Transient User Activation — עובד רק מתוך pointerup/keydown/touchend
     *    ישירים. לעולם לא מגלילה. אין עקיפה, זו מדיניות אבטחה של הדפדפן.
     * 3. שורת הכתובת וסרגל הסימניות הם chrome של הדפדפן — אין שום נקודת חיבור מ-JS.
     * ============================================================================================
     */
    
    (function () {
        'use strict';
    
        // ------------------------------------------------------------------ קונפיגורציה
    
        const UI_HOST_ID = 'header-manager-ui';
    
        // סלקטורים ממוקדים בלבד — לא סורקים כל div/section (נבדק: יקר מאוד, ~2.5s על 5000 אלמנטים)
        const SELECTORS = [
            'header', 'nav', '[role="banner"]', '[role="navigation"]',
            '[class*="header" i]', '[class*="navbar" i]', '[class*="toolbar" i]', '[class*="top-bar" i]',
            '[id*="header" i]', '[id*="navbar" i]',
            // v4.1 — לפי בדיקה בפועל: יוטיוב (#masthead-container), אלמנטור/וורדפרס
            // (elementor-sticky--active מתווסף רק אחרי גלילה), בוטסטרפ (.fixed-top)
            '[id*="masthead" i]', '[class*="masthead" i]',
            '[class*="sticky" i]', '[id*="sticky" i]',
            '[class*="fixed-top" i]',
        ].join(', ');
    
        // "סריקה עמוקה" — כל האלמנטים עד עומק 6 מתחת ל-body, בלי תלות בשמות id/class.
        // opt-in פר-דומיין (כפתור בתפריט) כי זה יקר מהסלקטורים הממוקדים; אחרי שנמצא
        // משהו בדומיין, הדגל נשמר והסריקה העמוקה רצה שם אוטומטית מאז.
        const DEEP_SELECTOR = (() => {
            const parts = [];
            let p = 'body';
            for (let i = 0; i < 6; i++) { p += ' > *'; parts.push(p); }
            return parts.join(', ');
        })();
    
        // בלוקליסט על id/class בלבד (לא על role — <header role="banner"> הוא HTML5 תקני!)
        const BLOCKLIST = /cookie|consent|gdpr|banner|notice|privacy|alert|popup|modal|widget|toast|newsletter|subscribe/i;
    
        const MAX_TOP = 60;            // rect.top מקסימלי כדי להיחשב "עליון"
        const MIN_WIDTH_RATIO = 0.4;   // רוחב מינימלי ביחס לחלון
        const MIN_HEIGHT = 15;
        // תקרת גובה דינמית (v4.1): ה-header של ערוץ יוטיוב הוא 343px, אז תקרה קשיחה
        // של 250px פספסה אותו. מרשים עד 45% מגובה החלון, מינימום 250px.
        function getMaxHeight() { return Math.max(250, Math.round(window.innerHeight * 0.45)); }
    
        const DEBOUNCE_MS = 250;       // debounce ל-Observer ול-resize
        const SCROLL_JITTER = 5;       // סף רעד לגלילה
        const SCROLL_MIN_Y = 80;       // לא מסתירים לפני שגללו לפחות ככה
        const HIDE_BUFFER = 8;         // באפר לצל/box-shadow ביציאה מהמסך
        const LONG_PRESS_MS = 350;     // כניסה למצב גרירה
        const MOVE_CANCEL_PX = 6;      // תזוזה שמבטלת כניסה לגרירה
        const DBL_WINDOW_MS = 300;     // חלון לחיצה-כפולה (מונע פליקר תפריט — באג #3)
        const ESC_WINDOW_MS = 600;     // חלון ל-Esc כפול (איפוס חירום)
        // v4.3: קומפקטי = הקטנה אמיתית ב-scale (לא חיתוך ב-max-height) — הסרגל
        // מוקטן לגובה יעד של ~40px, עם רצפת scale כדי שהתוכן יישאר קריא
        const COMPACT_TARGET = 40;
        const COMPACT_MIN_SCALE = 0.35;
    
        // v4.2: העיצוב מוחל דרך class-ים + stylesheet מוזרק, לא דרך inline style.
        // הסיבה: יוטיוב (ואתרים דומים) כותבים בעצמם inline transform על ה-header
        // בכל גלילה (אנימציית הקריסה המקורית של tp-yt-app-header) — וכתיבה כזו דורסת
        // inline שלנו. כלל stylesheet עם !important מנצח inline בלי !important,
        // אז הכתיבה שלהם מפסיקה להשפיע כל עוד ה-class שלנו על האלמנט.
        // בונוס: אין יותר צורך ב-snapshots — הסרת ה-class משחזרת את המקור נקי.
        const CLS = {
            hidden:  'hdrmgr-hidden',
            compact: 'hdrmgr-compact',
            slide:   'hdrmgr-slide',
            up:      'hdrmgr-up',
        };
        const STYLE_ID = 'hdrmgr-styles';
        const MODE_CSS = `
            .${CLS.hidden}  { display: none !important; }
            .${CLS.compact} { transform: scale(var(--hdrmgr-scale, 0.6)) !important;
                              transform-origin: top center !important;
                              transition: transform 0.2s ease !important; }
            .${CLS.slide}   { transition: transform 0.2s ease !important; }
            .${CLS.up}      { transform: translateY(var(--hdrmgr-dist, -110%)) !important; }
        `;
    
        const MODES = [
            { key: 'normal',  label: 'רגיל' },
            { key: 'compact', label: 'קומפקטי' },
            { key: 'hidden',  label: 'מוסתר' },
            { key: 'auto',    label: 'הסתר בגלילה' },
        ];
    
        // ------------------------------------------------------------------ מצב (state)
    
        let targetHeaders = [];                    // מערך — תמיכה בכמה סרגלים בו-זמנית
        const hideDistances = new Map();           // el -> px  (מרחק הסתרה פר-אלמנט)
        const scaleFactors = new Map();            // el -> scale (מקדם הקטנה פר-אלמנט, כמחרוזת)
        let modeStyleEl = null;                    // ה-stylesheet המוזרק של מצבי התצוגה
        let currentMode = 'normal';
        let autoState = 'visible';                 // 'visible' | 'hidden' (רלוונטי רק במצב auto)
        let lastY = 0;
        let scrollTicking = false;
    
        let uiHost = null, shadow = null, btn = null, menu = null, previewEl = null, statusEl = null;
    
        // ------------------------------------------------------------------ עזרים
    
        // ב-SVG, className הוא SVGAnimatedString ולא string — regex ישיר עליו קורס
        function getSafeClassName(el) {
            const c = el.className;
            if (typeof c === 'string') return c;
            if (c && typeof c.baseVal === 'string') return c.baseVal;
            return '';
        }
    
        function debounce(fn, ms) {
            let t = null;
            return function (...args) {
                clearTimeout(t);
                t = setTimeout(() => fn.apply(this, args), ms);
            };
        }
    
        // השוואת זהות (references), לא כמות — תיקון באג #1:
        // באתרי SPA ה-header מוחלף ב-node חדש בלי שהכמות משתנה.
        function sameHeaderSet(a, b) {
            if (a.length !== b.length) return false;
            return a.every(el => b.includes(el));
        }
    
        // ------------------------------------------------------------------ מנוע זיהוי
    
        function collectNodes(selector) {
            try { return document.querySelectorAll(selector); } catch (e) { return []; }
        }
    
        function isDeepDomain() {
            try { return !!GM_getValue('deepscan:' + location.hostname, false); } catch (e) { return false; }
        }
    
        function findAllHeaders(forceDeep = false) {
            const seen = new Set();
            const nodes = [];
            for (const el of collectNodes(SELECTORS)) {
                if (!seen.has(el)) { seen.add(el); nodes.push(el); }
            }
            if (forceDeep || isDeepDomain()) {
                for (const el of collectNodes(DEEP_SELECTOR)) {
                    if (!seen.has(el)) { seen.add(el); nodes.push(el); }
                }
            }
    
            const candidates = [];
            for (const el of nodes) {
                // self-exclusion: הממשק שלנו (ל-host יש id שמכיל "header" — חייבים לסנן במפורש)
                if (uiHost && (el === uiHost || uiHost.contains(el))) continue;
                if (el.closest && el.closest('#' + UI_HOST_ID)) continue;
    
                // אלמנט שכבר מנוהל על ידינו וחי ב-DOM נשאר בסט בלי בדיקות גיאומטריה:
                // במצב hidden (display:none) המידות שלו אפס, וסריקה חוזרת הייתה "מאבדת"
                // אותו ואז השחזור ל-normal לא היה מוצא אותו. הוא כבר עבר את הקריטריונים כשהיה נקי.
                if (targetHeaders.includes(el) && document.contains(el)) {
                    candidates.push(el);
                    continue;
                }
    
                // בלוקליסט — על id/class בלבד
                const idClass = (el.id || '') + ' ' + getSafeClassName(el);
                if (BLOCKLIST.test(idClass)) continue;
    
                const cs = getComputedStyle(el);
                if (cs.position !== 'fixed' && cs.position !== 'sticky') continue;
    
                const rect = el.getBoundingClientRect();
                if (rect.top > MAX_TOP) continue;
                // v4.4: תקרת גובה מקלה לאלמנטים עם "אות חזק" בשם עצמו — ה-header של
                // ערוץ יוטיוב (tp-yt-app-header, ‎329px) גבוה מהתקרה הרגילה בחלונות
                // נמוכים, אבל כשהשם כבר אומר header/masthead הסיכון לטעות נמוך
                const sig = (el.tagName + ' ' + (el.id || '') + ' ' + getSafeClassName(el)).toLowerCase();
                const cap = /header|masthead|navbar|toolbar|top-?bar/.test(sig)
                    ? Math.max(420, window.innerHeight * 0.75)
                    : getMaxHeight();
                if (rect.height < MIN_HEIGHT || rect.height > cap) continue;
                if (el.offsetWidth < window.innerWidth * MIN_WIDTH_RATIO) continue;
    
                candidates.push(el);
            }
    
            // שמירת החיצוני ביותר בלבד — לא מטפלים פעמיים ב-nav שמקונן בתוך header
            return candidates.filter(el =>
                !candidates.some(other => other !== el && other.contains(el))
            );
        }
    
        // ------------------------------------------------------------------ stylesheet מצבים
    
        function ensureStylesheet() {
            if (!modeStyleEl) {
                modeStyleEl = document.createElement('style');
                modeStyleEl.id = STYLE_ID;
                modeStyleEl.textContent = MODE_CSS;
            }
            // אתרי SPA לפעמים מחליפים את <head> — מחזירים את ה-style אם הועף
            if (!modeStyleEl.isConnected) {
                (document.head || document.documentElement).appendChild(modeStyleEl);
            }
        }
    
        function removeOurClasses(el) {
            el.classList.remove(CLS.hidden, CLS.compact, CLS.slide, CLS.up);
            el.style.removeProperty('--hdrmgr-dist');
            el.style.removeProperty('--hdrmgr-scale');
        }
    
        // ניקוי entries של אלמנטים שכבר לא ב-DOM — המקבילה של תיקון באג #2 המקורי
        // (ב-v4.2 אין יותר snapshots; נשאר רק hideDistances)
        function pruneStaleEntries() {
            for (const el of [...hideDistances.keys()]) {
                if (!document.contains(el)) hideDistances.delete(el);
            }
            for (const el of [...scaleFactors.keys()]) {
                if (!document.contains(el)) scaleFactors.delete(el);
            }
        }
    
        // ------------------------------------------------------------------ מצבי תצוגה
    
        function applyMode(mode, { save = true } = {}) {
            currentMode = mode;
            ensureStylesheet();
    
            // תמיד מנקים קודם — מעבר ישיר בין מצבים לא משאיר שאריות class/var
            for (const el of targetHeaders) removeOurClasses(el);
    
            if (mode === 'compact') {
                // מודדים קודם (אחרי הניקוי, כשהסרגל בגודל מקורי) ורק אז מקטינים
                measureCompactScales();
                for (const el of targetHeaders) el.classList.add(CLS.compact);
            } else if (mode === 'hidden') {
                for (const el of targetHeaders) el.classList.add(CLS.hidden);
            } else if (mode === 'auto') {
                autoState = 'visible';
                lastY = window.scrollY;
                // transform ולא display:none — לא משנה layout flow ונותן תחושת החלקה
                for (const el of targetHeaders) el.classList.add(CLS.slide);
                measureHideDistances();
            }
            // mode === 'normal': הניקוי שכבר בוצע הוא כל מה שצריך
    
            if (save) {
                try { GM_setValue(location.hostname, mode); } catch (e) { /* ignore */ }
            }
            updatePreview();
            updateMenuSelection();
        }
    
        // ---------------------------------------------- מצב "הסתר בגלילה" (auto)
    
        // הפתרון לכמה סרגלים: translateY(-100%) מזיז כל סרגל רק בגובה של עצמו,
        // אז סרגל שני שיושב ב-top:60px היה נשאר גלוי חלקית. במקום זה מודדים לכל
        // סרגל את הקצה התחתון שלו ביחס לחלון (rect.bottom) ומזיזים אותו למעלה
        // בדיוק במרחק הזה + באפר — כך כולם יוצאים לגמרי מהמסך, בכל offset.
        // ה-translateY האפקטיבי הנוכחי של אלמנט (של האתר או שלנו) — כדי לחשב גיאומטריה
        // "נקייה" גם כשהאתר כבר קרס/הזיז את הסרגל (יוטיוב עושה את זה בכל גלילה)
        function currentTranslateY(el) {
            const t = getComputedStyle(el).transform;
            if (!t || t === 'none') return 0;
            let m = t.match(/matrix3d\(([^)]+)\)/);
            if (m) return parseFloat(m[1].split(',')[13]) || 0;
            m = t.match(/matrix\(([^)]+)\)/);
            if (m) return parseFloat(m[1].split(',')[5]) || 0;
            m = t.match(/translateY\((-?[\d.]+)px\)/);
            if (m) return parseFloat(m[1]) || 0;
            return 0;
        }
    
        function measureHideDistances() {
            for (const el of targetHeaders) {
                const rect = el.getBoundingClientRect();
                // מנטרלים transform קיים — אחרת מדידה בזמן שהאתר כבר קרס את הסרגל
                // שלו הייתה נותנת מרחק קטן מדי (הסתרה חלקית)
                const ty = currentTranslateY(el);
                const dist = Math.round(Math.max(rect.bottom - ty, rect.height) + HIDE_BUFFER);
                hideDistances.set(el, dist);
                // המרחק מוזרם ל-CSS דרך custom property — האתר לא נוגע בזה גם כשהוא
                // דורס את transform (הכלל שלנו ב-stylesheet מנצח inline בכל מקרה)
                el.style.setProperty('--hdrmgr-dist', `-${dist}px`);
            }
        }
    
        // מקדם הקטנה פר-אלמנט למצב קומפקטי: הסרגל יורד לגובה יעד (~40px),
        // אבל לא מתחת לרצפת קריאות
        function measureCompactScales() {
            for (const el of targetHeaders) {
                const rect = el.getBoundingClientRect();
                const h = Math.max(rect.height, 1);
                const scale = Math.max(COMPACT_MIN_SCALE, Math.min(1, COMPACT_TARGET / h));
                const v = String(Math.round(scale * 100) / 100);
                scaleFactors.set(el, v);
                el.style.setProperty('--hdrmgr-scale', v);
            }
        }
    
        function setAutoState(state) {
            if (autoState === state) return;
            autoState = state;
            for (const el of targetHeaders) {
                el.classList.toggle(CLS.up, state === 'hidden');
            }
            // בחזרה לגלוי — מדידה מחדש אחרי שהאנימציה מסתיימת: אם ההפעלה המקורית
            // קרתה כשהאתר החזיק את הסרגל בגובה/מיקום חריג, המרחק מתעדכן לערך הנכון
            if (state === 'visible') {
                setTimeout(() => {
                    if (currentMode === 'auto' && autoState === 'visible') measureHideDistances();
                }, 250);
            }
        }
    
        function onScroll() {
            if (currentMode !== 'auto') return;
            if (scrollTicking) return;         // rAF — לא מריצים לוגיקה על כל אירוע scroll גולמי
            scrollTicking = true;
            requestAnimationFrame(() => {
                scrollTicking = false;
                const y = window.scrollY;
                if (y > lastY + SCROLL_JITTER && y > SCROLL_MIN_Y) setAutoState('hidden');
                else if (y < lastY - SCROLL_JITTER) setAutoState('visible');
                lastY = y;
            });
        }
    
        const onResize = debounce(() => {
            if (currentMode === 'auto') {
                // מודדים מחדש רק כשהסרגלים גלויים
                setAutoState('visible');
                requestAnimationFrame(measureHideDistances);
            }
            clampHostPosition();
        }, DEBOUNCE_MS);
    
        // ------------------------------------------------------------------ Observer (SPA)
    
        function rescanNow() {
            const next = findAllHeaders();
            if (sameHeaderSet(targetHeaders, next)) return;
            // אלמנטים שיוצאים מהסט אבל עדיין ב-DOM — מנקים מהם את ה-class-ים שלנו
            for (const el of targetHeaders) {
                if (!next.includes(el) && document.contains(el)) removeOurClasses(el);
            }
            targetHeaders = next;
            pruneStaleEntries();               // באג #2
            applyMode(currentMode, { save: false });   // באג #1 — החלה מחדש על הסט החדש
        }
    
        const debouncedRescan = debounce(rescanNow, DEBOUNCE_MS);
    
        // אכיפה: מוודא שה-class-ים וה-custom property שלנו עדיין על אלמנט מנוהל.
        // אתרים כמו יוטיוב/React דורסים לפעמים את className או את style בשלמותם —
        // כאן מחזירים את מה שנמחק. בטוח מלולאות: כשהכול במקום, לא משתנה כלום.
        function enforceElement(el) {
            ensureStylesheet();
            const want = [];
            if (currentMode === 'compact') want.push(CLS.compact);
            else if (currentMode === 'hidden') want.push(CLS.hidden);
            else if (currentMode === 'auto') {
                want.push(CLS.slide);
                if (autoState === 'hidden') want.push(CLS.up);
            }
            for (const c of want) {
                if (!el.classList.contains(c)) el.classList.add(c);
            }
            if (currentMode === 'auto' && hideDistances.has(el)) {
                const v = `-${hideDistances.get(el)}px`;
                if (el.style.getPropertyValue('--hdrmgr-dist') !== v) {
                    el.style.setProperty('--hdrmgr-dist', v);
                }
            }
            if (currentMode === 'compact' && scaleFactors.has(el)) {
                const v = scaleFactors.get(el);
                if (el.style.getPropertyValue('--hdrmgr-scale') !== v) {
                    el.style.setProperty('--hdrmgr-scale', v);
                }
            }
        }
    
        function startObserver() {
            const observer = new MutationObserver((records) => {
                let needRescan = false;
                for (const r of records) {
                    const t = r.target;
                    // הממשק שלנו — לא רלוונטי
                    if (uiHost && (t === uiHost || uiHost.contains(t))) continue;
                    // שינוי attribute על סרגל מנוהל: לא rescan מלא אלא אכיפה מיידית —
                    // אם האתר דרס את ה-class/style שלנו, מחזירים אותם
                    if (r.type === 'attributes' && targetHeaders.includes(t)) {
                        enforceElement(t);
                        continue;
                    }
                    needRescan = true;
                }
                if (needRescan) debouncedRescan();
            });
            // v4.1: מאזינים גם לשינויי class/style — תופס סרגלים שנהיים fixed רק
            // אחרי גלילה דרך הוספת class (למשל elementor-sticky--active ב-letsai)
            observer.observe(document.body, {
                childList: true, subtree: true,
                attributes: true, attributeFilter: ['class', 'style'],
            });
        }
    
        // ------------------------------------------------------------------ סריקה עמוקה
    
        function runDeepScan() {
            const found = findAllHeaders(true);
            const isNew = !sameHeaderSet(targetHeaders, found);
            if (isNew && found.length) {
                // מהיום הדומיין הזה נסרק עמוק אוטומטית בכל טעינה ובכל rescan
                try { GM_setValue('deepscan:' + location.hostname, true); } catch (e) { /* ignore */ }
                targetHeaders = found;
                pruneStaleEntries();
                applyMode(currentMode, { save: false });
            }
            if (statusEl) {
                statusEl.textContent = isNew && found.length
                    ? `נמצאו ${found.length} סרגלים — סריקה עמוקה תרוץ כאן אוטומטית מעכשיו`
                    : 'לא נמצאו סרגלים נוספים';
            }
            updatePreview();
        }
    
        // ------------------------------------------------------------------ ממשק משתמש
    
        const UI_CSS = `
            * { box-sizing: border-box; margin: 0; padding: 0;
                font-family: system-ui, "Segoe UI", Arial, sans-serif; }
            #btn {
                width: 44px; height: 44px; border-radius: 50%;
                background: #1f2937; color: #fff;
                border: 2px solid rgba(255,255,255,.3);
                box-shadow: 0 2px 10px rgba(0,0,0,.4);
                font-size: 18px; line-height: 1;
                display: flex; align-items: center; justify-content: center;
                cursor: grab; user-select: none; -webkit-user-select: none;
                /* v4.1: none ולא manipulation — manipulation משאיר לדפדפן את מחוות
                   הגלילה, והוא חוטף את הגרירה באמצע (pointercancel במסך מגע).
                   none גם מנטרל את השהיית ה-300ms וה-double-tap zoom. */
                touch-action: none;
            }
            #btn.dragging { cursor: grabbing; transform: scale(1.15); }
            #menu {
                position: absolute; direction: rtl; text-align: right;
                min-width: 240px; max-width: 300px;
                background: #111827; color: #e5e7eb;
                border: 1px solid #374151; border-radius: 10px;
                padding: 8px; box-shadow: 0 6px 24px rgba(0,0,0,.5);
                display: none; font-size: 13px;
            }
            #menu.open { display: block; }
            #menu .title { font-size: 12px; color: #9ca3af; padding: 2px 6px 6px; }
            .mode { padding: 8px 10px; border-radius: 6px; cursor: pointer; }
            .mode:hover { background: #1f2937; }
            .mode.active { background: #2563eb; color: #fff; }
            #preview {
                border-top: 1px solid #374151; margin-top: 6px; padding: 6px 6px 2px;
                font-size: 11px; color: #9ca3af; max-height: 140px; overflow: auto;
            }
            #preview .hdr { padding: 2px 0; direction: ltr; text-align: right;
                            unicode-bidi: plaintext; }
            #preview .none { color: #6b7280; font-style: italic; }
            .hint { font-size: 10px; color: #6b7280; padding: 6px 6px 0;
                    border-top: 1px solid #374151; margin-top: 6px; }
        `;
    
        function buildUI() {
            uiHost = document.createElement('div');
            uiHost.id = UI_HOST_ID;
            uiHost.style.setProperty('position', 'fixed', 'important');
            uiHost.style.setProperty('z-index', '2147483647', 'important'); // מקסימלי, דטרמיניסטי
            uiHost.style.setProperty('width', '44px', 'important');
            uiHost.style.setProperty('height', '44px', 'important');
    
            // מיקום גלובלי שמור (לא פר-דומיין)
            const left = GM_getValue('ui_global_left', null);
            const top = GM_getValue('ui_global_top', null);
            uiHost.style.setProperty('left',
                (left !== null ? left : window.innerWidth - 64) + 'px', 'important');
            uiHost.style.setProperty('top',
                (top !== null ? top : Math.round(window.innerHeight * 0.6)) + 'px', 'important');
    
            // Shadow DOM — CSS של האתר לא יכול לשבור את הממשק
            shadow = uiHost.attachShadow({ mode: 'open' });
            const style = document.createElement('style');
            style.textContent = UI_CSS;
            shadow.appendChild(style);
    
            btn = document.createElement('div');
            btn.id = 'btn';
            btn.textContent = '▤';
            btn.title = 'Header Manager — לחיצה: תפריט · לחיצה ארוכה: גרירה · דאבל-קליק: איפוס';
            shadow.appendChild(btn);
    
            menu = document.createElement('div');
            menu.id = 'menu';
            const title = document.createElement('div');
            title.className = 'title';
            title.textContent = 'Header Manager — ' + location.hostname;
            menu.appendChild(title);
    
            for (const m of MODES) {
                const item = document.createElement('div');
                item.className = 'mode';
                item.dataset.mode = m.key;
                item.textContent = m.label;
                item.addEventListener('click', () => {
                    applyMode(m.key);
                    closeMenu();
                });
                menu.appendChild(item);
            }
    
            const deepItem = document.createElement('div');
            deepItem.className = 'mode';
            deepItem.textContent = '🔍 סריקה עמוקה';
            deepItem.title = 'סריקת כל האלמנטים בעמוד — לאתרים שהזיהוי הרגיל מפספס';
            deepItem.addEventListener('click', runDeepScan);
            menu.appendChild(deepItem);
    
            statusEl = document.createElement('div');
            statusEl.className = 'title';
            menu.appendChild(statusEl);
    
            previewEl = document.createElement('div');
            previewEl.id = 'preview';
            menu.appendChild(previewEl);
    
            const hint = document.createElement('div');
            hint.className = 'hint';
            hint.textContent = 'Esc פעמיים: איפוס · דאבל-קליק עכבר: איפוס · דאבל-טאפ: מסך מלא';
            menu.appendChild(hint);
    
            shadow.appendChild(menu);
    
            // מונע תפריט הקשר בלחיצה ארוכה במגע
            btn.addEventListener('contextmenu', e => e.preventDefault());
    
            setupPointerHandling();
    
            // נצמד ל-documentElement ולא ל-body — עמיד יותר מול SPA שמחליפים את תוכן ה-body
            document.documentElement.appendChild(uiHost);
        }
    
        function updateMenuSelection() {
            if (!menu) return;
            for (const item of menu.querySelectorAll('.mode')) {
                item.classList.toggle('active', item.dataset.mode === currentMode);
            }
        }
    
        function updatePreview() {
            if (!previewEl) return;
            previewEl.textContent = '';
            if (targetHeaders.length === 0) {
                const d = document.createElement('div');
                d.className = 'none';
                d.textContent = 'לא זוהו סרגלים בעמוד זה';
                previewEl.appendChild(d);
                return;
            }
            targetHeaders.forEach((el, i) => {
                const d = document.createElement('div');
                d.className = 'hdr';
                const h = Math.round(el.getBoundingClientRect().height) || Math.round(el.offsetHeight);
                const idPart = el.id ? '#' + el.id : '';
                const clsPart = !idPart && getSafeClassName(el)
                    ? '.' + getSafeClassName(el).trim().split(/\s+/)[0] : '';
                d.textContent = `Header #${i + 1} <${el.tagName.toLowerCase()}${idPart}${clsPart}> ${h}px`;
                previewEl.appendChild(d);
            });
        }
    
        function openMenu() {
            updatePreview();
            updateMenuSelection();
            if (statusEl) statusEl.textContent = isDeepDomain() ? 'סריקה עמוקה פעילה בדומיין הזה' : '';
            // מיקום דינמי לפי מיקום הכפתור — לא יוצא מגבולות המסך
            const rect = uiHost.getBoundingClientRect();
            menu.style.top = ''; menu.style.bottom = '';
            menu.style.left = ''; menu.style.right = '';
            if (rect.top < window.innerHeight / 2) menu.style.top = '52px';
            else menu.style.bottom = '52px';
            if (rect.left < window.innerWidth / 2) menu.style.left = '0';
            else menu.style.right = '0';
            menu.classList.add('open');
        }
    
        function closeMenu() { menu.classList.remove('open'); }
        function isMenuOpen() { return menu.classList.contains('open'); }
    
        function clampHostPosition() {
            if (!uiHost) return;
            const rect = uiHost.getBoundingClientRect();
            const left = Math.min(Math.max(4, rect.left), window.innerWidth - 48);
            const top = Math.min(Math.max(4, rect.top), window.innerHeight - 48);
            uiHost.style.setProperty('left', left + 'px', 'important');
            uiHost.style.setProperty('top', top + 'px', 'important');
        }
    
        // ------------------------------------------- גרירה + קליק/דאבל-קליק (Pointer Events)
    
        function setupPointerHandling() {
            let drag = null;            // הקשר ה-pointer הנוכחי
            let longPressTimer = null;
            let clickTimer = null;      // חלון ההמתנה לקליק שני (תיקון באג #3 — פליקר)
    
            // v4.1: שמירת מיקום רציפה תוך כדי גרירה (debounce) — גם אם הגרירה
            // נקטעת באמצע (pointercancel), המיקום האחרון לא הולך לאיבוד
            const savePosition = debounce(() => {
                const rect = uiHost.getBoundingClientRect();
                GM_setValue('ui_global_left', Math.round(rect.left));
                GM_setValue('ui_global_top', Math.round(rect.top));
            }, 250);
    
            btn.addEventListener('pointerdown', (e) => {
                if (e.pointerType === 'mouse' && e.button !== 0) return;
                e.preventDefault();
                drag = {
                    pointerId: e.pointerId,
                    pointerType: e.pointerType,
                    startX: e.clientX, startY: e.clientY,
                    hostLeft: uiHost.getBoundingClientRect().left,
                    hostTop: uiHost.getBoundingClientRect().top,
                    canDrag: false,     // נכנסים לגרירה רק אחרי long-press
                    moved: false,       // תזוזה לפני ה-long-press => לא קליק ולא גרירה
                    dragged: false,     // הזזה בפועל בזמן גרירה
                };
                try { btn.setPointerCapture(e.pointerId); } catch (err) { /* ignore */ }
                longPressTimer = setTimeout(() => {
                    if (drag && !drag.moved) {
                        drag.canDrag = true;
                        btn.classList.add('dragging');
                    }
                }, LONG_PRESS_MS);
            });
    
            btn.addEventListener('pointermove', (e) => {
                if (!drag || e.pointerId !== drag.pointerId) return;
                const dx = e.clientX - drag.startX;
                const dy = e.clientY - drag.startY;
                const dist = Math.hypot(dx, dy);
    
                if (!drag.canDrag) {
                    // תזוזה מעל הסף לפני שחלף ה-long-press — מבטלים כניסה לגרירה
                    if (dist > MOVE_CANCEL_PX) {
                        drag.moved = true;
                        clearTimeout(longPressTimer);
                    }
                    return;
                }
                // גרירה פעילה
                if (dist > 1) drag.dragged = true;
                const left = Math.min(Math.max(4, drag.hostLeft + dx), window.innerWidth - 48);
                const top = Math.min(Math.max(4, drag.hostTop + dy), window.innerHeight - 48);
                uiHost.style.setProperty('left', left + 'px', 'important');
                uiHost.style.setProperty('top', top + 'px', 'important');
                savePosition();
            });
    
            const endPointer = (e) => {
                if (!drag || e.pointerId !== drag.pointerId) return;
                clearTimeout(longPressTimer);
                btn.classList.remove('dragging');
                const d = drag;
                drag = null;
    
                if (d.canDrag) {
                    if (d.dragged) {
                        // גרירה בפועל → שמירת מיקום גלובלית
                        const rect = uiHost.getBoundingClientRect();
                        GM_setValue('ui_global_left', Math.round(rect.left));
                        GM_setValue('ui_global_top', Math.round(rect.top));
                        return;
                    }
                    // long-press בלי הזזה → מתנהג כקליק רגיל (תפריט)
                } else if (d.moved) {
                    // תזוזה בלי long-press (swipe מהיר) → לא קליק, מתעלמים
                    return;
                }
    
                // --- קליק / דאבל-קליק, מאוחד לעכבר ולמגע ---
                // תיקון באג #3: התפריט לא נפתח מיידית אלא אחרי חלון של 300ms.
                // אם מגיע קליק שני בתוך החלון — זו לחיצה כפולה, והתפריט לא מהבהב.
                if (clickTimer !== null) {
                    clearTimeout(clickTimer);
                    clickTimer = null;
                    if (d.pointerType === 'touch') {
                        // double-tap במגע → מסך מלא (pointerup הוא user gesture תקף)
                        // התנהגות שונה במכוון מדאבל-קליק בעכבר.
                        toggleFullscreen();
                    } else {
                        // דאבל-קליק בעכבר → איפוס חירום
                        emergencyReset();
                    }
                } else {
                    clickTimer = setTimeout(() => {
                        clickTimer = null;
                        if (isMenuOpen()) closeMenu();
                        else openMenu();
                    }, DBL_WINDOW_MS);
                }
            };
    
            btn.addEventListener('pointerup', endPointer);
            btn.addEventListener('pointercancel', (e) => {
                if (drag && e.pointerId === drag.pointerId) {
                    clearTimeout(longPressTimer);
                    btn.classList.remove('dragging');
                    // אם הגרירה נקטעה אחרי הזזה בפועל — שומרים את המיקום שהושג
                    if (drag.canDrag && drag.dragged) savePosition();
                    drag = null;
                }
            });
        }
    
        // ------------------------------------------------------------------ ערוצי חירום
    
        function emergencyReset() {
            closeMenu();
            // איפוס + שמירה: normal נשמר כהעדפת הדומיין (התנהגות "כפתור חירום")
            applyMode('normal', { save: true });
        }
    
        function toggleFullscreen() {
            // עובד רק מתוך user gesture ישיר — ראו מגבלה #2 בראש הקובץ
            if (document.fullscreenElement) {
                document.exitFullscreen().catch(() => { /* ignore */ });
            } else {
                document.documentElement.requestFullscreen().catch(() => { /* ignore */ });
            }
        }
    
        // v4.3: איפוס רק ב-Esc כפול. Esc בודד נלחץ כל הזמן באתרים (יציאה ממסך
        // מלא ביוטיוב, סגירת חיפוש) והיה דורס את ההעדפה השמורה של המשתמש.
        let lastEscTime = 0;
        function onKeydown(e) {
            if (e.key !== 'Escape') return;
            if (isMenuOpen()) { closeMenu(); return; }
            const now = Date.now();
            if (now - lastEscTime < ESC_WINDOW_MS) {
                lastEscTime = 0;
                emergencyReset();
            } else {
                lastEscTime = now;
            }
        }
    
        // ------------------------------------------------------------------ אתחול
    
        function init() {
            if (!document.body) return;   // ביטחון — document-idle אמור להבטיח body
    
            currentMode = GM_getValue(location.hostname, 'normal');
            buildUI();
            clampHostPosition();
    
            targetHeaders = findAllHeaders();
            applyMode(currentMode, { save: false });
    
            startObserver();
            window.addEventListener('scroll', onScroll, { passive: true });
            window.addEventListener('resize', onResize);
            document.addEventListener('keydown', onKeydown, true);
    
            // ערוץ חירום רביעי — עובד גם אם הכפתור הצף נשבר/נעלם
            if (typeof GM_registerMenuCommand === 'function') {
                GM_registerMenuCommand('איפוס Header Manager באתר זה', emergencyReset);
            }
        }
    
        init();
    
        // API לדיבוג/בדיקות (לא חלק מהממשק למשתמש). נחשף על unsafeWindow כדי שאפשר
        // יהיה לגשת אליו מהקונסול הרגיל של הדפדפן (מחוץ ל-sandbox של Tampermonkey).
        const PAGE = (typeof unsafeWindow === 'object' && unsafeWindow) ? unsafeWindow : window;
        try {
            Object.defineProperty(PAGE, '__headerManagerDebug', {
                value: {
                    findAllHeaders,
                    rescanNow,
                    applyMode,
                    setAutoState,
                    runDeepScan,
                    getState: () => ({
                        currentMode, autoState,
                        targets: targetHeaders.slice(),
                        hideDistances: new Map(hideDistances),
                        scaleFactors: new Map(scaleFactors),
                    }),
                },
                configurable: true,
            });
        } catch (e) { /* ignore */ }
    })();
    
    עזרה הדדית - מחשבים וטכנולוגיה

  • שיתוף | הנדסה לאחור של אפליקצייה זדונית
    ר רוצה לדעת 3

    @מישהו12 מדהים!
    ועל הדרך גם שיווקת את מתמחים טופ לעוד הרבה אנשים שיכולים לעזור פה בפורום😊

    עזרה הדדית - אנדרואיד

  • מדריך | תגובות למדריך מזגנים חלק ב'- התקנה
    ר רוצה לדעת 3

    @תלת-פאזי @ARI
    החלק של להשאיר לפחות 10 ס"מ לתקרה הוא קריטי?
    מדובר במזגן קטן 1/1.5 כוח סוס שאני יכול להשאיר לו מקסימום 5 ס"מ!
    תודה רבה על הכל

    מכשירי חשמל ביתיים

  • שיתוף | כך תצרו פודקאסט מבוסס AI שפשוט קשה להאמין שהוא לא אנושי
    ר רוצה לדעת 3

    @NH.LOCAL יש אפשרות שהקולות של שני הגברים יהיו יותר שונים? כי חוץ מסגנון הדיבור קשה להבדיל ביניהם

    עזרה הדדית - בינה מלאכותית

  • המלצה | תוסף לשיפור ChatGPT: עיצוב משופר לצ'אט, סרגל ניווט צידי, תמיכה מסויימת בRTL, וכפתור להעתקת השיחה כולה!
    ר רוצה לדעת 3

    @י.-פל. כתב בהמלצה | תוסף לשיפור ChatGPT: עיצוב משופר לצ'אט, סרגל ניווט צידי, תמיכה מסויימת בRTL, וכפתור להעתקת השיחה כולה!:

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

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

    כללי - עזרה הדדית

  • בירור | האם יש קו טלפון שמעדכן על מדדי מניות?
    ר רוצה לדעת 3

    כמובן שפתוח בקומה הכשרה
    ואם אין כמה מסובך לפתח דבר כזה?

    מקווה שהשאלה השניה הפכה את הנושא לקשור לפורום 😊

    כללי - עזרה הדדית

  • בירור | מחיקת חשבון בעלי אקספרס ויצירה מחדש
    ר רוצה לדעת 3

    @2580 פעם קראתי שאם פותחים חשבון נוסף ומוסיפים בכתובת המייל נקודה בין האותיות שלפני השטרודל הוא מזהה את זה כחשבון חדש למרות שזה אותו מייל
    אני ניסתי את זה בgpt ועבד לי אין לי מושג האם עובד גם באלי אקספרס

    כללי - עזרה הדדית

  • להורדה | CTF שיצרתי, תהנו :)
    ר רוצה לדעת 3

    @10110000 חייבים להבין מה שעושים או שאפשר להיעזר בgpt?😊
    תודה רבה ממש נהנתי

    לא מצאתי flag אז זה במקום
    Wow, great work!

    Now that you’ve figured this out, you can start to understand how tools like Binwalk work.
    .....

    רעיונות לביצוע

  • המלצה | שירות AI ב־SMS
    ר רוצה לדעת 3

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

    עזרה הדדית - בינה מלאכותית

  • שאלה | שינוי imei נוקיה 8210?
    ר רוצה לדעת 3

    יש אצלי נוקיה 8210 4g שיצא בשנת 2020 ויש לו סים אחד בלבד
    מישהו יודע האם ניתן לשנות לו את הimei באמצעות התוכנה החינמית למרות שברשימה שם לא מופיע הדגם הזה?
    והאם יש לו הגנה נגד שינוי במקרה שזה יצליח להשתנות?
    @שלוםפ
    @10110000

    עזרה הדדית - נוקיה

  • שיתוף | בינה מלאכותית בטלפון צאט gpt
    ר רוצה לדעת 3

    @yehokarpel כתב בשיתוף | בינה מלאכותית בטלפון צאט gpt:

    @קראנץ-x27-ונילה 🤖 נחיה ונראה (ברור שזה אפשרי אבל אף אחד כרגע מהחברות הגדולות לא חשב לעשות את זה בפומבי)

    ל openAI יש אבל זה עובד רק באמריקה

    שונות וטיפים - סלולרי

  • בירור | איפוס סיסמא לנוקיה 215
    ר רוצה לדעת 3

    @גלעדבןדוד התוכנה בכלל לא עובדת?

    עזרה הדדית - נוקיה

  • בירור | איפוס סיסמא לנוקיה 215
    ר רוצה לדעת 3

    @גלעדבןדוד https://mitmachim.top/topic/69463/מדריך-פריצת-סיסמא-לnokia-215-225-ללא-איבוד-חומר

    עזרה הדדית - נוקיה

  • ישומים נפוצים לנוקיה I מתעדכן
    ר רוצה לדעת 3

    @אברימי-התותח לא מתאים ואי אפשר לשים שום דבר חוץ מזמני היום שמגיע עם המכשיר

    יישומים לנוקיה - jar ספרים לנוקיה ישומים לנוקיה
  • התחברות

  • אין לך חשבון עדיין? הרשמה

  • התחברו או הירשמו כדי לחפש.
  • פוסט ראשון
    פוסט אחרון
0
  • חוקי הפורום
  • פופולרי
  • לא נפתר
  • משתמשים
  • חיפוש גוגל בפורום
  • צור קשר