Guest User

Untitled

a guest
Jun 24th, 2025
277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.97 KB | None | 0 0
  1. // ==UserScript==
  2. // @name Markdown Editor Enhancer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description Live Markdown-to-HTML preview for cr8r.gg and expressional.social
  6. // @author Johan, ChatGPT
  7. // @match *://infosec.exchange/*
  8. // @match *://tilde.zone/*
  9. // @match *://cr8r.gg/*
  10. // @match *://expressional.social/*
  11. // @icon https://www.google.com/s2/favicons?sz=64&domain=cr8r.gg
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. (function () {
  16. 'use strict';
  17.  
  18. function enhanceTextareas() {
  19. document.querySelectorAll('textarea.autosuggest-textarea__textarea').forEach((textarea) => {
  20. textarea.classList.add('mdEdit');
  21. });
  22. }
  23.  
  24. function applyRegex(el) {
  25. let rawText = el.value;
  26.  
  27. // Экранируем HTML
  28. let escaped = rawText
  29. .replace(/&/g, "&")
  30. .replace(/</g, "&lt;")
  31. .replace(/>/g, "&gt;");
  32.  
  33. const paragraphs = escaped.split(/\n{2,}/).map(block => {
  34. // Blockquote
  35. if (/^(&gt;|>)\s+/m.test(block)) {
  36. const quoteLines = block
  37. .split('\n')
  38. .filter(line => /^(&gt;|>)\s+/.test(line))
  39. .map(line => line.replace(/^(&gt;|>)\s+/, ''))
  40. .join('<br>');
  41.  
  42. let html = applyInlineMarkdown(quoteLines);
  43. return `<blockquote>${html}</blockquote>`;
  44. }
  45.  
  46. // Headings
  47. const headingMatch = block.match(/^#{1,6} /);
  48. if (headingMatch) {
  49. const level = headingMatch[0].trim().length;
  50. const content = block.replace(/^#{1,6} /, '').trim();
  51. let html = applyInlineMarkdown(content);
  52. return `<h${level}>${html}</h${level}>`;
  53. }
  54.  
  55. // Paragraph with <br>
  56. let html = block.replace(/\n/g, '<br>');
  57. html = applyInlineMarkdown(html);
  58. return `<p>${html}</p>`;
  59. });
  60.  
  61. const parsed = paragraphs.join('');
  62.  
  63. let displayEl = el.nextElementSibling;
  64. if (!displayEl || !displayEl.classList.contains('status__content__text')) {
  65. displayEl = document.createElement('div');
  66. displayEl.classList.add('status__content__text', 'status__content__text--visible');
  67. displayEl.style.borderTop = "1px solid #ccc";
  68. displayEl.style.padding = "10px";
  69. displayEl.style.marginTop = "5px";
  70. el.insertAdjacentElement('afterend', displayEl);
  71. }
  72.  
  73. displayEl.innerHTML = parsed;
  74. }
  75.  
  76. function applyInlineMarkdown(text) {
  77. return text
  78. .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
  79. .replace(/\*(.*?)\*/g, '<em>$1</em>')
  80. .replace(/\b_(.*?)_\b/g, '<u>$1</u>')
  81. .replace(/~~(.*?)~~/g, '<del>$1</del>')
  82. .replace(/`(.*?)`/g, '<code>$1</code>')
  83. .replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" target="_blank" class="u-url status-link">$1</a>')
  84. .replace(/(^|\s)(https?:\/\/[^\s<]+)/g, (match, p1, url) => {
  85. return `${p1}<a href="${url}" target="_blank" class="u-url status-link">${url}</a>`;
  86. })
  87. .replace(/(#[\wА-Яа-я\d_]+)/g, '<span class="mention hashtag status-link">$1</span>')
  88. .replace(/(@[\w\d_.@]+)/g, '<span class="u-url mention status-link">$1</span>');
  89. }
  90.  
  91. function setupMarkdownEditing() {
  92. document.querySelectorAll('.mdEdit').forEach((el) => {
  93. el.addEventListener('input', () => applyRegex(el));
  94. applyRegex(el);
  95.  
  96. document.addEventListener('keydown', (ev) => {
  97. if (ev.key === 'Enter' && document.activeElement !== el) {
  98. el.focus();
  99. ev.preventDefault();
  100. ev.stopPropagation();
  101. } else if (ev.key === 'Escape' && document.activeElement === el) {
  102. el.blur();
  103.  
  104. // 👉 Раскомментируй одну из строк ниже, чтобы выбрать поведение:
  105.  
  106. // 🔽 Вариант 1: оставить текст в предпросмотре (по умолчанию — удобно для вычитки)
  107. // // ничего не делаем
  108.  
  109. // 🔽 Вариант 2: очищать предпросмотр при нажатии Escape
  110. // const displayEl = el.nextElementSibling;
  111. // if (displayEl && displayEl.classList.contains('status__content__text')) {
  112. // displayEl.innerHTML = '';
  113. // }
  114. }
  115. });
  116. });
  117. }
  118.  
  119. window.addEventListener('load', () => {
  120. setTimeout(() => {
  121. enhanceTextareas();
  122. setupMarkdownEditing();
  123. }, 5000);
  124. });
  125. })();
  126.  
Advertisement
Add Comment
Please, Sign In to add comment