Automatically detects Arabic/Persian text in ChatGPT, Gemini, and Grok and applies Right-to-Left (RTL) formatting.
// ==UserScript==
// @name Auto RTL for AI Chatbots (Arabic & Persian)
// @namespace http://tampermonkey.net/
// @version 1.1
// @description Automatically detects Arabic/Persian text in ChatGPT, Gemini, and Grok and applies Right-to-Left (RTL) formatting.
// @author AmirRezaTamandani
// @match https://chatgpt.com/*
// @match https://gemini.google.com/*
// @match https://x.com/i/grok*
// @match https://chat.deepseek.com/*
// @match https://claude.ai/*
// @match https://chat.z.ai/*
// @match https://gapgpt.app/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// Regular Expression to detect Arabic, Persian, and Urdu character ranges
const isRTL = (text) =>
/[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(
text,
);
// Function to check and apply RTL styling
const processNode = (node) => {
if (!node || node.nodeType !== Node.ELEMENT_NODE) return;
// Target paragraphs, divs, spans, and list items that hold text
const textElements = node.querySelectorAll
? node.querySelectorAll("p, div, span, li")
: [];
textElements.forEach((el) => {
// Ignore code blocks (we want code to stay LTR) and elements already set to RTL
if (el.closest("pre, code") || el.getAttribute("dir") === "rtl") return;
const text = el.innerText || el.textContent;
// If the text contains Arabic/Persian characters, flip it
if (text && isRTL(text)) {
el.setAttribute("dir", "rtl");
el.style.textAlign = "right";
el.style.direction = "rtl";
// Optional: Set a readable font for Arabic/Persian (uncomment the line below if you want)
// el.style.fontFamily = 'Vazirmatn, Tahoma, Arial, sans-serif';
}
});
};
// Run once on initial page load (with a slight delay for modern web apps)
setTimeout(() => {
processNode(document.body);
}, 2000);
// Set up a MutationObserver to watch for new chat messages being generated
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
processNode(node);
});
});
});
// Start observing the page for changes
observer.observe(document.body, { childList: true, subtree: true });
})();