// ==UserScript==
// @name Markdown Tool
// @name:pt-BR Ferramenta Markdown
// @name:en Markdown Tool
// @name:es Herramienta Markdown
// @name:zh-CN Markdown 工具
// @namespace http://github.com/0H4S
// @version 1.2
// @description Adds an advanced Markdown toolbar to text fields on Greasy Fork, allowing for easy insertion of formatting, lists, links, images, tables, videos, colors, and other elements with just one click.
// @description:pt-BR Adiciona uma barra de ferramentas avançada de Markdown aos campos de texto do Greasy Fork, permitindo inserir facilmente formatação, listas, links, imagens, tabelas, vídeos, cores e outros elementos com apenas um clique.
// @description:en Adds an advanced Markdown toolbar to text fields on Greasy Fork, allowing for easy insertion of formatting, lists, links, images, tables, videos, colors, and other elements with just one click.
// @description:es Agrega una barra de herramientas de Markdown avanzada a los campos de texto de Greasy Fork, lo que permite insertar fácilmente formato, listas, enlaces, imágenes, tablas, videos, colores y otros elementos con solo un clic.
// @description:zh-CN 在 Greasy Fork 的文本字段中添加高级 Markdown 工具栏,允许用户轻松插入格式、列表、链接、图像、表格、视频、颜色和其他元素,只需单击一下。
// @author OHAS
// @license CC-BY-NC-ND-4.0
// @copyright 2025 OHAS. All Rights Reserved.
// @icon https://cdn-icons-png.flaticon.com/512/9755/9755739.png
// @match *://greasyfork.org/*/*/new*
// @match *://greasyfork.org/*/*/edit*
// @match *://greasyfork.org/*/*/*/feedback*
// @match *://greasyfork.org/*/*/*/discussions*
// @match *://greasyfork.org/*/*/*/sets*
// @require https://update.greasyfork.org/scripts/549920/Script%20Notifier.js
// @connect gist.githubusercontent.com
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function() {
'use strict';
if (window.top !== window.self) {
return;
}
const SCRIPT_CONFIG = {
notificationsUrl: 'https://gist.githubusercontent.com/0H4S/6c31fa1e1f94932ca8bacbfd85d3009b/raw/markdown_tool_notifications.json',
scriptVersion: '1.2',
};
const notifier = new ScriptNotifier(SCRIPT_CONFIG);
notifier.run();
const translations = {
'en': {
langName: 'English',
languageSettings: '🌐 Language',
close: 'Close',
titles: 'Headings',
title_placeholder: 'Heading',
bold: 'Bold',
bold_placeholder: 'bold text',
italic: 'Italic',
italic_placeholder: 'italic text',
underline: 'Underline',
underline_placeholder: 'underlined text',
strikethrough: 'Strikethrough',
strikethrough_placeholder: 'strikethrough text',
unordered_list: 'Unordered List',
list_item_placeholder: 'Item',
ordered_list: 'Ordered List',
quote: 'Quote',
quote_placeholder: 'Quote',
inline_code: 'Inline Code',
inline_code_placeholder: 'code',
code_block: 'Code Block',
code_block_placeholder: 'code here',
horizontal_line: 'Horizontal Line',
link: 'Link',
prompt_insert_url: 'Enter the URL:',
link_text_placeholder: 'link text',
image: 'Image',
prompt_insert_image_url: 'Enter the image URL (https):',
table: 'Table',
prompt_columns: 'Number of columns:',
prompt_rows: 'Number of rows:',
table_header_placeholder: 'Header',
table_cell_placeholder: 'Cell',
video: 'Video (YouTube/Bilibili)',
prompt_insert_video_url: 'Enter the video URL (YouTube or Bilibili):',
alert_invalid_video_url: 'Invalid or unsupported video URL.',
subscript: 'Subscript',
subscript_placeholder: 'sub',
superscript: 'Superscript',
superscript_placeholder: 'sup',
highlight: 'Highlight',
highlight_placeholder: 'highlighted',
keyboard: 'Keyboard',
keyboard_placeholder: 'Ctrl+C',
abbreviation: 'Abbreviation',
prompt_abbreviation_meaning: 'What does the abbreviation stand for?',
abbreviation_placeholder: 'HTML',
text_color: 'Text Color',
colored_text_placeholder: 'colored text',
background_color: 'Background Color',
colored_background_placeholder: 'colored background'
},
'pt-BR': {
langName: 'Português (BR)',
languageSettings: '🌐 Idioma',
close: 'Fechar',
titles: 'Títulos',
title_placeholder: 'Título',
bold: 'Negrito',
bold_placeholder: 'negrito',
italic: 'Itálico',
italic_placeholder: 'itálico',
underline: 'Sublinhado',
underline_placeholder: 'sublinhado',
strikethrough: 'Riscado',
strikethrough_placeholder: 'riscado',
unordered_list: 'Lista não ordenada',
list_item_placeholder: 'Item',
ordered_list: 'Lista ordenada',
quote: 'Citação',
quote_placeholder: 'Citação',
inline_code: 'Código Inline',
inline_code_placeholder: 'código',
code_block: 'Bloco de Código',
code_block_placeholder: 'código aqui',
horizontal_line: 'Linha Horizontal',
link: 'Link',
prompt_insert_url: 'Insira a URL:',
link_text_placeholder: 'texto do link',
image: 'Imagem',
prompt_insert_image_url: 'Insira a URL da imagem (https):',
table: 'Tabela',
prompt_columns: 'Número de colunas:',
prompt_rows: 'Número de linhas:',
table_header_placeholder: 'Cabeçalho',
table_cell_placeholder: 'Célula',
video: 'Vídeo (YouTube/Bilibili)',
prompt_insert_video_url: 'Insira a URL do vídeo (YouTube ou Bilibili):',
alert_invalid_video_url: 'URL de vídeo inválida ou não suportada.',
subscript: 'Subscrito',
subscript_placeholder: 'sub',
superscript: 'Sobrescrito',
superscript_placeholder: 'sup',
highlight: 'Marcação',
highlight_placeholder: 'marcado',
keyboard: 'Teclado',
keyboard_placeholder: 'Ctrl+C',
abbreviation: 'Abreviação',
prompt_abbreviation_meaning: 'Qual o significado da abreviação?',
abbreviation_placeholder: 'HTML',
text_color: 'Cor do Texto',
colored_text_placeholder: 'texto colorido',
background_color: 'Cor de Fundo',
colored_background_placeholder: 'fundo colorido'
},
'es': {
langName: 'Español',
languageSettings: '🌐 Idioma',
close: 'Cerrar',
titles: 'Títulos',
title_placeholder: 'Título',
bold: 'Negrita',
bold_placeholder: 'texto en negrita',
italic: 'Cursiva',
italic_placeholder: 'texto en cursiva',
underline: 'Subrayado',
underline_placeholder: 'texto subrayado',
strikethrough: 'Tachado',
strikethrough_placeholder: 'texto tachado',
unordered_list: 'Lista sin ordenar',
list_item_placeholder: 'Elemento',
ordered_list: 'Lista ordenada',
quote: 'Cita',
quote_placeholder: 'Cita',
inline_code: 'Código en línea',
inline_code_placeholder: 'código',
code_block: 'Bloque de código',
code_block_placeholder: 'código aquí',
horizontal_line: 'Línea horizontal',
link: 'Enlace',
prompt_insert_url: 'Introduzca la URL:',
link_text_placeholder: 'texto del enlace',
image: 'Imagen',
prompt_insert_image_url: 'Introduzca la URL de la imagen (https):',
table: 'Tabla',
prompt_columns: 'Número de columnas:',
prompt_rows: 'Número de filas:',
table_header_placeholder: 'Encabezado',
table_cell_placeholder: 'Celda',
video: 'Video (YouTube/Bilibili)',
prompt_insert_video_url: 'Introduzca la URL del video (YouTube o Bilibili):',
alert_invalid_video_url: 'URL de video no válida o no compatible.',
subscript: 'Subíndice',
subscript_placeholder: 'sub',
superscript: 'Superíndice',
superscript_placeholder: 'sup',
highlight: 'Resaltado',
highlight_placeholder: 'resaltado',
keyboard: 'Teclado',
keyboard_placeholder: 'Ctrl+C',
abbreviation: 'Abreviatura',
prompt_abbreviation_meaning: '¿Qué significa la abreviatura?',
abbreviation_placeholder: 'HTML',
text_color: 'Color del texto',
colored_text_placeholder: 'texto coloreado',
background_color: 'Color de fondo',
colored_background_placeholder: 'fondo coloreado'
},
'zh-CN': {
langName: '简体中文',
languageSettings: '🌐 语言',
close: '关闭',
titles: '标题',
title_placeholder: '标题',
bold: '粗体',
bold_placeholder: '粗体文本',
italic: '斜体',
italic_placeholder: '斜体文本',
underline: '下划线',
underline_placeholder: '下划线文本',
strikethrough: '删除线',
strikethrough_placeholder: '删除线文本',
unordered_list: '无序列表',
list_item_placeholder: '项目',
ordered_list: '有序列表',
quote: '引用',
quote_placeholder: '引用',
inline_code: '行内代码',
inline_code_placeholder: '代码',
code_block: '代码块',
code_block_placeholder: '在此处编写代码',
horizontal_line: '水平线',
link: '链接',
prompt_insert_url: '请输入网址:',
link_text_placeholder: '链接文本',
image: '图片',
prompt_insert_image_url: '请输入图片网址 (https):',
table: '表格',
prompt_columns: '列数:',
prompt_rows: '行数:',
table_header_placeholder: '标题',
table_cell_placeholder: '单元格',
video: '视频 (YouTube/Bilibili)',
prompt_insert_video_url: '请输入视频网址 (YouTube or Bilibili):',
alert_invalid_video_url: '无效或不支持的视频网址。',
subscript: '下标',
subscript_placeholder: '下标',
superscript: '上标',
superscript_placeholder: '上标',
highlight: '标记',
highlight_placeholder: '标记',
keyboard: '键盘',
keyboard_placeholder: 'Ctrl+C',
abbreviation: '缩写',
prompt_abbreviation_meaning: '缩写的含义是什么?',
abbreviation_placeholder: 'HTML',
text_color: '文字颜色',
colored_text_placeholder: '彩色文本',
background_color: '背景颜色',
colored_background_placeholder: '彩色背景'
}
};
let currentLang = 'en';
let languageModal = null;
const LANG_STORAGE_KEY = 'UserScriptLang';
function getTranslation(key) {
return translations[currentLang]?.[key] || translations.en[key];
}
async function determineLanguage() {
const savedLang = await GM_getValue(LANG_STORAGE_KEY);
if (savedLang && translations[savedLang]) {
currentLang = savedLang;
return;
}
const browserLang = (navigator.language || navigator.userLanguage).toLowerCase();
if (browserLang.startsWith('pt')) currentLang = 'pt-BR';
else if (browserLang.startsWith('es')) currentLang = 'es';
else if (browserLang.startsWith('zh')) currentLang = 'zh-CN';
else currentLang = 'en';
}
function registerLanguageMenu() {
GM_registerMenuCommand(getTranslation('languageSettings'), () => {
showModal(languageModal);
});
}
const icons = {
h: `<svg viewBox="0 0 16 16"><path d="M3.75 2a.75.75 0 0 1 .75.75V7h7V2.75a.75.75 0 0 1 1.5 0v10.5a.75.75 0 0 1-1.5 0V8.5h-7v4.75a.75.75 0 0 1-1.5 0V2.75A.75.75 0 0 1 3.75 2Z"></path></svg>`,
bold: `<svg viewBox="0 0 16 16"><path d="M4 2h4.5a3.5 3.5 0 0 1 2.85 5.53A3.5 3.5 0 0 1 9.5 14H4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Zm1 7v3h4.5a1.5 1.5 0 0 0 0-3Zm3.5-2a1.5 1.5 0 0 0 0-3H5v3Z"></path></svg>`,
italic: `<svg viewBox="0 0 16 16"><path d="M6 2.75A.75.75 0 0 1 6.75 2h6.5a.75.75 0 0 1 0 1.5h-2.505l-3.858 9H9.25a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.505l3.858-9H6.75A.75.75 0 0 1 6 2.75Z"></path></svg>`,
underline: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M11 1h2v6.5c0 2.485-2.239 4.5-5 4.5S3 9.985 3 7.5V1h2v6.5c0 .628.285 1.23.802 1.695C6.379 9.714 7.159 10 8 10s1.621-.286 2.198-.805C10.715 8.729 11 8.127 11 7.5V1zM3 13h10v2H3z"/></svg>`,
strikethrough: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M952 474H569.9c-10-2-20.5-4-31.6-6c-15.9-2.9-22.2-4.1-30.8-5.8c-51.3-10-82.2-20-106.8-34.2c-35.1-20.5-52.2-48.3-52.2-85.1c0-37 15.2-67.7 44-89c28.4-21 68.8-32.1 116.8-32.1c54.8 0 97.1 14.4 125.8 42.8c14.6 14.4 25.3 32.1 31.8 52.6c1.3 4.1 2.8 10 4.3 17.8c.9 4.8 5.2 8.2 9.9 8.2h72.8c5.6 0 10.1-4.6 10.1-10.1v-1c-.7-6.8-1.3-12.1-2-16c-7.3-43.5-28-81.7-59.7-110.3c-44.4-40.5-109.7-61.8-188.7-61.8c-72.3 0-137.4 18.1-183.3 50.9c-25.6 18.4-45.4 41.2-58.6 67.7c-13.5 27.1-20.3 58.4-20.3 92.9c0 29.5 5.7 54.5 17.3 76.5c8.3 15.7 19.6 29.5 34.1 42H72c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h433.2c2.1.4 3.9.8 5.9 1.2c30.9 6.2 49.5 10.4 66.6 15.2c23 6.5 40.6 13.3 55.2 21.5c35.8 20.2 53.3 49.2 53.3 89c0 35.3-15.5 66.8-43.6 88.8c-30.5 23.9-75.6 36.4-130.5 36.4c-43.7 0-80.7-8.5-110.2-25c-29.1-16.3-49.1-39.8-59.7-69.5c-.8-2.2-1.7-5.2-2.7-9c-1.2-4.4-5.3-7.5-9.7-7.5h-79.7c-5.6 0-10.1 4.6-10.1 10.1v1c.2 2.3.4 4.2.6 5.7c6.5 48.8 30.3 88.8 70.7 118.8c47.1 34.8 113.4 53.2 191.8 53.2c84.2 0 154.8-19.8 204.2-57.3c25-18.9 44.2-42.2 57.1-69c13-27.1 19.7-57.9 19.7-91.5c0-31.8-5.8-58.4-17.8-81.4c-5.8-11.2-13.1-21.5-21.8-30.8H952c4.4 0 8-3.6 8-8v-60a8 8 0 0 0-8-7.9z"/></svg>`,
link: `<svg viewBox="0 0 16 16"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .75.75 0 0 1 1.06-1.06 2 2 0 0 0 2.83 0l2.5-2.5a2 2 0 0 0-2.83-2.83l-1.25 1.25a.75.75 0 0 1-1.06-1.06Zm-4.69 9.64a2 2 0 0 0 2.83 0l1.25-1.25a.75.75 0 0 1 1.06 1.06l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .75.75 0 0 1-1.06 1.06 2 2 0 0 0-2.83 0l-2.5 2.5a2 2 0 0 0 0 2.83Z"></path></svg>`,
quote: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 304 384"><path fill="currentColor" d="m21 299l43-86H0V85h128v128l-43 86H21zm171 0l43-86h-64V85h128v128l-43 86h-64z"/></svg>`,
code: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="m.586 12l4.95-4.95L6.95 8.464L3.414 12l3.536 3.536l-1.414 1.414L.586 12Zm8.201 8.728l4.486-17.94l1.94.485l-4.485 17.94l-1.94-.485Zm8.263-5.192L20.586 12L17.05 8.464l1.415-1.414l4.95 4.95l-4.95 4.95l-1.415-1.414Z"/></svg>`,
code_block: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path id="oouiCode0" fill="currentColor" d="M1 10.08V8.92h1.15c1.15 0 1.15 0 1.15-1.15V5a7.42 7.42 0 0 1 .09-1.3a2 2 0 0 1 .3-.7a1.84 1.84 0 0 1 .93-.68A6.44 6.44 0 0 1 6.74 2h1.18v1.15h-.86A1.32 1.32 0 0 0 6 3.62a1.71 1.71 0 0 0-.36 1.23V7a3.22 3.22 0 0 1-.28 1.72a2 2 0 0 1-1.26.77a2.15 2.15 0 0 1 1.26.79A3.26 3.26 0 0 1 5.62 12v3.15A1.67 1.67 0 0 0 6 16.37a1.31 1.31 0 0 0 1.08.47h.87V18H6.74a6.3 6.3 0 0 1-2.12-.29a1.82 1.82 0 0 1-.93-.71a1.94 1.94 0 0 1-.3-.72A7.46 7.46 0 0 1 3.31 15v-3.77c0-1.15 0-1.15-1.15-1.15zm18 0V8.92h-1.15c-1.15 0-1.15 0-1.15-1.15V5a7.42 7.42 0 0 0-.08-1.32a2 2 0 0 0-.3-.73a1.84 1.84 0 0 0-.93-.68A6.44 6.44 0 0 0 13.26 2h-1.18v1.15h.87a1.32 1.32 0 0 1 1.05.47a1.71 1.71 0 0 1 .36 1.23V7a3.22 3.22 0 0 0 .28 1.72a2 2 0 0 0 1.26.77a2.15 2.15 0 0 0-1.26.79a3.26 3.26 0 0 0-.26 1.72v3.15a1.67 1.67 0 0 1-.38 1.22a1.31 1.31 0 0 1-1.08.47h-.87V18h1.19a6.3 6.3 0 0 0 2.12-.29a1.82 1.82 0 0 0 .93-.68a1.94 1.94 0 0 0 .3-.72a7.46 7.46 0 0 0 .1-1.31v-3.77c0-1.15 0-1.15 1.15-1.15z"/><use href="#oouiCode0" transform="matrix(-1 0 0 1 20 0)"/></svg>`,
ul: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1025 1024"><path fill="currentColor" d="M960.356 1024h-512q-27 0-45.5-19t-18.5-45V832q0-26 18.5-45t45.5-19h512q26 0 45 19t19 45v128q0 27-19 45.5t-45 18.5zm0-384h-512q-27 0-45.5-18.5t-18.5-45.5V448q0-27 18.5-45.5t45.5-18.5h512q26 0 45 18.5t19 45.5v128q0 27-19 45.5t-45 18.5zm0-384h-512q-27 0-45.5-19t-18.5-45V64q0-27 18.5-45.5t45.5-18.5h512q26 0 45 18.5t19 45.5v128q0 26-19 45t-45 19zm-768 768h-128q-26 0-45-19t-19-45V832q0-26 19-45t45-19h128q26 0 45 19t19 45v128q0 27-18.5 45.5t-45.5 18.5zm0-384h-128q-26 0-45-18.5t-19-45.5V448q0-27 19-45.5t45-18.5h128q26 0 45 18.5t19 45.5v128q0 27-18.5 45.5t-45.5 18.5zm0-384h-128q-26 0-45-19t-19-45V64q0-27 19-45.5t45-18.5h128q26 0 45 18.5t19 45.5v128q0 26-18.5 45t-45.5 19z"/></svg>`,
ol: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M3.684 1.01c.193.045.33.21.33.402v3.294a.42.42 0 0 1-.428.412a.42.42 0 0 1-.428-.412V2.58a3.11 3.11 0 0 1-.664.435a.436.436 0 0 1-.574-.184a.405.405 0 0 1 .192-.552c.353-.17.629-.432.82-.661a2.884 2.884 0 0 0 .27-.388a.44.44 0 0 1 .482-.22Zm-1.53 6.046a.401.401 0 0 1 0-.582l.002-.001V6.47l.004-.002l.008-.008a1.12 1.12 0 0 1 .103-.084a2.2 2.2 0 0 1 1.313-.435h.007c.32.004.668.084.947.283c.295.21.485.536.485.951c0 .452-.207.767-.488.992c-.214.173-.49.303-.714.409c-.036.016-.07.033-.103.049c-.267.128-.468.24-.61.39a.763.763 0 0 0-.147.22h1.635a.42.42 0 0 1 .427.411a.42.42 0 0 1-.428.412H2.457a.42.42 0 0 1-.428-.412c0-.51.17-.893.446-1.184c.259-.275.592-.445.86-.574c.043-.02.085-.04.124-.06c.231-.11.4-.19.529-.293c.12-.097.18-.193.18-.36c0-.148-.057-.23-.14-.289a.816.816 0 0 0-.448-.122a1.32 1.32 0 0 0-.818.289l-.005.005a.44.44 0 0 1-.602-.003Zm.94 5.885a.42.42 0 0 1 .427-.412c.294 0 .456-.08.537-.15a.303.303 0 0 0 .11-.246c-.006-.16-.158-.427-.647-.427c-.352 0-.535.084-.618.137a.349.349 0 0 0-.076.062l-.003.004a.435.435 0 0 0 .01-.018v.001l-.002.002l-.002.004l-.003.006l-.005.008l.002-.003a.436.436 0 0 1-.563.165a.405.405 0 0 1-.191-.552v-.002l.002-.003l.003-.006l.008-.013a.71.71 0 0 1 .087-.12c.058-.067.142-.146.259-.22c.238-.153.59-.276 1.092-.276c.88 0 1.477.556 1.502 1.22c.012.303-.1.606-.339.84c.238.232.351.535.34.838c-.026.664-.622 1.22-1.503 1.22c-.502 0-.854-.122-1.092-.275a1.19 1.19 0 0 1-.326-.308a.71.71 0 0 1-.02-.033l-.008-.013l-.003-.005l-.001-.003v-.001l-.001-.001a.405.405 0 0 1 .19-.553a.436.436 0 0 1 .564.165l.003.004c.01.01.033.035.076.063c.083.053.266.137.618.137c.489 0 .641-.268.648-.428a.303.303 0 0 0-.11-.245c-.082-.072-.244-.151-.538-.151a.42.42 0 0 1-.427-.412ZM7.75 3a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Zm0 4a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Zm0 4a.75.75 0 0 0 0 1.5h5.5a.75.75 0 0 0 0-1.5h-5.5Z"/></svg>`,
image: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42"><path fill="currentColor" d="M.5 7.5v27c0 2.52.51 3 3 3h34c2.471 0 3-.46 3-3v-27c0-2.46-.471-3-3-3h-34c-2.48 0-3 .43-3 3zm35.29 23H5.23c3.34-4.87 9.279-12.99 10.789-12.99c1.461 0 6.42 6.561 8.661 8.87c0 0 2.881-3.851 4.391-3.851c1.538 0 6.669 7.931 6.719 7.971zm-8.979-17c0-2.04 1.649-3.689 3.689-3.689s3.689 1.649 3.689 3.689s-1.649 3.689-3.689 3.689s-3.689-1.649-3.689-3.689z"/></svg>`,
video: `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 1024 768"><path fill="currentColor" d="M928 736q-222 32-416 32q-86 0-190-8t-165-16l-61-8q-27-5-47.5-37.5t-30-78.5t-14-86T0 461V307Q0 52 96 32Q318 0 512 0q86 0 190 8t165 16l61 8q29 4 49.5 36.5T1007 148t13 86t4 73v154q0 36-3 73t-12 85t-30 80t-51 37zM693 359L431 199q-11-10-29-5.5T384 208v352q0 11 18 15t29-6l262-160q11-10 11-25t-11-25z"/></svg>`,
subscript: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 64C14.3 64 0 78.3 0 96s14.3 32 32 32h15.3l89.6 128l-89.6 128H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h32c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6h32c17.7 0 32-14.3 32-32s-14.3-32-32-32h-15.3l-89.6-128l89.6-128H320c17.7 0 32-14.3 32-32s-14.3-32-32-32h-32c-10.4 0-20.2 5.1-26.2 13.6L176 200.2L90.2 77.6C84.2 69.1 74.4 64 64 64H32zm448 256c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 361.5 404.3 368 416 368v80c-17.7 0-32 14.3-32 32s14.3 32 32 32h64c17.7 0 32-14.3 32-32s-14.3-32-32-32V320z"/></svg>`,
superscript: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M480 32c0-11.1-5.7-21.4-15.2-27.2s-21.2-6.4-31.1-1.4l-32 16c-15.8 7.9-22.2 27.1-14.3 42.9C393 73.5 404.3 80 416 80v80c-17.7 0-32 14.3-32 32s14.3 32 32 32h64c17.7 0 32-14.3 32-32s-14.3-32-32-32V32zM32 64C14.3 64 0 78.3 0 96s14.3 32 32 32h15.3l89.6 128l-89.6 128H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h32c10.4 0 20.2-5.1 26.2-13.6L176 311.8l85.8 122.6c6 8.6 15.8 13.6 26.2 13.6h32c17.7 0 32-14.3 32-32s-14.3-32-32-32h-15.3l-89.6-128l89.6-128H320c17.7 0 32-14.3 32-32s-14.3-32-32-32h-32c-10.4 0-20.2 5.1-26.2 13.6L176 200.2L90.2 77.6C84.2 69.1 74.4 64 64 64H32z"/></svg>`,
highlight: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="currentColor" d="M16.5 3v4a.5.5 0 0 1-.5.5H4a.5.5 0 0 1-.5-.5V3h13Zm-10 7.5v7l6.447-3.106a1 1 0 0 0 .553-.894v-3h-7Z"/></svg>`,
abbreviation: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M20 424.229h20V279.771H20c-11.046 0-20-8.954-20-20V212c0-11.046 8.954-20 20-20h112c11.046 0 20 8.954 20 20v212.229h20c11.046 0 20 8.954 20 20V492c0 11.046-8.954 20-20 20H20c-11.046 0-20-8.954-20-20v-47.771c0-11.046 8.954-20 20-20zM96 0C56.235 0 24 32.235 24 72s32.235 72 72 72s72-32.235 72-72S135.764 0 96 0z"/></svg>`,
keyboard: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M3 4h10a1.5 1.5 0 0 1 1.5 1.5v5A1.5 1.5 0 0 1 13 12H3a1.5 1.5 0 0 1-1.5-1.5v-5A1.5 1.5 0 0 1 3 4M0 5.5a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v5a3 3 0 0 1-3 3H3a3 3 0 0 1-3-3zm6.25 3.25a.75.75 0 0 0 0 1.5h3.5a.75.75 0 0 0 0-1.5zM4.5 6.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0m2 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2m4-1a1 1 0 1 1-2 0a1 1 0 0 1 2 0m2 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2m-8 2a1 1 0 1 1-2 0a1 1 0 0 1 2 0m8 1a1 1 0 1 0 0-2a1 1 0 0 0 0 2" clip-rule="evenodd"/></svg>`,
text_color: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><path fill="currentColor" d="M12 1v2a1 1 0 0 1-2 0V2H7v8h1a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2h1V2H2v1a1 1 0 1 1-2 0V1a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1Z"/></svg>`,
background_color: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M4 20q-.825 0-1.413-.588T2 18V6q0-.825.588-1.413T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.588 1.413T20 20H4Zm2-3h12q.425 0 .713-.288T19 16q0-.425-.288-.713T18 15H6q-.425 0-.713.288T5 16q0 .425.288.713T6 17Zm0-4h12q.425 0 .713-.288T19 12q0-.425-.288-.713T18 11H6q-.425 0-.713.288T5 12q0 .425.288.713T6 13Zm0-4h8q.425 0 .713-.288T15 8q0-.425-.288-.713T14 7H6q-.425 0-.713.288T5 8q0 .425.288.713T6 9Z"/></svg>`,
table: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="currentColor" d="M2 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 4h7v4H2zm0 10v-4h7v4zm16 0h-7v-4h7zm0-6h-7V6h7z"/></svg>`,
hr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" d="M2 7.75A.75.75 0 0 1 2.75 7h10.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 7.75Z"/></svg>`,
};
GM_addStyle(`
.txt-editor-container.dark-theme {
background-color: #0d1117; border: 1px solid #3d444d;
}
.dark-theme .txt-editor-toolbar {
background-color: #151b23f2; border-bottom: 1px solid #3d444d;
}
.dark-theme .txt-editor-toolbar-button, .dark-theme .txt-editor-toolbar-select {
color: #9198a1;
}
.dark-theme .txt-editor-toolbar-button:hover, .dark-theme .txt-editor-toolbar-select:hover {
background-color: #212830; color: #fcf0f0ff;
}
.dark-theme .txt-editor-toolbar-button[data-tooltip]:hover::after {
background-color: #212830; color: #fcf0f7ff;
}
.dark-theme textarea {
background-color: #0d1117; color: #f0f6fc; border-top: 1px solid #3d444d;
}
.dark-theme .txt-editor-toolbar-divider {
border-left: 1px solid #3d444d;
}
.dark-theme .txt-editor-toolbar-select {
background-color: #151b23; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%239198a1" viewBox="0 0 16 16"><path d="M4.427 7.427a.25.25 0 0 0-.354.354l4 4a.25.25 0 0 0 .354 0l4-4a.25.25 0 0 0-.354-.354L8.25 11.22l-3.823-3.793Z"></path></svg>');
}
.dark-theme .txt-editor-toolbar-select option {
background: #0d1117; color: #f0f6fc;
}
.dark-theme .txt-color-picker-input {
border: 1px solid #3d444d; background-color: #0d1117;
}
.txt-editor-container.light-theme {
background-color: #ffffff; border: 1px solid #d0d7de;
}
.light-theme .txt-editor-toolbar {
background-color: #f6f8fa; border-bottom: 1px solid #d0d7de;
}
.light-theme .txt-editor-toolbar-button, .light-theme .txt-editor-toolbar-select {
color: #57606a;
}
.light-theme .txt-editor-toolbar-button:hover, .light-theme .txt-editor-toolbar-select:hover {
background-color: #ebecf0; color: #24292f;
}
.light-theme .txt-editor-toolbar-button[data-tooltip]:hover::after {
background-color: #24292f; color: #ffffff;
}
.light-theme textarea {
background-color: #ffffff; color: #24292f; border-top: 1px solid #d0d7de;
}
.light-theme .txt-editor-toolbar-divider {
border-left: 1px solid #d0d7de;
}
.light-theme .txt-editor-toolbar-select {
background-color: #f6f8fa; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="%2357606a" viewBox="0 0 16 16"><path d="M4.427 7.427a.25.25 0 0 0-.354.354l4 4a.25.25 0 0 0 .354 0l4-4a.25.25 0 0 0-.354-.354L8.25 11.22l-3.823-3.793Z"></path></svg>');
}
.light-theme .txt-editor-toolbar-select option {
background: #ffffff; color: #24292f;
}
.light-theme .txt-color-picker-input {
border: 1px solid #d0d7de; background-color: #ffffff;
}
.txt-editor-container {
border-radius: 6px; margin: 10px 0;
}
.txt-editor-container textarea {
border: 0; border-radius: 0 0 6px 6px; width: 100% !important; min-height: 180px; padding: 10px; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; resize: vertical;
}
.txt-editor-toolbar {
display: flex; flex-wrap: wrap; align-items: center; padding: 8px 5px;
}
.txt-editor-toolbar-button, .txt-editor-toolbar-select {
background: none; border: none; cursor: pointer; padding: 6px; margin: 0 2px; border-radius: 6px; position: relative;
}
.txt-editor-toolbar-button svg {
width: 16px; height: 16px; fill: currentColor; vertical-align: middle;
}
.txt-editor-toolbar-button[data-tooltip]:hover::after {
content: attr(data-tooltip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); padding: 4px 8px; border-radius: 4px; font-size: 12px; white-space: nowrap; z-index: 10; margin-bottom: 5px;
}
.txt-editor-toolbar-divider {
margin: 4px 8px; height: 16px;
}
.txt-editor-toolbar-select {
-webkit-appearance: none; appearance: none; padding-right: 20px; background-repeat: no-repeat; background-position: right 6px center;
}
.txt-color-picker-container {
display: flex; align-items: center;
}
.txt-color-picker-input {
border-radius: 4px; width: 24px; height: 24px; padding: 1px; cursor: pointer;
}
.lang-modal-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: none; z-index: 2147483647; justify-content: center; align-items: center; backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
}
.lang-modal-box {
padding: 24px; border-radius: 16px; width: min(90vw, 320px); text-align: center; transform: scale(0.95); opacity: 0; transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s ease-out;
}
.lang-modal-box.dark-theme {
background-color: rgba(30, 30, 32, 0.85); border: 1px solid #333; box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}
.lang-modal-box.light-theme {
background-color: rgba(255, 255, 255, 0.8); border: 1px solid rgba(0, 0, 0, 0.1); box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.lang-modal-buttons {
display: flex; flex-direction: column; gap: 12px;
}
.lang-modal-buttons button {
padding: 14px; border-radius: 10px; font-weight: 500; cursor: pointer; transition: all 0.2s ease-in-out; text-align: center; font-size: 1rem;
}
.lang-modal-box.dark-theme .lang-modal-buttons button {
background-color: #2a2d31; color: #e0e0e0; border: 1px solid #444;
}
.lang-modal-box.light-theme .lang-modal-buttons button {
background-color: #f0f2f5; color: #333; border: 1px solid #ddd;
}
.lang-modal-box.dark-theme .lang-modal-buttons button:hover {
border-color: #58a6ff; background-color: #313438;
}
.lang-modal-box.light-theme .lang-modal-buttons button:hover {
border-color: #0969da; background-color: #e6e8eb;
}
`);
function showModal(modal) {
if (!modal) return;
modal.style.display = 'flex';
setTimeout(() => {
const box = modal.querySelector('.lang-modal-box');
box.style.opacity = '1';
box.style.transform = 'scale(1)';
}, 10);
}
function hideModal(modal) {
if (!modal) return;
const box = modal.querySelector('.lang-modal-box');
box.style.opacity = '0';
box.style.transform = 'scale(0.95)';
setTimeout(() => {
modal.style.display = 'none';
}, 200);
}
function createLanguageModal() {
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
const overlay = document.createElement('div');
overlay.className = 'lang-modal-overlay';
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
hideModal(overlay);
}
});
const box = document.createElement('div');
box.className = `lang-modal-box ${isDark ? 'dark-theme' : 'light-theme'}`;
const buttonsContainer = document.createElement('div');
buttonsContainer.className = 'lang-modal-buttons';
Object.keys(translations).forEach(langKey => {
const btn = document.createElement('button');
btn.textContent = translations[langKey].langName;
btn.onclick = async () => {
await GM_setValue(LANG_STORAGE_KEY, langKey);
window.location.reload();
};
buttonsContainer.appendChild(btn);
});
box.appendChild(buttonsContainer);
overlay.appendChild(box);
return overlay;
}
function insertText(textarea, prefix, suffix = '', placeholder = '') {
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
const selected = textarea.value.substring(start, end);
const text = selected || placeholder;
textarea.setRangeText(prefix + text + suffix, start, end, selected ? 'end' : 'select');
textarea.focus();
}
function createToolbarButton(def) {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'txt-editor-toolbar-button';
btn.dataset.tooltip = def.title;
btn.innerHTML = def.icon || def.label;
btn.addEventListener('click', e => {
e.preventDefault();
def.action();
});
return btn;
}
function createTextStyleEditor(textarea) {
if (textarea.dataset.editorApplied) return;
textarea.dataset.editorApplied = 'true';
const container = document.createElement('div');
container.className = 'txt-editor-container';
const toolbar = document.createElement('div');
toolbar.className = 'txt-editor-toolbar';
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
function applyTheme(isDark) {
container.classList.toggle('dark-theme', isDark);
container.classList.toggle('light-theme', !isDark);
}
applyTheme(mediaQuery.matches);
mediaQuery.addEventListener('change', e => applyTheme(e.matches));
const tools = [{
type: 'select',
title: getTranslation('titles'),
options: {
'H': '### ',
'H1': '# ',
'H2': '## ',
'H3': '### ',
'H4': '#### ',
'H5': '##### ',
'H6': '###### '
},
action: (val) => insertText(textarea, val, '', getTranslation('title_placeholder'))
},
{
type: 'divider'
},
{
title: getTranslation('bold'),
icon: icons.bold,
action: () => insertText(textarea, '**', '**', getTranslation('bold_placeholder'))
},
{
title: getTranslation('italic'),
icon: icons.italic,
action: () => insertText(textarea, '*', '*', getTranslation('italic_placeholder'))
},
{
title: getTranslation('underline'),
icon: icons.underline,
action: () => insertText(textarea, '<u>', '</u>', getTranslation('underline_placeholder'))
},
{
title: getTranslation('strikethrough'),
icon: icons.strikethrough,
action: () => insertText(textarea, '~~', '~~', getTranslation('strikethrough_placeholder'))
},
{
type: 'divider'
},
{
title: getTranslation('unordered_list'),
icon: icons.ul,
action: () => {
const start = textarea.selectionStart,
end = textarea.selectionEnd,
selection = textarea.value.substring(start, end);
textarea.setRangeText(selection ? selection.split('\n').map(line => line.trim() === '' ? '' : '- ' + line).join('\n') : '\n- ' + getTranslation('list_item_placeholder'), start, end, 'select');
textarea.focus();
}
},
{
title: getTranslation('ordered_list'),
icon: icons.ol,
action: () => {
const start = textarea.selectionStart,
end = textarea.selectionEnd,
selection = textarea.value.substring(start, end);
if (selection) {
let counter = 1;
textarea.setRangeText(selection.split('\n').map(line => line.trim() === '' ? '' : (counter++) + '. ' + line).join('\n'), start, end, 'select');
} else insertText(textarea, '\n1. ', '', getTranslation('list_item_placeholder'));
textarea.focus();
}
},
{
type: 'divider'
},
{
title: getTranslation('quote'),
icon: icons.quote,
action: () => insertText(textarea, '\n> ', '', getTranslation('quote_placeholder'))
},
{
title: getTranslation('inline_code'),
icon: icons.code,
action: () => insertText(textarea, '`', '`', getTranslation('inline_code_placeholder'))
},
{
title: getTranslation('code_block'),
label: icons.code_block,
action: () => insertText(textarea, '\n```\n', '\n```\n', getTranslation('code_block_placeholder'))
},
{
title: getTranslation('horizontal_line'),
icon: icons.hr,
action: () => insertText(textarea, '\n---\n')
},
{
type: 'divider'
},
{
title: getTranslation('link'),
icon: icons.link,
action: () => {
const url = prompt(getTranslation('prompt_insert_url'), "https://");
if (url) insertText(textarea, '[', `](${url})`, getTranslation('link_text_placeholder'));
}
},
{
title: getTranslation('image'),
icon: icons.image,
action: () => {
const url = prompt(getTranslation('prompt_insert_image_url'), "https://");
if (url) insertText(textarea, ``);
}
},
{
title: getTranslation('table'),
icon: icons.table,
action: () => {
const cols = parseInt(prompt(getTranslation('prompt_columns'), "3"), 10) || 3;
const rows = parseInt(prompt(getTranslation('prompt_rows'), "2"), 10) || 2;
let table = '\n| ' + Array(cols).fill(getTranslation('table_header_placeholder')).join(' | ') + ' |\n';
table += '| ' + Array(cols).fill('---').join(' | ') + ' |\n';
for (let i = 0; i < rows; i++) {
table += '| ' + Array(cols).fill(getTranslation('table_cell_placeholder')).join(' | ') + ' |\n';
}
insertText(textarea, table);
}
},
{
title: getTranslation('video'),
icon: icons.video,
action: () => {
const url = prompt(getTranslation('prompt_insert_video_url'));
if (!url) return;
let src = '';
if (url.includes('youtube.com/watch?v=')) src = `https://www.youtube.com/embed/${new URL(url).searchParams.get('v')}`;
else if (url.includes('youtu.be/')) src = `https://www.youtube.com/embed/${new URL(url).pathname.substring(1)}`;
else if (url.includes('bilibili.com/video/')) src = `https://player.bilibili.com/player.html?bvid=${new URL(url).pathname.split('/')[2]}`;
if (src) insertText(textarea, `<iframe src="${src}" allowfullscreen></iframe>`);
else alert(getTranslation('alert_invalid_video_url'));
}
},
{
type: 'divider'
},
{
title: getTranslation('subscript'),
label: icons.subscript,
action: () => insertText(textarea, '<sub>', '</sub>', getTranslation('subscript_placeholder'))
},
{
title: getTranslation('superscript'),
label: icons.superscript,
action: () => insertText(textarea, '<sup>', '</sup>', getTranslation('superscript_placeholder'))
},
{
title: getTranslation('highlight'),
label: icons.highlight,
action: () => insertText(textarea, '<mark>', '</mark>', getTranslation('highlight_placeholder'))
},
{
title: getTranslation('keyboard'),
label: icons.keyboard,
action: () => insertText(textarea, '<kbd>', '</kbd>', getTranslation('keyboard_placeholder'))
},
{
title: getTranslation('abbreviation'),
label: icons.abbreviation,
action: () => {
const title = prompt(getTranslation('prompt_abbreviation_meaning'));
if (title) insertText(textarea, `<abbr title="${title}">`, `</abbr>`, getTranslation('abbreviation_placeholder'));
}
},
{
type: 'color-picker'
}
];
tools.forEach(tool => {
if (tool.type === 'divider') {
const div = document.createElement('div');
div.className = 'txt-editor-toolbar-divider';
toolbar.appendChild(div);
} else if (tool.type === 'select') {
const select = document.createElement('select');
select.className = 'txt-editor-toolbar-select';
select.dataset.tooltip = tool.title;
Object.keys(tool.options).forEach(key => {
const opt = document.createElement('option');
opt.value = tool.options[key];
opt.textContent = key;
if (key === 'H') {
opt.disabled = true;
opt.selected = true;
}
select.appendChild(opt);
});
select.addEventListener('change', () => {
if (select.value) tool.action(select.value);
select.selectedIndex = 0;
});
toolbar.appendChild(select);
} else if (tool.type === 'color-picker') {
const colorContainer = document.createElement('div');
colorContainer.className = 'txt-color-picker-container';
const input = document.createElement('input');
input.type = 'color';
input.className = 'txt-color-picker-input';
input.value = "#58a6ff";
const colorBtn = createToolbarButton({
title: getTranslation('text_color'),
label: icons.text_color,
action: () => insertText(textarea, `<span style="color: ${input.value};">`, '</span>', getTranslation('colored_text_placeholder'))
});
const bgBtn = createToolbarButton({
title: getTranslation('background_color'),
label: icons.background_color,
action: () => insertText(textarea, `<span style="background-color: ${input.value};">`, '</span>', getTranslation('colored_background_placeholder'))
});
colorContainer.append(input, colorBtn, bgBtn);
toolbar.appendChild(colorContainer);
} else {
toolbar.appendChild(createToolbarButton(tool));
}
});
textarea.parentNode.insertBefore(container, textarea);
container.append(toolbar, textarea);
}
function applyToAllTextareas() {
const textareas = document.querySelectorAll('textarea:not(#script_version_code):not([data-editor-applied])');
textareas.forEach(createTextStyleEditor);
}
function enableSourceEditorCheckbox() {
const enableCheckbox = () => {
const checkbox = document.getElementById('enable-source-editor-code');
if (checkbox && !checkbox.checked) {
checkbox.checked = true;
const event = new Event('change', { bubbles: true });
checkbox.dispatchEvent(event);
}
};
enableCheckbox();
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
const checkbox = document.getElementById('enable-source-editor-code');
if (checkbox) {
enableCheckbox();
observer.disconnect();
break;
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
async function start() {
await determineLanguage();
languageModal = createLanguageModal();
document.body.appendChild(languageModal);
registerLanguageMenu();
applyToAllTextareas();
enableSourceEditorCheckbox();
new MutationObserver(applyToAllTextareas).observe(document.body, {
childList: true,
subtree: true
});
}
start();
})();