המלצה | חדש בפורום! נושאים שנצפו לאחרונה!
-
בס"ד ולאחר מאמצים משותפים שלי ושל AI... עלה בידינו לכתוב סקריפט לטמפרמונקי להטמעת כפתור 'נצפו לאחרונה' בפורום, כך שניתן יהיה לראות את כל הנושאים שביקרתם בהם ברחבי הפורום.
סקירה כללית:- מראה את כל הנושאים שביקרתם בהם למין הזמן שהתקנתם את הסקריפט.
- כל נושא מופיע פעם אחת בלבד אף אם ביקרתם בו מספר פעמים.
- תיבת חיפוש בין כל הנושאים שנצפו לאחרונה.
// ==UserScript== // @name Mitmachim Top - נצפו לאחרונה (v2.0.4 - Syntax Check) // @namespace http://tampermonkey.net/ // @version 2.0.4 // @description בדיקה מחודשת של תחביר ותפיסת שגיאות מוקדמות. // @author לאצי&AI // @match https://mitmachim.top/* // @icon https://mitmachim.top/assets/uploads/files/1744298283200-font_awesome_5_solid_history.svg.png // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @run-at document-idle // ==/UserScript== (function() { 'use strict'; function ensureDependencies(callback) { let checks = 0; const MAX_CHECKS = 120; const CHECK_INTERVAL = 100; console.log("[LastViewed] Starting dependency check..."); const interval = setInterval(function() { const jQueryReady = typeof $ === 'function'; // More robust check for jQuery const $navList = jQueryReady ? $('nav[component="sidebar/left"] ul#main-nav') : null; const navListReady = $navList && $navList.length > 0; if (navListReady) { clearInterval(interval); console.log(`[LastViewed] Dependencies ($) AND Nav List found after ${checks + 1} checks.`); try { callback(); // Run the main script logic } catch(e) { console.error("[LastViewed] Error executing initialize callback:", e); } } else { checks++; if (checks >= MAX_CHECKS) { clearInterval(interval); console.error(`[LastViewed] Dependency check FAILED after ${MAX_CHECKS} checks (Timeout).`); // Provide detailed status on failure console.error(`[LastViewed] Final Status: jQuery=${typeof $ === 'function'}, NavList Element Found=${jQueryReady ? $('nav[component="sidebar/left"] ul#main-nav').length : 'N/A (jQuery missing)'}`); } } }, CHECK_INTERVAL); } const STORAGE_KEY = 'mitmachim_last_viewed_topics'; const MAX_HISTORY_ITEMS = 20; const TOPIC_URL_PREFIX = 'https://mitmachim.top/topic/'; const DEBOUNCE_DELAY = 300; const styles = ` /* Popup Styles */ #last-viewed-popup { display: flex; flex-direction: column; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90%; max-width: 550px; max-height: 80vh; overflow-y: auto; background-color: var(--bs-body-bg, #ffffff); color: var(--bs-body-color, #212529); border: 1px solid var(--bs-border-color, #dee2e6); box-shadow: 0 5px 15px rgba(0,0,0,0.2); padding: 20px; z-index: 1060; direction: rtl; font-family: "Assistant", sans-serif; font-size: 14px; border-radius: var(--bs-border-radius, 0.375rem); } #last-viewed-popup h2 { flex-shrink: 0; margin-top: 0; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid var(--bs-border-color-translucent, rgba(0,0,0,0.175)); color: var(--bs-heading-color, inherit); text-align: right; font-size: 1.25rem; font-weight: 600; } #last-viewed-popup ul#last-viewed-list { flex-grow: 1; list-style: none; padding: 0; margin: 0; overflow-y: auto; } #last-viewed-popup li { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px dashed var(--bs-border-color-translucent, rgba(0,0,0,0.1)); text-align: right; line-height: 1.5; } #last-viewed-popup li:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } #last-viewed-popup a { text-decoration: none; color: var(--bs-link-color, #0d6efd); font-weight: 600; } #last-viewed-popup a:hover { text-decoration: underline; color: var(--bs-link-hover-color, #0a58ca); } #last-viewed-popup .popup-controls { flex-shrink: 0; display: flex; justify-content: space-between; align-items: center; margin-top: 15px; padding-top: 10px; border-top: 1px solid var(--bs-border-color-translucent, rgba(0,0,0,0.175)); } #last-viewed-popup .close-popup-btn, #last-viewed-popup #clear-last-viewed { padding: 0.375rem 0.75rem; font-size: 0.9rem; border-radius: var(--bs-border-radius-sm, 0.25rem); cursor: pointer; text-align: center; vertical-align: middle; user-select: none; border: 1px solid transparent; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; } #last-viewed-popup .close-popup-btn { color: var(--bs-secondary-color, #6c757d); background-color: var(--bs-secondary-bg, #e9ecef); border-color: var(--bs-secondary-border, #dee2e6); } #last-viewed-popup .close-popup-btn:hover { background-color: var(--bs-secondary-active-bg, #ced4da); } #last-viewed-popup #clear-last-viewed { color: var(--bs-danger-color, #dc3545); background-color: var(--bs-danger-bg, #f8d7da); border-color: var(--bs-danger-border, #f5c2c7); } #last-viewed-popup #clear-last-viewed:hover { background-color: var(--bs-danger-active-bg, #f1aeb5); } #last-viewed-popup::-webkit-scrollbar { width: 7px; height: 7px; background-color: #e6f9ff; border-radius: 4px; } #last-viewed-popup::-webkit-scrollbar-thumb { background: #99ceff; border-radius: 100px; } #last-viewed-popup::-webkit-scrollbar:hover { width: 12px; } /* Search box styles */ #last-viewed-search-container { flex-shrink: 0; margin-bottom: 15px; } #last-viewed-search { width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; font-weight: 400; line-height: 1.5; color: var(--bs-body-color); background-color: var(--bs-body-bg); background-clip: padding-box; border: 1px solid var(--bs-border-color); appearance: none; border-radius: var(--bs-border-radius); transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; } #last-viewed-search:focus { border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13,110,253,.25); } #last-viewed-search::placeholder { color: #6c757d; opacity: 1; } #last-viewed-no-results { text-align: center; color: #6c757d; margin-top: 1rem; display: none; } /* Dynamic Tooltip Styles */ #last-viewed-dynamic-tooltip{position:fixed;background-color:#212529;color:#fff;padding:2px 10px;border-radius:6px;font-size:16px;font-weight:150;font-family:"Assistant",sans-serif;white-space:nowrap;z-index:1100;pointer-events:none;--arrow-size:6px;} #last-viewed-dynamic-tooltip::before{content:"";position:absolute;top:50%;left:100%;margin-top:calc(-1*var(--arrow-size));border-width:var(--arrow-size);border-style:solid;border-color:transparent transparent transparent #212529;} `; GM_addStyle(styles); function getLastViewed() { const data = GM_getValue(STORAGE_KEY, '[]'); try { return JSON.parse(data); } catch (e) { console.error("[LastViewed] Error parsing stored data:", e); return []; } } function setLastViewed(items) { const limitedItems = items.slice(-MAX_HISTORY_ITEMS); GM_setValue(STORAGE_KEY, JSON.stringify(limitedItems)); } function addTopicToHistory(topicTitle, topicUrl) { if (!topicTitle || !topicUrl) return; topicTitle = topicTitle.trim(); const normalizedUrl = topicUrl.split('?')[0].split('#')[0]; let items = getLastViewed(); const existingIndex = items.findIndex(item => item && item.url === normalizedUrl && item.title === topicTitle); if (existingIndex !== -1) { items[existingIndex].timestamp = Date.now(); } else { items = items.filter(item => item && item.url !== normalizedUrl || item.title !== topicTitle); items.push({ title: topicTitle, url: normalizedUrl, timestamp: Date.now() }); } setLastViewed(items); } function getTopicDetailsFromPage() { const currentUrl = window.location.href; if (!currentUrl.startsWith(TOPIC_URL_PREFIX)) { return null; } let title = ''; let $titleSpan = $('h1[component="post/header"] span[component="topic/title"]'); if ($titleSpan.length > 0) { let $tempSpan = $titleSpan.clone(); $tempSpan.find('span[style*="font-size: 10px"]').remove(); title = $tempSpan.text().replace(/\s+/g, ' ').trim(); } if (!title) { title = document.title.replace(/ \| מתמחים טופ.*/, '').trim(); } const normalizedUrl = currentUrl.split('?')[0].split('#')[0]; return { title: title, url: normalizedUrl }; } let debounceTimer; function debounce(func, delay) { return function(...args) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { if (typeof func === 'function') { func.apply(this, args); } else { console.error("[LastViewed] Debounced function is not valid at time of execution."); } }, delay); }; } const saveCurrentTopicDebounced = debounce(function() { const currentTopic = getTopicDetailsFromPage(); if (currentTopic && currentTopic.title) { addTopicToHistory(currentTopic.title, currentTopic.url); } }, DEBOUNCE_DELAY); function closePopupAndRemoveListener() { const $popup = $('#last-viewed-popup'); if ($popup.length) { $(document).off('click.closeLastViewed'); $popup.remove(); } } function addClosePopupListener() { $(document).off('click.closeLastViewed'); $(document).on('click.closeLastViewed', function(event) { if (!$('#last-viewed-popup').length) { $(document).off('click.closeLastViewed'); return; } if (!$(event.target).closest('#last-viewed-popup').length && !$(event.target).closest('#last-viewed-btn-li').length) { closePopupAndRemoveListener(); } }); } function filterLastViewedList() { const searchTerm = $('#last-viewed-search').val().toLowerCase().trim(); const $listItems = $('#last-viewed-list li'); let visibleCount = 0; $listItems.each(function() { const $item = $(this); const itemText = $item.find('a').text().toLowerCase(); if (itemText.includes(searchTerm)) { $item.show(); visibleCount++; } else { $item.hide(); } }); if (visibleCount === 0 && $listItems.length > 0) { $('#last-viewed-no-results').show(); } else { $('#last-viewed-no-results').hide(); } } function showLastViewedPopup() { if ($('#last-viewed-popup').length) return; closePopupAndRemoveListener(); console.log("[LastViewed] Opening popup."); let items = getLastViewed(); const uniqueItems = []; const seenTitles = new Set(); for (let i = items.length - 1; i >= 0; i--) { const item = items[i]; if (!seenTitles.has(item.title)) { uniqueItems.push(item); seenTitles.add(item.title); } } items = uniqueItems; const popup = document.createElement('div'); popup.id = 'last-viewed-popup'; const searchContainer = document.createElement('div'); searchContainer.id = 'last-viewed-search-container'; const searchInput = document.createElement('input'); searchInput.type = 'search'; searchInput.id = 'last-viewed-search'; searchInput.placeholder = 'חיפוש בנצפו לאחרונה...'; searchInput.className = 'form-control'; searchContainer.appendChild(searchInput); popup.appendChild(searchContainer); const title = document.createElement('h2'); title.textContent = 'שרשורים שנצפו לאחרונה'; popup.appendChild(title); if (items.length === 0) { const emptyMsg = document.createElement('p'); emptyMsg.textContent = 'לא נצפו שרשורים לאחרונה.'; popup.appendChild(emptyMsg); } else { const list = document.createElement('ul'); list.id = 'last-viewed-list'; for (let i = 0; i < items.length; i++) { const item = items[i]; const listItem = document.createElement('li'); const link = document.createElement('a'); link.href = item.url; link.textContent = item.title || 'ללא כותרת'; link.target = '_blank'; listItem.appendChild(link); list.appendChild(listItem); } popup.appendChild(list); const noResultsMsg = document.createElement('p'); noResultsMsg.id = 'last-viewed-no-results'; noResultsMsg.textContent = 'לא נמצאו שרשורים תואמים.'; popup.appendChild(noResultsMsg); } const controlsDiv = document.createElement('div'); controlsDiv.className = 'popup-controls'; const clearButton = document.createElement('button'); clearButton.textContent = 'נקה היסטוריה'; clearButton.id = 'clear-last-viewed'; controlsDiv.appendChild(clearButton); const closeButton = document.createElement('button'); closeButton.textContent = 'סגור'; closeButton.className = 'close-popup-btn'; controlsDiv.appendChild(closeButton); popup.appendChild(controlsDiv); document.body.appendChild(popup); $('#clear-last-viewed').on('click', function(e) { e.stopPropagation(); if (confirm('האם אתה בטוח שברצונך למחוק את היסטוריית הצפייה?')) { GM_deleteValue(STORAGE_KEY); closePopupAndRemoveListener(); console.log("[LastViewed] History cleared."); } }); $('.close-popup-btn').on('click', function(e) { e.stopPropagation(); closePopupAndRemoveListener(); }); $('#last-viewed-search').on('input', filterLastViewedList); setTimeout(addClosePopupListener, 50); $('#last-viewed-search').trigger('focus'); } let currentTooltip = null; const TOOLTIP_TEXT = "נצפו לאחרונה"; const TOOLTIP_GAP = -88.5; function showDynamicTooltip(targetElement) { const sidebar = document.querySelector('nav[component="sidebar/left"]'); if (sidebar && sidebar.classList.contains('open')) { return; } removeDynamicTooltip(); currentTooltip = document.createElement('div'); currentTooltip.id = 'last-viewed-dynamic-tooltip'; currentTooltip.textContent = TOOLTIP_TEXT; document.body.appendChild(currentTooltip); const rect = targetElement.getBoundingClientRect(); const tooltipRect = currentTooltip.getBoundingClientRect(); let top = rect.top + (rect.height / 2) - (tooltipRect.height / 2); let left = rect.left + rect.width + TOOLTIP_GAP - 58; if (top < 5) top = 5; if (top + tooltipRect.height > window.innerHeight - 5) top = window.innerHeight - tooltipRect.height - 5; if (left + tooltipRect.width > window.innerWidth - 5) left = window.innerWidth - tooltipRect.width - 5; currentTooltip.style.top = `${top}px`; currentTooltip.style.left = `${left}px`; currentTooltip.style.right = 'auto'; } function removeDynamicTooltip() { if (currentTooltip) { currentTooltip.remove(); currentTooltip = null; } } function createSidebarButton() { console.log("[LastViewed] Attempting createSidebarButton execution..."); if ($('#last-viewed-btn-li').length > 0) { console.log("[LastViewed] Button LI already exists."); return; } console.log("[LastViewed] Selecting navList..."); const $navList = $('nav[component="sidebar/left"] ul#main-nav'); console.log(`[LastViewed] $navList length: ${$navList.length}`); if ($navList.length === 0) { console.error("[LastViewed] FAILED to find $navList."); return; } // Exit if navList not found console.log("[LastViewed] Selecting recentPostsItem..."); const $recentPostsItem = $navList.find('li a[href="/recent"]').closest('li'); console.log(`[LastViewed] $recentPostsItem length: ${$recentPostsItem.length}`); if ($recentPostsItem.length === 0) { console.error("[LastViewed] FAILED to find $recentPostsItem."); return; } // Exit if recent post item not found console.log("[LastViewed] Creating $newItem element..."); const $newItem = $(` <li class="nav-item mx-2" id="last-viewed-btn-li" data-bs-original-title="נצפו לאחרונה"> <a class="nav-link navigation-link d-flex gap-2 justify-content-between align-items-center" href="#" role="button" id="last-viewed-btn" aria-label="נצפו לאחרונה"> <span class="d-flex gap-2 align-items-center text-nowrap truncate-open"> <span class="position-relative"> <i class="fa fa-fw fa-history" data-content=""></i> <span component="navigation/count" class="visible-closed position-absolute top-0 start-100 translate-middle badge rounded-1 bg-primary hidden"></span> </span> <span class="nav-text small visible-open fw-semibold text-truncate">נצפו לאחרונה</span> </span> <span component="navigation/count" class="visible-open badge rounded-1 bg-primary hidden"></span> </a> </li> `); console.log("[LastViewed] Attaching listeners to $newItem..."); $newItem.find('a#last-viewed-btn').on('click', function(event) { event.preventDefault(); event.stopPropagation(); removeDynamicTooltip(); showLastViewedPopup(); }); $newItem.on('mouseenter', function(event) { showDynamicTooltip(event.currentTarget); }).on('mouseleave', function() { removeDynamicTooltip(); }); try { console.log("[LastViewed] Attempting insertBefore..."); $newItem.insertBefore($('li[data-bs-original-title="נושאים שלא נפתרו"]')); setTimeout(() => { if ($('#last-viewed-btn-li').length > 0) { console.log("[LastViewed] Button inserted successfully (verified)."); } else { console.error("[LastViewed] Button insertion failed silently (not found after insertBefore)."); } }, 50); } catch (e) { console.error("[LastViewed] Error during button insertion:", e); } } let observer = null; function setupMutationObserver() { const targetNode = document.getElementById('content'); if (!targetNode) { console.error("[LastViewed] Cannot find target node '#content'. Retrying..."); setTimeout(setupMutationObserver, 1000); return; } const config = { childList: true, subtree: true }; const callback = function(mutationsList, observer) { let titleSpanFound = false; for(const mutation of mutationsList) { if (mutation.type === 'childList') { mutation.addedNodes.forEach(node => { if (node.nodeType === Node.ELEMENT_NODE) { if ($(node).find('span[component="topic/title"]').length > 0 || $(node).is('span[component="topic/title"]')) { titleSpanFound = true; } } }); } if (titleSpanFound) break; } if (titleSpanFound) { saveCurrentTopicDebounced(); } }; observer = new MutationObserver(callback); observer.observe(targetNode, config); const parentObserver = new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.removedNodes.forEach(removedNode => { if (removedNode.id === 'content') { console.warn("[LastViewed] Target node #content was removed. Re-initializing observer."); if(observer) observer.disconnect(); setTimeout(setupMutationObserver, 50); } }); }); }); if (targetNode.parentNode) { parentObserver.observe(targetNode.parentNode, { childList: true }); } } function initialize() { console.log("[LastViewed] Initializing script v2.0.1 (Increased Timeout)..."); if (typeof createSidebarButton !== 'function' || typeof saveCurrentTopicDebounced !== 'function' || typeof setupMutationObserver !== 'function') { console.error("[LastViewed] CRITICAL: One or more core functions not defined!"); return; } console.log("[LastViewed] Calling createSidebarButton..."); createSidebarButton(); console.log("[LastViewed] Calling saveCurrentTopicDebounced (initial)..."); saveCurrentTopicDebounced(); console.log("[LastViewed] Calling setupMutationObserver..."); setupMutationObserver(); console.log("[LastViewed] Initialization complete."); } ensureDependencies(initialize); })();
בהנאה!
וכאן המקום להודות ל @עדלאידע שבהשראת הסקריפט שלו יצרתי את הסקריפט הזה.
עריכה: ותודה על השיפורים הנפלאים בסקריפט!לכל אלו שהמתינו וייחלו לדבר הזה, הנה זה הגיע! @ארץ-הצבי @חטח @יוסף-אלחנן
נ.ב. אשמח אם מישהו שמבין יוכל לפתח את זה ולפתוח pull request בgithub ל nodebb.
(באותו רעיון שכתבתי כאן...) -
זה בעצם תוסף? או סקריפט שמתבסס על תוסף אחר?
למה לא לכתוב כבר תוסף ממש? -
@מים-אחרונים כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
זה בעצם תוסף? או סקריפט שמתבסס על תוסף אחר?
סקריפט שמתבסס על התוסף טמפרמונקי
למה לא לכתוב כבר תוסף ממש?
-
@מים-אחרונים זה סקריפט שמיועד לתוסף בשם tampermonkey - טמפרמונקי -תוסף המיועד להטמיע עיצובים ותכונות בתוך אתרים. חשבתי בתחילה לעשות את זה תוסף, אבל השיקול הוא שהרבה יותר נח כשזה מתווסף ישירות לסרגל הכלים של הפורום.
@החפץ-בעילום-שם-0 כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
@לאצי אולי שיוסיפו רשמית את האפשרות הזאת?
זה מה שאמרתי
@לאצי כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
נ.ב. אשמח אם מישהו שמבין יוכל לפתח את זה ולפתוח pull request בgithub ל nodebb.
-
@לאצי כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
נ.ב. אשמח אם מישהו שמבין יוכל לפתח את זה ולפתוח pull request בgithub ל nodebb.
הם לא יוסיפו טלאים מבוססי צד לקוח ועוד של GPT (שלא מכיר את הAPIs שנודביבי חושפים בצד לקוח ולכן עושה שטיקים נוראים)
-
@צדיק-תמים כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
ועוד של GPT
טעות, AI STUDIO..
@צדיק-תמים כתב בהמלצה | חדש בפורום! נושאים שנצפו לאחרונה!:
(שלא מכיר את הAPIs שנודביבי חושפים בצד לקוח ולכן עושה שטיקים נוראים)
לא הבנתי. אפשר הסבר לפשוטי העם?
-
@קראנץ-x27-ונילה איך התקנת את התוסף באנדרואיד?
אפשר להתקין תוספים רק בדפדפן Kiwi -
@צדיק-תמים
חנות האינטרנט של כרום - קישור שהוא הביא בתחילת ההסבר -
@קראנץ-x27-ונילה זה התקין לך במחשב לא במכשיר הנוכחי
-
@צדיק-תמים איך? אין לי מחשב
-
@קראנץ-x27-ונילה אין לך מחשב נוסף שמחובר לאותו חשבון?
-
@מים-אחרונים יש
-
@מים-אחרונים אין חנות כרום לאנדרואיד?