שיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי! 🛠️✨
-
תמיד הפריע לי שבמערכת הפניות של נטפרי חסרה האפשרות לעצב את הטקסט בצורה נוחה וקלה – להדגיש מילים חשובות, להוסיף קישורים מוטמעים בתוך מילה , להוסיף צבע - בלחיצת כפתור, או לראות איך ההודעה תיראה לפני השליחה.
אז בעז"ה יצרתי סקריפט ל-Tampermonkey שמוסיף סרגל כלים מלא ומעוצב ישירות לתוך תיבת הכתיבה במערכת הפניות.
מה הסקריפט כולל?
הסרגל משתלב בעיצוב של האתר וכולל את הפיצ'רים הבאים:
- כותרות: תפריט נפתח לבחירת גודל כותרת (H1-H6).
- עיצוב בסיסי: מודגש, נטוי,
קו חוצה, וקו מפריד כזה:
- רשימות וקישורים: יצירת רשימות בולטים והוספת קישורים בקלות.
- בלוק קוד: כפתור להוספת קוד בצורה קריאה.
- יישור טקסט מתקדם: כפתורים ליישור לימין, למרכז, לשמאל, ויישור לשני הצדדים.
- צבעים: בורר צבעים (Color Picker) לשינוי צבע הטקסט.
- תצוגה מקדימה (Preview): כפתור שפותח חלונית צפה ומראה לכם בדיוק איך הפניה תיראה לאחר השליחה, כולל כל העיצובים.
איך מתקינים?
- ודאו שיש לכם את התוסף Tampermonkey מותקן בדפדפן.
- לחצו על האייקון של התוסף ובחרו ב-"צור סקריפט חדש" (Create a new script).
- מחקו את כל מה שמופיע בעורך, והדביקו את הקוד המצורף למטה.
- לחצו על שמירה (File -> Save או Ctrl+S).
- רעננו את עמוד הפניות בנטפרי – והסרגל יופיע!
הקוד להעתקה:
// ==UserScript== // @name NetFree Advanced Ticket Editor (Final) // @namespace http://tampermonkey.net/ // @version 5.0 // @description עורך טקסט מתקדם לנטפרי // @author לאצי&AI // @match https://netfree.link/app/* // @icon https://www.google.com/s2/favicons?sz=64&domain=netfree.link // @grant none // ==/UserScript== (function() { 'use strict'; // --- הגדרות סגנון (CSS) --- const styles = ` .nf-md-toolbar { display: flex; gap: 4px; padding: 6px; background: #f3f3f4; border: 1px solid #e7eaec; border-bottom: none; border-radius: 4px 4px 0 0; flex-wrap: wrap; direction: rtl; align-items: center; position: relative; } .nf-md-btn { background: #fff; border: 1px solid #ddd; cursor: pointer; width: 30px; height: 30px; color: #676a6c; border-radius: 3px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; position: relative; } .nf-md-btn:hover, .nf-md-btn.active { background-color: #fff; color: #1ab394; border-color: #1ab394; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .nf-md-active-textarea { border-top: none !important; border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .nf-dropdown-menu { display: none; position: absolute; top: 100%; right: 0; z-index: 1000; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); min-width: 160px; padding: 5px 0; margin-top: 2px; } .nf-dropdown-menu.show { display: block; } .nf-dropdown-item { display: block; padding: 8px 15px; clear: both; font-weight: 400; line-height: 1.42857143; color: #333; white-space: nowrap; text-decoration: none; cursor: pointer; text-align: right; } .nf-dropdown-item:hover { background-color: #f5f5f5; color: #262626; } .nf-color-picker-container { position: relative; display: inline-block; } input[type="color"] { opacity: 0; position: absolute; left: 0; top: 0; width: 100%; height: 100%; cursor: pointer; } .nf-preview-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 800px; height: 70vh; background: white; z-index: 9999; box-shadow: 0 0 50px rgba(0,0,0,0.5); border-radius: 8px; display: flex; flex-direction: column; border: 1px solid #ccc; } .nf-preview-header { padding: 15px; border-bottom: 1px solid #eee; background: #f9f9f9; font-weight: bold; display: flex; justify-content: space-between; border-radius: 8px 8px 0 0; } .nf-preview-content { padding: 20px; overflow-y: auto; flex-grow: 1; direction: rtl; font-family: Arial, sans-serif; line-height: 1.6; } /* עיצוב כותרות בתצוגה מקדימה - ללא קו תחתון */ .nf-preview-content h1, .nf-preview-content h2, .nf-preview-content h3 { margin-top: 15px; margin-bottom: 10px; font-weight: bold; } /* עיצוב קו מפריד בתצוגה מקדימה */ .nf-preview-content hr { border: 0; border-top: 1px solid #ccc; margin: 20px 0; } .nf-preview-content blockquote { border-right: 5px solid #eee; padding-right: 15px; margin-right: 0; color: #777; } .nf-preview-content code { background: #f4f4f4; padding: 2px 5px; border-radius: 3px; font-family: monospace; } .nf-preview-content pre { background: #f8f8f8; padding: 10px; border: 1px solid #e7e7e7; border-radius: 3px; direction: ltr; text-align: left; white-space: pre-wrap; } .nf-preview-close { cursor: pointer; color: #999; font-size: 20px; } .nf-preview-close:hover { color: #d9534f; } .nf-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9998; } .nf-separator { width: 1px; height: 20px; background: #ccc; margin: 0 5px; } `; // הוספת ה-CSS לדף const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); // --- פונקציות עזר לטקסט --- function insertSmart(textarea, prefix, suffix, placeholder) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const selectedText = text.substring(start, end); let newText = ''; let newSelectionStart = 0; let newSelectionEnd = 0; if (selectedText.length === 0) { newText = text.substring(0, start) + prefix + placeholder + suffix + text.substring(end); newSelectionStart = start + prefix.length; newSelectionEnd = newSelectionStart + placeholder.length; } else { newText = text.substring(0, start) + prefix + selectedText + suffix + text.substring(end); newSelectionStart = start; newSelectionEnd = end + prefix.length + suffix.length; } textarea.value = newText; textarea.focus(); textarea.setSelectionRange(newSelectionStart, newSelectionEnd); // עדכון אנגולר textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } // --- יצירת הסרגל --- function createToolbar(textarea) { const toolbar = document.createElement('div'); toolbar.className = 'nf-md-toolbar'; toolbar.onmousedown = (e) => e.preventDefault(); const createBtn = (icon, title, onClick) => { const btn = document.createElement('button'); btn.className = 'nf-md-btn'; btn.type = 'button'; btn.title = title; btn.innerHTML = `<i class="fa ${icon}"></i>`; btn.onclick = (e) => { e.preventDefault(); onClick(); }; return btn; }; const createSep = () => { const div = document.createElement('div'); div.className = 'nf-separator'; return div; }; // 1. כותרות const headingWrapper = document.createElement('div'); headingWrapper.style.position = 'relative'; const headingBtn = createBtn('fa-heading', 'כותרת', () => { headingList.classList.toggle('show'); }); const headingList = document.createElement('div'); headingList.className = 'nf-dropdown-menu'; for (let i = 1; i <= 6; i++) { const item = document.createElement('div'); item.className = 'nf-dropdown-item'; item.innerHTML = `<span style="color:#888; margin-left:5px;">H${i}</span> כותרת ${i}`; item.style.fontSize = (20 - i) + 'px'; item.onclick = () => { insertSmart(textarea, '#'.repeat(i) + ' ', '', `כותרת ${i}`); headingList.classList.remove('show'); }; headingList.appendChild(item); } headingWrapper.appendChild(headingBtn); headingWrapper.appendChild(headingList); toolbar.appendChild(headingWrapper); // 2. קו הפרדה ידני (הכפתור החדש) toolbar.appendChild(createBtn('fa-minus', 'קו הפרדה', () => insertSmart(textarea, '\n***\n', '', ''))); toolbar.appendChild(createSep()); // 3-5. עיצוב בסיסי toolbar.appendChild(createBtn('fa-bold', 'מודגש', () => insertSmart(textarea, '**', '**', 'טקסט מודגש'))); toolbar.appendChild(createBtn('fa-italic', 'נטוי', () => insertSmart(textarea, '*', '*', 'טקסט נטוי'))); toolbar.appendChild(createBtn('fa-strikethrough', 'קו חוצה', () => insertSmart(textarea, '~~', '~~', 'קו חוצה'))); toolbar.appendChild(createSep()); // 6-7. רשימה וקישור toolbar.appendChild(createBtn('fa-list-ul', 'רשימה', () => insertSmart(textarea, '* ', '', 'פריט רשימה'))); toolbar.appendChild(createBtn('fa-link', 'קישור', () => insertSmart(textarea, '[', '](http://)', 'טקסט קישור'))); // 8. קוד toolbar.appendChild(createBtn('fa-code', 'בלוק קוד', () => insertSmart(textarea, '\n```\n', '\n```\n', 'קוד'))); toolbar.appendChild(createSep()); // 9. יישור toolbar.appendChild(createBtn('fa-align-right', 'יישור לימין', () => insertSmart(textarea, '', '-|', 'יישור לימין'))); toolbar.appendChild(createBtn('fa-align-center', 'מרכוז', () => insertSmart(textarea, '|-', '-|', 'מרכוז'))); toolbar.appendChild(createBtn('fa-align-left', 'יישור לשמאל', () => insertSmart(textarea, '|-', '', 'יישור לשמאל'))); toolbar.appendChild(createBtn('fa-align-justify', 'יישור לשני הצדדים', () => insertSmart(textarea, '|=', '=|', 'רגיל'))); toolbar.appendChild(createSep()); // 10. צבע const colorWrapper = document.createElement('div'); colorWrapper.className = 'nf-color-picker-container'; const colorBtn = createBtn('fa-eyedropper', 'צבע טקסט', () => {}); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorInput.onchange = (e) => { insertSmart(textarea, `%(${e.target.value})[`, ']', 'צבעי טקסט'); }; colorWrapper.appendChild(colorBtn); colorWrapper.appendChild(colorInput); toolbar.appendChild(colorWrapper); toolbar.appendChild(createSep()); // 11. תצוגה מקדימה toolbar.appendChild(createBtn('fa-eye', 'תצוגה מקדימה', () => showPreview(textarea.value))); document.addEventListener('click', (e) => { if (!headingWrapper.contains(e.target)) headingList.classList.remove('show'); }); return toolbar; } // --- לוגיקת תצוגה מקדימה --- function showPreview(text) { let html = text // קודם כל מטפלים בבלוקים של קוד כדי שהתוכן שלהם לא יפורש .replace(/```([\s\S]*?)```/g, function(match, code) { return '<pre>' + code.replace(/</g, '<').replace(/>/g, '>') + '</pre>'; }) // כותרות (נקיות, ללא קו תחתון) .replace(/^#{1}\s+(.*)$/gm, '<h1>$1</h1>') .replace(/^#{2}\s+(.*)$/gm, '<h2>$1</h2>') .replace(/^#{3}\s+(.*)$/gm, '<h3>$1</h3>') .replace(/^#{4}\s+(.*)$/gm, '<h4>$1</h4>') .replace(/^#{5}\s+(.*)$/gm, '<h5>$1</h5>') .replace(/^#{6}\s+(.*)$/gm, '<h6>$1</h6>') // קו הפרדה (שלוש כוכביות) .replace(/^\s*\*\*\*\s*$/gm, '<hr>') // טיפול ביישור .replace(/\|-(.*?)-\|/g, '<div style="text-align: center;">$1</div>') .replace(/\|=(.*?)=\|/g, '<div style="text-align: justify;">$1</div>') .replace(/(^|\n)\|-(.*?)(?=\n|$)/g, '<div style="text-align: left; direction: ltr;">$2</div>') .replace(/(^|\n)(.*?)-\|(?=\n|$)/g, '<div style="text-align: right;">$2</div>') // עיצובים נוספים .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') .replace(/\*(.*?)\*/g, '<i>$1</i>') .replace(/~~(.*?)~~/g, '<del>$1</del>') .replace(/`([^`\n]+)`/g, '<code>$1</code>') .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" style="color:#1ab394">$1</a>') .replace(/%\((.*?)\)\[(.*?)\]/g, '<span style="color:$1">$2</span>') .replace(/^\*\s+(.*)/gm, '<li>$1</li>') // בסוף: המרת ירידות שורה .replace(/\n/g, '<br>'); // ניקוי html = html.replace(/<\/h(\d)><br>/g, '</h$1>'); html = html.replace(/<\/div><br>/g, '</div>'); html = html.replace(/<\/pre><br>/g, '</pre>'); html = html.replace(/<hr><br>/g, '<hr>'); // יצירת המודאל const overlay = document.createElement('div'); overlay.className = 'nf-overlay'; const modal = document.createElement('div'); modal.className = 'nf-preview-modal'; modal.innerHTML = ` <div class="nf-preview-header"> <span>תצוגה מקדימה</span> <span class="nf-preview-close">×</span> </div> <div class="nf-preview-content">${html}</div> `; const close = () => { document.body.removeChild(overlay); document.body.removeChild(modal); }; modal.querySelector('.nf-preview-close').onclick = close; overlay.onclick = close; document.body.appendChild(overlay); document.body.appendChild(modal); } // --- הזרקה לדף --- function scanAndInject() { const selectors = [ 'textarea[name="content"]', 'textarea#respons-text', 'textarea[placeholder="כתוב הודעה"]' ]; selectors.forEach(selector => { const textareas = document.querySelectorAll(selector); textareas.forEach(textarea => { if (textarea.previousElementSibling && textarea.previousElementSibling.classList.contains('nf-md-toolbar')) { return; } const toolbar = createToolbar(textarea); textarea.parentNode.insertBefore(toolbar, textarea); textarea.classList.add('nf-md-active-textarea'); }); }); } const observer = new MutationObserver(() => scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(scanAndInject, 1000); })();תהנו! אשמח לשמוע הערות או רעיונות לשיפור.

