您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Dynamically fixes RTL/LTR direction, justifies Persian text, and allows font switching on AI chatbots.
// ==UserScript== // @name AI Chat Font & Direction Fixer // @name:fa اصلاح فونت و جهتدهی چتباتها // @namespace https://github.com/gemini-user/ai-chat-styler // @version 4.1 // @description Dynamically fixes RTL/LTR direction, justifies Persian text, and allows font switching on AI chatbots. // @description:fa اصلاح پویای جهت نوشتار، ترازبندی متن فارسی و امکان انتخاب فونت در چتباتهای هوش مصنوعی. // @author Your Assistant // @match https://chatgpt.com/* // @match https://gemini.google.com/* // @match https://chat.qwen.ai/* // @match https://grok.com/* // @match https://chat.deepseek.com/* // @match https://copilot.chat.github.com/* // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @run-at document-body // ==/UserScript== (function() { 'use strict'; // --- بخش اول: تنظیمات فونت و منو --- const fonts = { vazirmatn: { menuName: 'انتخاب فونت: وزیرمتن', cssName: "'Vazirmatn'", cdnUrl: 'https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/Vazirmatn-font-face.css' }, vazirCode: { menuName: 'انتخاب فونت: وزیر کد', cssName: "'Vazir Code'", cdnUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/dist/font-face.css' }, iranSans: { menuName: 'انتخاب فونت: ایران سنس', cssName: "'IRANSans-medium'", cdnUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/iransans/font.css' } }; const selectedFontKey = GM_getValue('selectedFont', 'vazirmatn'); const selectedFont = fonts[selectedFontKey]; Object.keys(fonts).forEach(key => { GM_registerMenuCommand(fonts[key].menuName, () => { GM_setValue('selectedFont', key); window.location.reload(); }); }); // --- بخش دوم: استایلهای پایه (فونت و کد) --- const css = ` @import url('${fonts.vazirmatn.cdnUrl}'); @import url('${fonts.vazirCode.cdnUrl}'); @import url('${fonts.iranSans.cdnUrl}'); /* اعمال فونت انتخاب شده به تمام بخشهای متنی */ body, button, input, textarea, div, p, span, li, h1, h2, h3, h4, h5, h6, a, label, strong, td, th { font-family: ${selectedFont.cssName}, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important; } /* استایل ثابت برای بلوکهای کد */ pre, code, kbd, samp, pre *, code * { font-family: 'Consolas', 'Menlo', 'monospace' !important; direction: ltr !important; text-align: left !important; } `; GM_addStyle(css); // --- بخش سوم: منطق هوشمند جاوااسکریپت برای اصلاح جهتدهی و ترازبندی --- const persianRegex = /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF]/; function fixDirection(element) { // از دستکاری بلوکهای کد و منوهای قابل ویرایش خودداری کن if (!element || element.nodeType !== 1 || element.isContentEditable || element.closest('pre, code, nav, [role="navigation"], [role="menu"]')) { return; } // فقط عناصر متنی اصلی را بررسی کن if (element.matches('p, li, h1, h2, h3, h4, h5, h6, blockquote, td, th, div')) { // بررسی کن آیا خود عنصر یا فرزندانش متن فارسی دارند const hasPersian = persianRegex.test(element.textContent); if (hasPersian) { element.style.direction = 'rtl'; element.style.textAlign = 'justify'; // <--- تغییر اصلی: ترازبندی دوطرفه برای متن فارسی } else { // اگر هیچ متن فارسی در عنصر و فرزندانش نبود، به حالت پیشفرض برگردان if (!element.querySelector('[style*="direction: rtl"]')) { element.style.direction = 'ltr'; element.style.textAlign = 'left'; } } } } // مشاهدهگر برای شناسایی تغییرات زنده در صفحه (مثل پاسخهای جدید چتبات) const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === 1) { // اگر یک عنصر HTML بود fixDirection(node); // خود عنصر را بررسی کن node.querySelectorAll('p, li, h1, h2, h3, h4, div').forEach(fixDirection); // فرزندانش را هم بررسی کن } }); }); }); // مشاهدهگر را روی کل بدنه صفحه فعال کن observer.observe(document.body, { childList: true, subtree: true }); // یک اسکن اولیه برای محتوایی که از قبل در صفحه وجود دارد document.querySelectorAll('p, li, h1, h2, h3, h4, div').forEach(fixDirection); })();