@לאצי
מעולה!שאפו גדול גם על השימוש בטמפרמונקי ולא בתוסף נפרד ומעצבן...
מתאים גם לשפת העיצוב באתר.
רעיון לשיפור - שיפור התצוגה המקדימה לרענון מיידי עם עיצוב מהרעיונות הבאים:
-
מעל התיבה, בצורה שבה זה יראה אחרי שזה ישלח. (אפשר להגזים ולשאוב גם את שם המשתמש וכו' כך שזה יופיע ממש כאילו התשובה שוגרה כבר...)
-
לצד התיבה, בדומה לתיבה המחולקת שיש כאן בפורום.
עריכה:
ביקשתי מג'מיני את האפשרות הראשונה.
נראה בסדר, לא בדקתי לעומק. לא לוקח אחריות.// ==UserScript== // @name NetFree Advanced Ticket Editor (Live Preview Default On) // @namespace http://tampermonkey.net/ // @version 6.1 // @description עורך טקסט מתקדם לנטפרי - תצוגה מקדימה חיה פתוחה כברירת מחדל // @author לאצי&AI // @match https://netfree.link/app/* // @icon https://www.google.com/s2/favicons?sz=64&domain=netfree.link // @grant none // ==/UserScript== (function() { 'use strict'; // --- הגדרות סגנון (CSS) --- const styles = ` .nf-md-toolbar { display: flex; gap: 4px; padding: 6px; background: #f3f3f4; border: 1px solid #e7eaec; border-bottom: none; border-radius: 4px 4px 0 0; flex-wrap: wrap; direction: rtl; align-items: center; position: relative; } .nf-md-btn { background: #fff; border: 1px solid #ddd; cursor: pointer; width: 30px; height: 30px; color: #676a6c; border-radius: 3px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; position: relative; } .nf-md-btn:hover, .nf-md-btn.active { background-color: #fff; color: #1ab394; border-color: #1ab394; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .nf-md-active-textarea { border-top: none !important; border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .nf-dropdown-menu { display: none; position: absolute; top: 100%; right: 0; z-index: 1000; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); min-width: 160px; padding: 5px 0; margin-top: 2px; } .nf-dropdown-menu.show { display: block; } .nf-dropdown-item { display: block; padding: 8px 15px; clear: both; font-weight: 400; line-height: 1.42857143; color: #333; white-space: nowrap; text-decoration: none; cursor: pointer; text-align: right; } .nf-dropdown-item:hover { background-color: #f5f5f5; color: #262626; } .nf-color-picker-container { position: relative; display: inline-block; } input[type="color"] { opacity: 0; position: absolute; left: 0; top: 0; width: 100%; height: 100%; cursor: pointer; } .nf-separator { width: 1px; height: 20px; background: #ccc; margin: 0 5px; } /* סגנון לאזור התצוגה המקדימה החיה */ .nf-live-preview-container { display: none; /* מוסתר ב-CSS, נדליק אותו ב-JS */ margin-bottom: 10px; padding: 10px; border: 1px dashed #1ab394; border-radius: 5px; background: rgba(26, 179, 148, 0.05); transition: all 0.3s ease; } .nf-live-preview-container.active { display: block; animation: fadeIn 0.3s; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .nf-preview-header-label { font-size: 11px; color: #1ab394; font-weight: bold; margin-bottom: 8px; display: block; text-align: right; border-bottom: 1px solid #1ab39433; padding-bottom: 2px; } `; const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); // --- לוגיקת המרת Markdown ל-HTML --- function parseMarkdown(text) { let html = text .replace(/```([\s\S]*?)```/g, (match, code) => '<pre>' + code.replace(/</g, '<').replace(/>/g, '>') + '</pre>') .replace(/^#{1}\s+(.*)$/gm, '<h1>$1</h1>') .replace(/^#{2}\s+(.*)$/gm, '<h2>$1</h2>') .replace(/^#{3}\s+(.*)$/gm, '<h3>$1</h3>') .replace(/^#{4}\s+(.*)$/gm, '<h4>$1</h4>') .replace(/^#{5}\s+(.*)$/gm, '<h5>$1</h5>') .replace(/^#{6}\s+(.*)$/gm, '<h6>$1</h6>') .replace(/^\s*\*\*\*\s*$/gm, '<hr>') .replace(/\|-(.*?)-\|/g, '<div style="text-align: center;">$1</div>') .replace(/\|=(.*?)=\|/g, '<div style="text-align: justify;">$1</div>') .replace(/(^|\n)\|-(.*?)(?=\n|$)/g, '<div style="text-align: left; direction: ltr;">$2</div>') .replace(/(^|\n)(.*?)-\|(?=\n|$)/g, '<div style="text-align: right;">$2</div>') .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') .replace(/\*(.*?)\*/g, '<i>$1</i>') .replace(/~~(.*?)~~/g, '<del>$1</del>') .replace(/`([^`\n]+)`/g, '<code>$1</code>') .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" style="color:#1ab394">$1</a>') .replace(/%\((.*?)\)\[(.*?)\]/g, '<span style="color:$1">$2</span>') .replace(/^\*\s+(.*)/gm, '<li>$1</li>') .replace(/\n/g, '<br>'); html = html.replace(/<\/h(\d)><br>/g, '</h$1>') .replace(/<\/div><br>/g, '</div>') .replace(/<\/pre><br>/g, '</pre>') .replace(/<hr><br>/g, '<hr>'); return html; } // --- שליפת פרטי משתמש --- function getUserDetails() { const nameElement = document.querySelector('ul.nav.navbar-top-links li a[href="#/user/info"] span'); const userName = nameElement ? nameElement.innerText.trim() : 'אני'; let avatarSrc = 'https://secure.gravatar.com/avatar/00000000000000000000000000000000?d=mp&f=y'; const myImages = document.querySelectorAll('.chat-discussion .chat-message img'); if (myImages.length > 0) { avatarSrc = myImages[myImages.length - 1].src; } return { name: userName, avatar: avatarSrc }; } // --- בניית HTML של התצוגה המקדימה --- function createPreviewContainer() { const user = getUserDetails(); const container = document.createElement('div'); container.className = 'nf-live-preview-container'; container.innerHTML = ` <span class="nf-preview-header-label">תצוגה מקדימה (כך זה ייראה לאחר השליחה):</span> <div class="chat-message left" style="background: transparent; padding: 0;"> <img class="pull-start" style="border-radius: 100%; height: 38px; width: 38px; float: right; margin-left: 10px;" src="${user.avatar}"> <div class="message" style="display: block; margin-right: 50px; background-color: #fff; border: 1px solid #e7eaec; text-align: right; padding: 10px 20px; border-radius: 4px;"> <div class="title" style="color: #1ab394; font-weight: bold; margin-bottom: 5px;"> <strong>${user.name}</strong> <div class="post-title-right" style="float: left; font-size: 10px; color: #888;"> לפני רגע </div> </div> <div class="message-content" style="padding-top: 5px; line-height: 1.5;"></div> </div> </div> `; return container; } // --- הכנסת טקסט חכמה --- function insertSmart(textarea, prefix, suffix, placeholder) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const selectedText = text.substring(start, end); let newText = ''; let newSelectionStart = 0; let newSelectionEnd = 0; if (selectedText.length === 0) { newText = text.substring(0, start) + prefix + placeholder + suffix + text.substring(end); newSelectionStart = start + prefix.length; newSelectionEnd = newSelectionStart + placeholder.length; } else { newText = text.substring(0, start) + prefix + selectedText + suffix + text.substring(end); newSelectionStart = start; newSelectionEnd = end + prefix.length + suffix.length; } textarea.value = newText; textarea.focus(); textarea.setSelectionRange(newSelectionStart, newSelectionEnd); textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } // --- יצירת הסרגל והלוגיקה --- function createToolbar(textarea) { const toolbar = document.createElement('div'); toolbar.className = 'nf-md-toolbar'; toolbar.onmousedown = (e) => e.preventDefault(); // 1. יצירת אלמנט התצוגה המקדימה const previewBox = createPreviewContainer(); const contentDiv = previewBox.querySelector('.message-content'); // פונקציית עדכון const updatePreview = () => { if (previewBox.classList.contains('active')) { contentDiv.innerHTML = parseMarkdown(textarea.value); } }; // האזנה לשינויים בתיבת הטקסט textarea.addEventListener('input', updatePreview); textarea.addEventListener('keyup', updatePreview); textarea.addEventListener('change', updatePreview); const createBtn = (icon, title, onClick) => { const btn = document.createElement('button'); btn.className = 'nf-md-btn'; btn.type = 'button'; btn.title = title; btn.innerHTML = `<i class="fa ${icon}"></i>`; btn.onclick = (e) => { e.preventDefault(); onClick(btn); }; return btn; }; const createSep = () => { const div = document.createElement('div'); div.className = 'nf-separator'; return div; }; // --- כפתורים --- const headingWrapper = document.createElement('div'); headingWrapper.style.position = 'relative'; const headingBtn = createBtn('fa-heading', 'כותרת', () => headingList.classList.toggle('show')); const headingList = document.createElement('div'); headingList.className = 'nf-dropdown-menu'; for (let i = 1; i <= 6; i++) { const item = document.createElement('div'); item.className = 'nf-dropdown-item'; item.innerHTML = `<span style="color:#888; margin-left:5px;">H${i}</span> כותרת ${i}`; item.style.fontSize = (20 - i) + 'px'; item.onclick = () => { insertSmart(textarea, '#'.repeat(i) + ' ', '', `כותרת ${i}`); headingList.classList.remove('show'); }; headingList.appendChild(item); } headingWrapper.appendChild(headingBtn); headingWrapper.appendChild(headingList); toolbar.appendChild(headingWrapper); document.addEventListener('click', (e) => { if (!headingWrapper.contains(e.target)) headingList.classList.remove('show'); }); toolbar.appendChild(createBtn('fa-minus', 'קו הפרדה', () => insertSmart(textarea, '\n***\n', '', ''))); toolbar.appendChild(createSep()); toolbar.appendChild(createBtn('fa-bold', 'מודגש', () => insertSmart(textarea, '**', '**', 'טקסט מודגש'))); toolbar.appendChild(createBtn('fa-italic', 'נטוי', () => insertSmart(textarea, '*', '*', 'טקסט נטוי'))); toolbar.appendChild(createBtn('fa-strikethrough', 'קו חוצה', () => insertSmart(textarea, '~~', '~~', 'קו חוצה'))); toolbar.appendChild(createSep()); toolbar.appendChild(createBtn('fa-list-ul', 'רשימה', () => insertSmart(textarea, '* ', '', 'פריט רשימה'))); toolbar.appendChild(createBtn('fa-link', 'קישור', () => insertSmart(textarea, '[', '](http://)', 'טקסט קישור'))); toolbar.appendChild(createBtn('fa-code', 'בלוק קוד', () => insertSmart(textarea, '\n```\n', '\n```\n', 'קוד'))); toolbar.appendChild(createSep()); toolbar.appendChild(createBtn('fa-align-right', 'יישור לימין', () => insertSmart(textarea, '', '-|', 'יישור לימין'))); toolbar.appendChild(createBtn('fa-align-center', 'מרכוז', () => insertSmart(textarea, '|-', '-|', 'מרכוז'))); toolbar.appendChild(createBtn('fa-align-left', 'יישור לשמאל', () => insertSmart(textarea, '|-', '', 'יישור לשמאל'))); toolbar.appendChild(createBtn('fa-align-justify', 'יישור לשני הצדדים', () => insertSmart(textarea, '|=', '=|', 'רגיל'))); toolbar.appendChild(createSep()); const colorWrapper = document.createElement('div'); colorWrapper.className = 'nf-color-picker-container'; const colorBtn = createBtn('fa-eyedropper', 'צבע טקסט', () => {}); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorInput.onchange = (e) => { insertSmart(textarea, `%(${e.target.value})[`, ']', 'צבעי טקסט'); }; colorWrapper.appendChild(colorBtn); colorWrapper.appendChild(colorInput); toolbar.appendChild(colorWrapper); toolbar.appendChild(createSep()); // --- כפתור תצוגה מקדימה חיה --- const eyeBtn = createBtn('fa-eye', 'הפעל/הסתר תצוגה חיה', (btn) => { previewBox.classList.toggle('active'); btn.classList.toggle('active'); if (previewBox.classList.contains('active')) { updatePreview(); } }); // ====================================== // הפעלה כברירת מחדל // ====================================== previewBox.classList.add('active'); eyeBtn.classList.add('active'); setTimeout(updatePreview, 0); // רענון ראשוני toolbar.appendChild(eyeBtn); return { toolbar, previewBox }; } // --- הזרקה לדף --- function scanAndInject() { const selectors = [ 'textarea[name="content"]', 'textarea#respons-text', 'textarea[placeholder="כתוב הודעה"]' ]; selectors.forEach(selector => { const textareas = document.querySelectorAll(selector); textareas.forEach(textarea => { if (textarea.previousElementSibling && textarea.previousElementSibling.classList.contains('nf-md-toolbar')) { return; } const { toolbar, previewBox } = createToolbar(textarea); textarea.parentNode.insertBefore(previewBox, textarea); textarea.parentNode.insertBefore(toolbar, textarea); textarea.classList.add('nf-md-active-textarea'); }); }); } const observer = new MutationObserver(() => scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(scanAndInject, 1000); })(); -
תמיד הפריע לי שבמערכת הפניות של נטפרי חסרה האפשרות לעצב את הטקסט בצורה נוחה וקלה – להדגיש מילים חשובות, להוסיף קישורים מוטמעים בתוך מילה , להוסיף צבע - בלחיצת כפתור, או לראות איך ההודעה תיראה לפני השליחה.
אז בעז"ה יצרתי סקריפט ל-Tampermonkey שמוסיף סרגל כלים מלא ומעוצב ישירות לתוך תיבת הכתיבה במערכת הפניות.
מה הסקריפט כולל?
הסרגל משתלב בעיצוב של האתר וכולל את הפיצ'רים הבאים:
- כותרות: תפריט נפתח לבחירת גודל כותרת (H1-H6).
- עיצוב בסיסי: מודגש, נטוי,
קו חוצה, וקו מפריד כזה:
- רשימות וקישורים: יצירת רשימות בולטים והוספת קישורים בקלות.
- בלוק קוד: כפתור להוספת קוד בצורה קריאה.
- יישור טקסט מתקדם: כפתורים ליישור לימין, למרכז, לשמאל, ויישור לשני הצדדים.
- צבעים: בורר צבעים (Color Picker) לשינוי צבע הטקסט.
- תצוגה מקדימה (Preview): כפתור שפותח חלונית צפה ומראה לכם בדיוק איך הפניה תיראה לאחר השליחה, כולל כל העיצובים.
איך מתקינים?
- ודאו שיש לכם את התוסף Tampermonkey מותקן בדפדפן.
- לחצו על האייקון של התוסף ובחרו ב-"צור סקריפט חדש" (Create a new script).
- מחקו את כל מה שמופיע בעורך, והדביקו את הקוד המצורף למטה.
- לחצו על שמירה (File -> Save או Ctrl+S).
- רעננו את עמוד הפניות בנטפרי – והסרגל יופיע!
הקוד להעתקה:
// ==UserScript== // @name NetFree Advanced Ticket Editor (Final) // @namespace http://tampermonkey.net/ // @version 5.0 // @description עורך טקסט מתקדם לנטפרי // @author לאצי&AI // @match https://netfree.link/app/* // @icon https://www.google.com/s2/favicons?sz=64&domain=netfree.link // @grant none // ==/UserScript== (function() { 'use strict'; // --- הגדרות סגנון (CSS) --- const styles = ` .nf-md-toolbar { display: flex; gap: 4px; padding: 6px; background: #f3f3f4; border: 1px solid #e7eaec; border-bottom: none; border-radius: 4px 4px 0 0; flex-wrap: wrap; direction: rtl; align-items: center; position: relative; } .nf-md-btn { background: #fff; border: 1px solid #ddd; cursor: pointer; width: 30px; height: 30px; color: #676a6c; border-radius: 3px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; position: relative; } .nf-md-btn:hover, .nf-md-btn.active { background-color: #fff; color: #1ab394; border-color: #1ab394; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .nf-md-active-textarea { border-top: none !important; border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .nf-dropdown-menu { display: none; position: absolute; top: 100%; right: 0; z-index: 1000; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); min-width: 160px; padding: 5px 0; margin-top: 2px; } .nf-dropdown-menu.show { display: block; } .nf-dropdown-item { display: block; padding: 8px 15px; clear: both; font-weight: 400; line-height: 1.42857143; color: #333; white-space: nowrap; text-decoration: none; cursor: pointer; text-align: right; } .nf-dropdown-item:hover { background-color: #f5f5f5; color: #262626; } .nf-color-picker-container { position: relative; display: inline-block; } input[type="color"] { opacity: 0; position: absolute; left: 0; top: 0; width: 100%; height: 100%; cursor: pointer; } .nf-preview-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 800px; height: 70vh; background: white; z-index: 9999; box-shadow: 0 0 50px rgba(0,0,0,0.5); border-radius: 8px; display: flex; flex-direction: column; border: 1px solid #ccc; } .nf-preview-header { padding: 15px; border-bottom: 1px solid #eee; background: #f9f9f9; font-weight: bold; display: flex; justify-content: space-between; border-radius: 8px 8px 0 0; } .nf-preview-content { padding: 20px; overflow-y: auto; flex-grow: 1; direction: rtl; font-family: Arial, sans-serif; line-height: 1.6; } /* עיצוב כותרות בתצוגה מקדימה - ללא קו תחתון */ .nf-preview-content h1, .nf-preview-content h2, .nf-preview-content h3 { margin-top: 15px; margin-bottom: 10px; font-weight: bold; } /* עיצוב קו מפריד בתצוגה מקדימה */ .nf-preview-content hr { border: 0; border-top: 1px solid #ccc; margin: 20px 0; } .nf-preview-content blockquote { border-right: 5px solid #eee; padding-right: 15px; margin-right: 0; color: #777; } .nf-preview-content code { background: #f4f4f4; padding: 2px 5px; border-radius: 3px; font-family: monospace; } .nf-preview-content pre { background: #f8f8f8; padding: 10px; border: 1px solid #e7e7e7; border-radius: 3px; direction: ltr; text-align: left; white-space: pre-wrap; } .nf-preview-close { cursor: pointer; color: #999; font-size: 20px; } .nf-preview-close:hover { color: #d9534f; } .nf-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9998; } .nf-separator { width: 1px; height: 20px; background: #ccc; margin: 0 5px; } `; // הוספת ה-CSS לדף const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); // --- פונקציות עזר לטקסט --- function insertSmart(textarea, prefix, suffix, placeholder) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const selectedText = text.substring(start, end); let newText = ''; let newSelectionStart = 0; let newSelectionEnd = 0; if (selectedText.length === 0) { newText = text.substring(0, start) + prefix + placeholder + suffix + text.substring(end); newSelectionStart = start + prefix.length; newSelectionEnd = newSelectionStart + placeholder.length; } else { newText = text.substring(0, start) + prefix + selectedText + suffix + text.substring(end); newSelectionStart = start; newSelectionEnd = end + prefix.length + suffix.length; } textarea.value = newText; textarea.focus(); textarea.setSelectionRange(newSelectionStart, newSelectionEnd); // עדכון אנגולר textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } // --- יצירת הסרגל --- function createToolbar(textarea) { const toolbar = document.createElement('div'); toolbar.className = 'nf-md-toolbar'; toolbar.onmousedown = (e) => e.preventDefault(); const createBtn = (icon, title, onClick) => { const btn = document.createElement('button'); btn.className = 'nf-md-btn'; btn.type = 'button'; btn.title = title; btn.innerHTML = `<i class="fa ${icon}"></i>`; btn.onclick = (e) => { e.preventDefault(); onClick(); }; return btn; }; const createSep = () => { const div = document.createElement('div'); div.className = 'nf-separator'; return div; }; // 1. כותרות const headingWrapper = document.createElement('div'); headingWrapper.style.position = 'relative'; const headingBtn = createBtn('fa-heading', 'כותרת', () => { headingList.classList.toggle('show'); }); const headingList = document.createElement('div'); headingList.className = 'nf-dropdown-menu'; for (let i = 1; i <= 6; i++) { const item = document.createElement('div'); item.className = 'nf-dropdown-item'; item.innerHTML = `<span style="color:#888; margin-left:5px;">H${i}</span> כותרת ${i}`; item.style.fontSize = (20 - i) + 'px'; item.onclick = () => { insertSmart(textarea, '#'.repeat(i) + ' ', '', `כותרת ${i}`); headingList.classList.remove('show'); }; headingList.appendChild(item); } headingWrapper.appendChild(headingBtn); headingWrapper.appendChild(headingList); toolbar.appendChild(headingWrapper); // 2. קו הפרדה ידני (הכפתור החדש) toolbar.appendChild(createBtn('fa-minus', 'קו הפרדה', () => insertSmart(textarea, '\n***\n', '', ''))); toolbar.appendChild(createSep()); // 3-5. עיצוב בסיסי toolbar.appendChild(createBtn('fa-bold', 'מודגש', () => insertSmart(textarea, '**', '**', 'טקסט מודגש'))); toolbar.appendChild(createBtn('fa-italic', 'נטוי', () => insertSmart(textarea, '*', '*', 'טקסט נטוי'))); toolbar.appendChild(createBtn('fa-strikethrough', 'קו חוצה', () => insertSmart(textarea, '~~', '~~', 'קו חוצה'))); toolbar.appendChild(createSep()); // 6-7. רשימה וקישור toolbar.appendChild(createBtn('fa-list-ul', 'רשימה', () => insertSmart(textarea, '* ', '', 'פריט רשימה'))); toolbar.appendChild(createBtn('fa-link', 'קישור', () => insertSmart(textarea, '[', '](http://)', 'טקסט קישור'))); // 8. קוד toolbar.appendChild(createBtn('fa-code', 'בלוק קוד', () => insertSmart(textarea, '\n```\n', '\n```\n', 'קוד'))); toolbar.appendChild(createSep()); // 9. יישור toolbar.appendChild(createBtn('fa-align-right', 'יישור לימין', () => insertSmart(textarea, '', '-|', 'יישור לימין'))); toolbar.appendChild(createBtn('fa-align-center', 'מרכוז', () => insertSmart(textarea, '|-', '-|', 'מרכוז'))); toolbar.appendChild(createBtn('fa-align-left', 'יישור לשמאל', () => insertSmart(textarea, '|-', '', 'יישור לשמאל'))); toolbar.appendChild(createBtn('fa-align-justify', 'יישור לשני הצדדים', () => insertSmart(textarea, '|=', '=|', 'רגיל'))); toolbar.appendChild(createSep()); // 10. צבע const colorWrapper = document.createElement('div'); colorWrapper.className = 'nf-color-picker-container'; const colorBtn = createBtn('fa-eyedropper', 'צבע טקסט', () => {}); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorInput.onchange = (e) => { insertSmart(textarea, `%(${e.target.value})[`, ']', 'צבעי טקסט'); }; colorWrapper.appendChild(colorBtn); colorWrapper.appendChild(colorInput); toolbar.appendChild(colorWrapper); toolbar.appendChild(createSep()); // 11. תצוגה מקדימה toolbar.appendChild(createBtn('fa-eye', 'תצוגה מקדימה', () => showPreview(textarea.value))); document.addEventListener('click', (e) => { if (!headingWrapper.contains(e.target)) headingList.classList.remove('show'); }); return toolbar; } // --- לוגיקת תצוגה מקדימה --- function showPreview(text) { let html = text // קודם כל מטפלים בבלוקים של קוד כדי שהתוכן שלהם לא יפורש .replace(/```([\s\S]*?)```/g, function(match, code) { return '<pre>' + code.replace(/</g, '<').replace(/>/g, '>') + '</pre>'; }) // כותרות (נקיות, ללא קו תחתון) .replace(/^#{1}\s+(.*)$/gm, '<h1>$1</h1>') .replace(/^#{2}\s+(.*)$/gm, '<h2>$1</h2>') .replace(/^#{3}\s+(.*)$/gm, '<h3>$1</h3>') .replace(/^#{4}\s+(.*)$/gm, '<h4>$1</h4>') .replace(/^#{5}\s+(.*)$/gm, '<h5>$1</h5>') .replace(/^#{6}\s+(.*)$/gm, '<h6>$1</h6>') // קו הפרדה (שלוש כוכביות) .replace(/^\s*\*\*\*\s*$/gm, '<hr>') // טיפול ביישור .replace(/\|-(.*?)-\|/g, '<div style="text-align: center;">$1</div>') .replace(/\|=(.*?)=\|/g, '<div style="text-align: justify;">$1</div>') .replace(/(^|\n)\|-(.*?)(?=\n|$)/g, '<div style="text-align: left; direction: ltr;">$2</div>') .replace(/(^|\n)(.*?)-\|(?=\n|$)/g, '<div style="text-align: right;">$2</div>') // עיצובים נוספים .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') .replace(/\*(.*?)\*/g, '<i>$1</i>') .replace(/~~(.*?)~~/g, '<del>$1</del>') .replace(/`([^`\n]+)`/g, '<code>$1</code>') .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" style="color:#1ab394">$1</a>') .replace(/%\((.*?)\)\[(.*?)\]/g, '<span style="color:$1">$2</span>') .replace(/^\*\s+(.*)/gm, '<li>$1</li>') // בסוף: המרת ירידות שורה .replace(/\n/g, '<br>'); // ניקוי html = html.replace(/<\/h(\d)><br>/g, '</h$1>'); html = html.replace(/<\/div><br>/g, '</div>'); html = html.replace(/<\/pre><br>/g, '</pre>'); html = html.replace(/<hr><br>/g, '<hr>'); // יצירת המודאל const overlay = document.createElement('div'); overlay.className = 'nf-overlay'; const modal = document.createElement('div'); modal.className = 'nf-preview-modal'; modal.innerHTML = ` <div class="nf-preview-header"> <span>תצוגה מקדימה</span> <span class="nf-preview-close">×</span> </div> <div class="nf-preview-content">${html}</div> `; const close = () => { document.body.removeChild(overlay); document.body.removeChild(modal); }; modal.querySelector('.nf-preview-close').onclick = close; overlay.onclick = close; document.body.appendChild(overlay); document.body.appendChild(modal); } // --- הזרקה לדף --- function scanAndInject() { const selectors = [ 'textarea[name="content"]', 'textarea#respons-text', 'textarea[placeholder="כתוב הודעה"]' ]; selectors.forEach(selector => { const textareas = document.querySelectorAll(selector); textareas.forEach(textarea => { if (textarea.previousElementSibling && textarea.previousElementSibling.classList.contains('nf-md-toolbar')) { return; } const toolbar = createToolbar(textarea); textarea.parentNode.insertBefore(toolbar, textarea); textarea.classList.add('nf-md-active-textarea'); }); }); } const observer = new MutationObserver(() => scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(scanAndInject, 1000); })();תהנו! אשמח לשמוע הערות או רעיונות לשיפור.

@לאצי כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:רעיונות לשיפור.
מאוד מפריע לי שקונטרול + אנטר לא שולח את ההודעה.
הסקריפט יכול ללכוד לחיצות מקלדת?
מכיוון שאני מאמין שעוד יהיו שיפורים רבים מאוד, אשמח שתשים בגיטהאב עם עדכון אוטומטי. -
תמיד הפריע לי שבמערכת הפניות של נטפרי חסרה האפשרות לעצב את הטקסט בצורה נוחה וקלה – להדגיש מילים חשובות, להוסיף קישורים מוטמעים בתוך מילה , להוסיף צבע - בלחיצת כפתור, או לראות איך ההודעה תיראה לפני השליחה.
אז בעז"ה יצרתי סקריפט ל-Tampermonkey שמוסיף סרגל כלים מלא ומעוצב ישירות לתוך תיבת הכתיבה במערכת הפניות.
מה הסקריפט כולל?
הסרגל משתלב בעיצוב של האתר וכולל את הפיצ'רים הבאים:
- כותרות: תפריט נפתח לבחירת גודל כותרת (H1-H6).
- עיצוב בסיסי: מודגש, נטוי,
קו חוצה, וקו מפריד כזה:
- רשימות וקישורים: יצירת רשימות בולטים והוספת קישורים בקלות.
- בלוק קוד: כפתור להוספת קוד בצורה קריאה.
- יישור טקסט מתקדם: כפתורים ליישור לימין, למרכז, לשמאל, ויישור לשני הצדדים.
- צבעים: בורר צבעים (Color Picker) לשינוי צבע הטקסט.
- תצוגה מקדימה (Preview): כפתור שפותח חלונית צפה ומראה לכם בדיוק איך הפניה תיראה לאחר השליחה, כולל כל העיצובים.
איך מתקינים?
- ודאו שיש לכם את התוסף Tampermonkey מותקן בדפדפן.
- לחצו על האייקון של התוסף ובחרו ב-"צור סקריפט חדש" (Create a new script).
- מחקו את כל מה שמופיע בעורך, והדביקו את הקוד המצורף למטה.
- לחצו על שמירה (File -> Save או Ctrl+S).
- רעננו את עמוד הפניות בנטפרי – והסרגל יופיע!
הקוד להעתקה:
// ==UserScript== // @name NetFree Advanced Ticket Editor (Final) // @namespace http://tampermonkey.net/ // @version 5.0 // @description עורך טקסט מתקדם לנטפרי // @author לאצי&AI // @match https://netfree.link/app/* // @icon https://www.google.com/s2/favicons?sz=64&domain=netfree.link // @grant none // ==/UserScript== (function() { 'use strict'; // --- הגדרות סגנון (CSS) --- const styles = ` .nf-md-toolbar { display: flex; gap: 4px; padding: 6px; background: #f3f3f4; border: 1px solid #e7eaec; border-bottom: none; border-radius: 4px 4px 0 0; flex-wrap: wrap; direction: rtl; align-items: center; position: relative; } .nf-md-btn { background: #fff; border: 1px solid #ddd; cursor: pointer; width: 30px; height: 30px; color: #676a6c; border-radius: 3px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; position: relative; } .nf-md-btn:hover, .nf-md-btn.active { background-color: #fff; color: #1ab394; border-color: #1ab394; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .nf-md-active-textarea { border-top: none !important; border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .nf-dropdown-menu { display: none; position: absolute; top: 100%; right: 0; z-index: 1000; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); min-width: 160px; padding: 5px 0; margin-top: 2px; } .nf-dropdown-menu.show { display: block; } .nf-dropdown-item { display: block; padding: 8px 15px; clear: both; font-weight: 400; line-height: 1.42857143; color: #333; white-space: nowrap; text-decoration: none; cursor: pointer; text-align: right; } .nf-dropdown-item:hover { background-color: #f5f5f5; color: #262626; } .nf-color-picker-container { position: relative; display: inline-block; } input[type="color"] { opacity: 0; position: absolute; left: 0; top: 0; width: 100%; height: 100%; cursor: pointer; } .nf-preview-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 800px; height: 70vh; background: white; z-index: 9999; box-shadow: 0 0 50px rgba(0,0,0,0.5); border-radius: 8px; display: flex; flex-direction: column; border: 1px solid #ccc; } .nf-preview-header { padding: 15px; border-bottom: 1px solid #eee; background: #f9f9f9; font-weight: bold; display: flex; justify-content: space-between; border-radius: 8px 8px 0 0; } .nf-preview-content { padding: 20px; overflow-y: auto; flex-grow: 1; direction: rtl; font-family: Arial, sans-serif; line-height: 1.6; } /* עיצוב כותרות בתצוגה מקדימה - ללא קו תחתון */ .nf-preview-content h1, .nf-preview-content h2, .nf-preview-content h3 { margin-top: 15px; margin-bottom: 10px; font-weight: bold; } /* עיצוב קו מפריד בתצוגה מקדימה */ .nf-preview-content hr { border: 0; border-top: 1px solid #ccc; margin: 20px 0; } .nf-preview-content blockquote { border-right: 5px solid #eee; padding-right: 15px; margin-right: 0; color: #777; } .nf-preview-content code { background: #f4f4f4; padding: 2px 5px; border-radius: 3px; font-family: monospace; } .nf-preview-content pre { background: #f8f8f8; padding: 10px; border: 1px solid #e7e7e7; border-radius: 3px; direction: ltr; text-align: left; white-space: pre-wrap; } .nf-preview-close { cursor: pointer; color: #999; font-size: 20px; } .nf-preview-close:hover { color: #d9534f; } .nf-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9998; } .nf-separator { width: 1px; height: 20px; background: #ccc; margin: 0 5px; } `; // הוספת ה-CSS לדף const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); // --- פונקציות עזר לטקסט --- function insertSmart(textarea, prefix, suffix, placeholder) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const selectedText = text.substring(start, end); let newText = ''; let newSelectionStart = 0; let newSelectionEnd = 0; if (selectedText.length === 0) { newText = text.substring(0, start) + prefix + placeholder + suffix + text.substring(end); newSelectionStart = start + prefix.length; newSelectionEnd = newSelectionStart + placeholder.length; } else { newText = text.substring(0, start) + prefix + selectedText + suffix + text.substring(end); newSelectionStart = start; newSelectionEnd = end + prefix.length + suffix.length; } textarea.value = newText; textarea.focus(); textarea.setSelectionRange(newSelectionStart, newSelectionEnd); // עדכון אנגולר textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } // --- יצירת הסרגל --- function createToolbar(textarea) { const toolbar = document.createElement('div'); toolbar.className = 'nf-md-toolbar'; toolbar.onmousedown = (e) => e.preventDefault(); const createBtn = (icon, title, onClick) => { const btn = document.createElement('button'); btn.className = 'nf-md-btn'; btn.type = 'button'; btn.title = title; btn.innerHTML = `<i class="fa ${icon}"></i>`; btn.onclick = (e) => { e.preventDefault(); onClick(); }; return btn; }; const createSep = () => { const div = document.createElement('div'); div.className = 'nf-separator'; return div; }; // 1. כותרות const headingWrapper = document.createElement('div'); headingWrapper.style.position = 'relative'; const headingBtn = createBtn('fa-heading', 'כותרת', () => { headingList.classList.toggle('show'); }); const headingList = document.createElement('div'); headingList.className = 'nf-dropdown-menu'; for (let i = 1; i <= 6; i++) { const item = document.createElement('div'); item.className = 'nf-dropdown-item'; item.innerHTML = `<span style="color:#888; margin-left:5px;">H${i}</span> כותרת ${i}`; item.style.fontSize = (20 - i) + 'px'; item.onclick = () => { insertSmart(textarea, '#'.repeat(i) + ' ', '', `כותרת ${i}`); headingList.classList.remove('show'); }; headingList.appendChild(item); } headingWrapper.appendChild(headingBtn); headingWrapper.appendChild(headingList); toolbar.appendChild(headingWrapper); // 2. קו הפרדה ידני (הכפתור החדש) toolbar.appendChild(createBtn('fa-minus', 'קו הפרדה', () => insertSmart(textarea, '\n***\n', '', ''))); toolbar.appendChild(createSep()); // 3-5. עיצוב בסיסי toolbar.appendChild(createBtn('fa-bold', 'מודגש', () => insertSmart(textarea, '**', '**', 'טקסט מודגש'))); toolbar.appendChild(createBtn('fa-italic', 'נטוי', () => insertSmart(textarea, '*', '*', 'טקסט נטוי'))); toolbar.appendChild(createBtn('fa-strikethrough', 'קו חוצה', () => insertSmart(textarea, '~~', '~~', 'קו חוצה'))); toolbar.appendChild(createSep()); // 6-7. רשימה וקישור toolbar.appendChild(createBtn('fa-list-ul', 'רשימה', () => insertSmart(textarea, '* ', '', 'פריט רשימה'))); toolbar.appendChild(createBtn('fa-link', 'קישור', () => insertSmart(textarea, '[', '](http://)', 'טקסט קישור'))); // 8. קוד toolbar.appendChild(createBtn('fa-code', 'בלוק קוד', () => insertSmart(textarea, '\n```\n', '\n```\n', 'קוד'))); toolbar.appendChild(createSep()); // 9. יישור toolbar.appendChild(createBtn('fa-align-right', 'יישור לימין', () => insertSmart(textarea, '', '-|', 'יישור לימין'))); toolbar.appendChild(createBtn('fa-align-center', 'מרכוז', () => insertSmart(textarea, '|-', '-|', 'מרכוז'))); toolbar.appendChild(createBtn('fa-align-left', 'יישור לשמאל', () => insertSmart(textarea, '|-', '', 'יישור לשמאל'))); toolbar.appendChild(createBtn('fa-align-justify', 'יישור לשני הצדדים', () => insertSmart(textarea, '|=', '=|', 'רגיל'))); toolbar.appendChild(createSep()); // 10. צבע const colorWrapper = document.createElement('div'); colorWrapper.className = 'nf-color-picker-container'; const colorBtn = createBtn('fa-eyedropper', 'צבע טקסט', () => {}); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorInput.onchange = (e) => { insertSmart(textarea, `%(${e.target.value})[`, ']', 'צבעי טקסט'); }; colorWrapper.appendChild(colorBtn); colorWrapper.appendChild(colorInput); toolbar.appendChild(colorWrapper); toolbar.appendChild(createSep()); // 11. תצוגה מקדימה toolbar.appendChild(createBtn('fa-eye', 'תצוגה מקדימה', () => showPreview(textarea.value))); document.addEventListener('click', (e) => { if (!headingWrapper.contains(e.target)) headingList.classList.remove('show'); }); return toolbar; } // --- לוגיקת תצוגה מקדימה --- function showPreview(text) { let html = text // קודם כל מטפלים בבלוקים של קוד כדי שהתוכן שלהם לא יפורש .replace(/```([\s\S]*?)```/g, function(match, code) { return '<pre>' + code.replace(/</g, '<').replace(/>/g, '>') + '</pre>'; }) // כותרות (נקיות, ללא קו תחתון) .replace(/^#{1}\s+(.*)$/gm, '<h1>$1</h1>') .replace(/^#{2}\s+(.*)$/gm, '<h2>$1</h2>') .replace(/^#{3}\s+(.*)$/gm, '<h3>$1</h3>') .replace(/^#{4}\s+(.*)$/gm, '<h4>$1</h4>') .replace(/^#{5}\s+(.*)$/gm, '<h5>$1</h5>') .replace(/^#{6}\s+(.*)$/gm, '<h6>$1</h6>') // קו הפרדה (שלוש כוכביות) .replace(/^\s*\*\*\*\s*$/gm, '<hr>') // טיפול ביישור .replace(/\|-(.*?)-\|/g, '<div style="text-align: center;">$1</div>') .replace(/\|=(.*?)=\|/g, '<div style="text-align: justify;">$1</div>') .replace(/(^|\n)\|-(.*?)(?=\n|$)/g, '<div style="text-align: left; direction: ltr;">$2</div>') .replace(/(^|\n)(.*?)-\|(?=\n|$)/g, '<div style="text-align: right;">$2</div>') // עיצובים נוספים .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') .replace(/\*(.*?)\*/g, '<i>$1</i>') .replace(/~~(.*?)~~/g, '<del>$1</del>') .replace(/`([^`\n]+)`/g, '<code>$1</code>') .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" style="color:#1ab394">$1</a>') .replace(/%\((.*?)\)\[(.*?)\]/g, '<span style="color:$1">$2</span>') .replace(/^\*\s+(.*)/gm, '<li>$1</li>') // בסוף: המרת ירידות שורה .replace(/\n/g, '<br>'); // ניקוי html = html.replace(/<\/h(\d)><br>/g, '</h$1>'); html = html.replace(/<\/div><br>/g, '</div>'); html = html.replace(/<\/pre><br>/g, '</pre>'); html = html.replace(/<hr><br>/g, '<hr>'); // יצירת המודאל const overlay = document.createElement('div'); overlay.className = 'nf-overlay'; const modal = document.createElement('div'); modal.className = 'nf-preview-modal'; modal.innerHTML = ` <div class="nf-preview-header"> <span>תצוגה מקדימה</span> <span class="nf-preview-close">×</span> </div> <div class="nf-preview-content">${html}</div> `; const close = () => { document.body.removeChild(overlay); document.body.removeChild(modal); }; modal.querySelector('.nf-preview-close').onclick = close; overlay.onclick = close; document.body.appendChild(overlay); document.body.appendChild(modal); } // --- הזרקה לדף --- function scanAndInject() { const selectors = [ 'textarea[name="content"]', 'textarea#respons-text', 'textarea[placeholder="כתוב הודעה"]' ]; selectors.forEach(selector => { const textareas = document.querySelectorAll(selector); textareas.forEach(textarea => { if (textarea.previousElementSibling && textarea.previousElementSibling.classList.contains('nf-md-toolbar')) { return; } const toolbar = createToolbar(textarea); textarea.parentNode.insertBefore(toolbar, textarea); textarea.classList.add('nf-md-active-textarea'); }); }); } const observer = new MutationObserver(() => scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(scanAndInject, 1000); })();תהנו! אשמח לשמוע הערות או רעיונות לשיפור.

@לאצי כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:להדגיש מילים חשובות
השאלה האם גם העובד שמקבל את ההודעה יראה את זה מודגש.
אחרת מה הרווחת? -
@לאצי כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:להדגיש מילים חשובות
השאלה האם גם העובד שמקבל את ההודעה יראה את זה מודגש.
אחרת מה הרווחת?@צדיק-וטוב-לו-0 לכאו' כן, כי גם מה שאני מקבל מהם כתוב במרקדאון.
אבל ליתר ביטחון בא נשאל מישהו שמכיר את נטפרי מקרוב. אולי @צדיק-תמים או שמא הרב @magicode בכבודו ובעצמו? -
@צדיק-וטוב-לו-0 לכאו' כן, כי גם מה שאני מקבל מהם כתוב במרקדאון.
אבל ליתר ביטחון בא נשאל מישהו שמכיר את נטפרי מקרוב. אולי @צדיק-תמים או שמא הרב @magicode בכבודו ובעצמו?@לאצי כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:@צדיק-וטוב-לו-0 לכאו' כן, כי גם מה שאני מקבל מהם כתוב במרקדאון.
אבל ליתר ביטחון בא נשאל מישהו שמכיר את נטפרי מקרוב. אולי @צדיק-תמים או שמא הרב @magicode בכבודו ובעצמו?אני מקווה שלא הקפצת לו התראה

בכל אופן זה ברור שזה מוצג בMD, לא אמור להיות שום הבדל בין מה שאתה רואה לאחר השליחה לבין מה שהנציג רואה.
ברשימת הפניות מבחוץ העיצוב לא עובד ומוצג כטקסט פשוט.
-
@לאצי כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:@צדיק-וטוב-לו-0 לכאו' כן, כי גם מה שאני מקבל מהם כתוב במרקדאון.
אבל ליתר ביטחון בא נשאל מישהו שמכיר את נטפרי מקרוב. אולי @צדיק-תמים או שמא הרב @magicode בכבודו ובעצמו?אני מקווה שלא הקפצת לו התראה

בכל אופן זה ברור שזה מוצג בMD, לא אמור להיות שום הבדל בין מה שאתה רואה לאחר השליחה לבין מה שהנציג רואה.
ברשימת הפניות מבחוץ העיצוב לא עובד ומוצג כטקסט פשוט.
התמונה לא נפתחת. מה תוכנה?
@ע-ה-דכו-ע כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:ברשימת הפניות מבחוץ העיצוב לא עובד ומוצג כטקסט פשוט.
נכון. ושם באמת רואים כי הם משתמשים עם מרקדאון ולא עם html למשל.
-
התמונה לא נפתחת. מה תוכנה?
@ע-ה-דכו-ע כתב בשיתוף | סקריפט לטמפרמונקי ליצירת Markdown בקלות במערכת הפניות של נטפרי!
️
:ברשימת הפניות מבחוץ העיצוב לא עובד ומוצג כטקסט פשוט.
נכון. ושם באמת רואים כי הם משתמשים עם מרקדאון ולא עם html למשל.
-
תמיד הפריע לי שבמערכת הפניות של נטפרי חסרה האפשרות לעצב את הטקסט בצורה נוחה וקלה – להדגיש מילים חשובות, להוסיף קישורים מוטמעים בתוך מילה , להוסיף צבע - בלחיצת כפתור, או לראות איך ההודעה תיראה לפני השליחה.
אז בעז"ה יצרתי סקריפט ל-Tampermonkey שמוסיף סרגל כלים מלא ומעוצב ישירות לתוך תיבת הכתיבה במערכת הפניות.
מה הסקריפט כולל?
הסרגל משתלב בעיצוב של האתר וכולל את הפיצ'רים הבאים:
- כותרות: תפריט נפתח לבחירת גודל כותרת (H1-H6).
- עיצוב בסיסי: מודגש, נטוי,
קו חוצה, וקו מפריד כזה:
- רשימות וקישורים: יצירת רשימות בולטים והוספת קישורים בקלות.
- בלוק קוד: כפתור להוספת קוד בצורה קריאה.
- יישור טקסט מתקדם: כפתורים ליישור לימין, למרכז, לשמאל, ויישור לשני הצדדים.
- צבעים: בורר צבעים (Color Picker) לשינוי צבע הטקסט.
- תצוגה מקדימה (Preview): כפתור שפותח חלונית צפה ומראה לכם בדיוק איך הפניה תיראה לאחר השליחה, כולל כל העיצובים.
איך מתקינים?
- ודאו שיש לכם את התוסף Tampermonkey מותקן בדפדפן.
- לחצו על האייקון של התוסף ובחרו ב-"צור סקריפט חדש" (Create a new script).
- מחקו את כל מה שמופיע בעורך, והדביקו את הקוד המצורף למטה.
- לחצו על שמירה (File -> Save או Ctrl+S).
- רעננו את עמוד הפניות בנטפרי – והסרגל יופיע!
הקוד להעתקה:
// ==UserScript== // @name NetFree Advanced Ticket Editor (Final) // @namespace http://tampermonkey.net/ // @version 5.0 // @description עורך טקסט מתקדם לנטפרי // @author לאצי&AI // @match https://netfree.link/app/* // @icon https://www.google.com/s2/favicons?sz=64&domain=netfree.link // @grant none // ==/UserScript== (function() { 'use strict'; // --- הגדרות סגנון (CSS) --- const styles = ` .nf-md-toolbar { display: flex; gap: 4px; padding: 6px; background: #f3f3f4; border: 1px solid #e7eaec; border-bottom: none; border-radius: 4px 4px 0 0; flex-wrap: wrap; direction: rtl; align-items: center; position: relative; } .nf-md-btn { background: #fff; border: 1px solid #ddd; cursor: pointer; width: 30px; height: 30px; color: #676a6c; border-radius: 3px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s; position: relative; } .nf-md-btn:hover, .nf-md-btn.active { background-color: #fff; color: #1ab394; border-color: #1ab394; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .nf-md-active-textarea { border-top: none !important; border-top-left-radius: 0 !important; border-top-right-radius: 0 !important; } .nf-dropdown-menu { display: none; position: absolute; top: 100%; right: 0; z-index: 1000; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 6px 12px rgba(0,0,0,0.175); min-width: 160px; padding: 5px 0; margin-top: 2px; } .nf-dropdown-menu.show { display: block; } .nf-dropdown-item { display: block; padding: 8px 15px; clear: both; font-weight: 400; line-height: 1.42857143; color: #333; white-space: nowrap; text-decoration: none; cursor: pointer; text-align: right; } .nf-dropdown-item:hover { background-color: #f5f5f5; color: #262626; } .nf-color-picker-container { position: relative; display: inline-block; } input[type="color"] { opacity: 0; position: absolute; left: 0; top: 0; width: 100%; height: 100%; cursor: pointer; } .nf-preview-modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 80%; max-width: 800px; height: 70vh; background: white; z-index: 9999; box-shadow: 0 0 50px rgba(0,0,0,0.5); border-radius: 8px; display: flex; flex-direction: column; border: 1px solid #ccc; } .nf-preview-header { padding: 15px; border-bottom: 1px solid #eee; background: #f9f9f9; font-weight: bold; display: flex; justify-content: space-between; border-radius: 8px 8px 0 0; } .nf-preview-content { padding: 20px; overflow-y: auto; flex-grow: 1; direction: rtl; font-family: Arial, sans-serif; line-height: 1.6; } /* עיצוב כותרות בתצוגה מקדימה - ללא קו תחתון */ .nf-preview-content h1, .nf-preview-content h2, .nf-preview-content h3 { margin-top: 15px; margin-bottom: 10px; font-weight: bold; } /* עיצוב קו מפריד בתצוגה מקדימה */ .nf-preview-content hr { border: 0; border-top: 1px solid #ccc; margin: 20px 0; } .nf-preview-content blockquote { border-right: 5px solid #eee; padding-right: 15px; margin-right: 0; color: #777; } .nf-preview-content code { background: #f4f4f4; padding: 2px 5px; border-radius: 3px; font-family: monospace; } .nf-preview-content pre { background: #f8f8f8; padding: 10px; border: 1px solid #e7e7e7; border-radius: 3px; direction: ltr; text-align: left; white-space: pre-wrap; } .nf-preview-close { cursor: pointer; color: #999; font-size: 20px; } .nf-preview-close:hover { color: #d9534f; } .nf-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 9998; } .nf-separator { width: 1px; height: 20px; background: #ccc; margin: 0 5px; } `; // הוספת ה-CSS לדף const styleSheet = document.createElement("style"); styleSheet.innerText = styles; document.head.appendChild(styleSheet); // --- פונקציות עזר לטקסט --- function insertSmart(textarea, prefix, suffix, placeholder) { const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; const selectedText = text.substring(start, end); let newText = ''; let newSelectionStart = 0; let newSelectionEnd = 0; if (selectedText.length === 0) { newText = text.substring(0, start) + prefix + placeholder + suffix + text.substring(end); newSelectionStart = start + prefix.length; newSelectionEnd = newSelectionStart + placeholder.length; } else { newText = text.substring(0, start) + prefix + selectedText + suffix + text.substring(end); newSelectionStart = start; newSelectionEnd = end + prefix.length + suffix.length; } textarea.value = newText; textarea.focus(); textarea.setSelectionRange(newSelectionStart, newSelectionEnd); // עדכון אנגולר textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.dispatchEvent(new Event('change', { bubbles: true })); } // --- יצירת הסרגל --- function createToolbar(textarea) { const toolbar = document.createElement('div'); toolbar.className = 'nf-md-toolbar'; toolbar.onmousedown = (e) => e.preventDefault(); const createBtn = (icon, title, onClick) => { const btn = document.createElement('button'); btn.className = 'nf-md-btn'; btn.type = 'button'; btn.title = title; btn.innerHTML = `<i class="fa ${icon}"></i>`; btn.onclick = (e) => { e.preventDefault(); onClick(); }; return btn; }; const createSep = () => { const div = document.createElement('div'); div.className = 'nf-separator'; return div; }; // 1. כותרות const headingWrapper = document.createElement('div'); headingWrapper.style.position = 'relative'; const headingBtn = createBtn('fa-heading', 'כותרת', () => { headingList.classList.toggle('show'); }); const headingList = document.createElement('div'); headingList.className = 'nf-dropdown-menu'; for (let i = 1; i <= 6; i++) { const item = document.createElement('div'); item.className = 'nf-dropdown-item'; item.innerHTML = `<span style="color:#888; margin-left:5px;">H${i}</span> כותרת ${i}`; item.style.fontSize = (20 - i) + 'px'; item.onclick = () => { insertSmart(textarea, '#'.repeat(i) + ' ', '', `כותרת ${i}`); headingList.classList.remove('show'); }; headingList.appendChild(item); } headingWrapper.appendChild(headingBtn); headingWrapper.appendChild(headingList); toolbar.appendChild(headingWrapper); // 2. קו הפרדה ידני (הכפתור החדש) toolbar.appendChild(createBtn('fa-minus', 'קו הפרדה', () => insertSmart(textarea, '\n***\n', '', ''))); toolbar.appendChild(createSep()); // 3-5. עיצוב בסיסי toolbar.appendChild(createBtn('fa-bold', 'מודגש', () => insertSmart(textarea, '**', '**', 'טקסט מודגש'))); toolbar.appendChild(createBtn('fa-italic', 'נטוי', () => insertSmart(textarea, '*', '*', 'טקסט נטוי'))); toolbar.appendChild(createBtn('fa-strikethrough', 'קו חוצה', () => insertSmart(textarea, '~~', '~~', 'קו חוצה'))); toolbar.appendChild(createSep()); // 6-7. רשימה וקישור toolbar.appendChild(createBtn('fa-list-ul', 'רשימה', () => insertSmart(textarea, '* ', '', 'פריט רשימה'))); toolbar.appendChild(createBtn('fa-link', 'קישור', () => insertSmart(textarea, '[', '](http://)', 'טקסט קישור'))); // 8. קוד toolbar.appendChild(createBtn('fa-code', 'בלוק קוד', () => insertSmart(textarea, '\n```\n', '\n```\n', 'קוד'))); toolbar.appendChild(createSep()); // 9. יישור toolbar.appendChild(createBtn('fa-align-right', 'יישור לימין', () => insertSmart(textarea, '', '-|', 'יישור לימין'))); toolbar.appendChild(createBtn('fa-align-center', 'מרכוז', () => insertSmart(textarea, '|-', '-|', 'מרכוז'))); toolbar.appendChild(createBtn('fa-align-left', 'יישור לשמאל', () => insertSmart(textarea, '|-', '', 'יישור לשמאל'))); toolbar.appendChild(createBtn('fa-align-justify', 'יישור לשני הצדדים', () => insertSmart(textarea, '|=', '=|', 'רגיל'))); toolbar.appendChild(createSep()); // 10. צבע const colorWrapper = document.createElement('div'); colorWrapper.className = 'nf-color-picker-container'; const colorBtn = createBtn('fa-eyedropper', 'צבע טקסט', () => {}); const colorInput = document.createElement('input'); colorInput.type = 'color'; colorInput.value = '#000000'; colorInput.onchange = (e) => { insertSmart(textarea, `%(${e.target.value})[`, ']', 'צבעי טקסט'); }; colorWrapper.appendChild(colorBtn); colorWrapper.appendChild(colorInput); toolbar.appendChild(colorWrapper); toolbar.appendChild(createSep()); // 11. תצוגה מקדימה toolbar.appendChild(createBtn('fa-eye', 'תצוגה מקדימה', () => showPreview(textarea.value))); document.addEventListener('click', (e) => { if (!headingWrapper.contains(e.target)) headingList.classList.remove('show'); }); return toolbar; } // --- לוגיקת תצוגה מקדימה --- function showPreview(text) { let html = text // קודם כל מטפלים בבלוקים של קוד כדי שהתוכן שלהם לא יפורש .replace(/```([\s\S]*?)```/g, function(match, code) { return '<pre>' + code.replace(/</g, '<').replace(/>/g, '>') + '</pre>'; }) // כותרות (נקיות, ללא קו תחתון) .replace(/^#{1}\s+(.*)$/gm, '<h1>$1</h1>') .replace(/^#{2}\s+(.*)$/gm, '<h2>$1</h2>') .replace(/^#{3}\s+(.*)$/gm, '<h3>$1</h3>') .replace(/^#{4}\s+(.*)$/gm, '<h4>$1</h4>') .replace(/^#{5}\s+(.*)$/gm, '<h5>$1</h5>') .replace(/^#{6}\s+(.*)$/gm, '<h6>$1</h6>') // קו הפרדה (שלוש כוכביות) .replace(/^\s*\*\*\*\s*$/gm, '<hr>') // טיפול ביישור .replace(/\|-(.*?)-\|/g, '<div style="text-align: center;">$1</div>') .replace(/\|=(.*?)=\|/g, '<div style="text-align: justify;">$1</div>') .replace(/(^|\n)\|-(.*?)(?=\n|$)/g, '<div style="text-align: left; direction: ltr;">$2</div>') .replace(/(^|\n)(.*?)-\|(?=\n|$)/g, '<div style="text-align: right;">$2</div>') // עיצובים נוספים .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') .replace(/\*(.*?)\*/g, '<i>$1</i>') .replace(/~~(.*?)~~/g, '<del>$1</del>') .replace(/`([^`\n]+)`/g, '<code>$1</code>') .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" style="color:#1ab394">$1</a>') .replace(/%\((.*?)\)\[(.*?)\]/g, '<span style="color:$1">$2</span>') .replace(/^\*\s+(.*)/gm, '<li>$1</li>') // בסוף: המרת ירידות שורה .replace(/\n/g, '<br>'); // ניקוי html = html.replace(/<\/h(\d)><br>/g, '</h$1>'); html = html.replace(/<\/div><br>/g, '</div>'); html = html.replace(/<\/pre><br>/g, '</pre>'); html = html.replace(/<hr><br>/g, '<hr>'); // יצירת המודאל const overlay = document.createElement('div'); overlay.className = 'nf-overlay'; const modal = document.createElement('div'); modal.className = 'nf-preview-modal'; modal.innerHTML = ` <div class="nf-preview-header"> <span>תצוגה מקדימה</span> <span class="nf-preview-close">×</span> </div> <div class="nf-preview-content">${html}</div> `; const close = () => { document.body.removeChild(overlay); document.body.removeChild(modal); }; modal.querySelector('.nf-preview-close').onclick = close; overlay.onclick = close; document.body.appendChild(overlay); document.body.appendChild(modal); } // --- הזרקה לדף --- function scanAndInject() { const selectors = [ 'textarea[name="content"]', 'textarea#respons-text', 'textarea[placeholder="כתוב הודעה"]' ]; selectors.forEach(selector => { const textareas = document.querySelectorAll(selector); textareas.forEach(textarea => { if (textarea.previousElementSibling && textarea.previousElementSibling.classList.contains('nf-md-toolbar')) { return; } const toolbar = createToolbar(textarea); textarea.parentNode.insertBefore(toolbar, textarea); textarea.classList.add('nf-md-active-textarea'); }); }); } const observer = new MutationObserver(() => scanAndInject()); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(scanAndInject, 1000); })();תהנו! אשמח לשמוע הערות או רעיונות לשיפור.

-
@צדיק-וטוב-לו-0 לכאו' כן, כי גם מה שאני מקבל מהם כתוב במרקדאון.
אבל ליתר ביטחון בא נשאל מישהו שמכיר את נטפרי מקרוב. אולי @צדיק-תמים או שמא הרב @magicode בכבודו ובעצמו?