🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

Greasy Fork is available in English.

📂 缓存分发状态(共享加速已生效)
🕒 页面同步时间:2026/01/10 07:40:27
🔄 下次更新时间:2026/01/10 08:40:27
手动刷新缓存

Janitor AI - Automatic Message Formatting Corrector (Settings Menu)

The "One-Click" cleanup script. Floating, Inline Top, or Inline Bottom. Select Italics/Bold/Plain text. Edge compatible.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         Janitor AI - Automatic Message Formatting Corrector (Settings Menu)
// @namespace    http://tampermonkey.net/
// @version      10.7
// @description  The "One-Click" cleanup script. Floating, Inline Top, or Inline Bottom. Select Italics/Bold/Plain text. Edge compatible.
// @author       accforfaciet
// @match        *://janitorai.com/chats/*
// @grant        GM_addStyle
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- CONSTANTS & DEFAULTS ---
    const DEBUG_MODE = false;
    const POSITION_KEY = 'janitorFormatterPosition';
    const SETTINGS_KEY = 'janitorFormatterSettings';

    const DEFAULT_SETTINGS = {
        // Formatting
        styleMode: 'italic',
        removeThinkTags: true,
        removeSystemPrompt: true,
        removeGeneralTags: true,
        removeEmDashes: false, // New: Disabled by default
        // UI
        buttonPosition: 'floating', // 'floating', 'inline-top', 'inline-bottom'
        floatSize: 50,
        floatOpacity: 50,
        showButtonText: true,
        showSettingsText: true
    };

    // --- SELECTORS ---
    const EDIT_BUTTON_SELECTOR = 'button[title="Edit Message"], button[aria-label="Edit"]';
    const TEXT_AREA_SELECTOR = 'textarea[class*="_autoResizeTextarea"], textarea[placeholder^="Type a message"], textarea[style*="font-size: 16px"]';
    const CONFIRM_BUTTON_SELECTOR = 'button[aria-label="Confirm"], button[aria-label="Save"], button[aria-label*="Confirm"], button[aria-label*="Save"]';

    // UI Selectors
    const MSG_CONTROLS_SELECTOR = '[class*="messageControls"], [class*="_messageControls_"]'; // Top
    const MSG_NEXT_BTN_SELECTOR = 'button[aria-label="Next"]'; // Bottom (Footer)
    const TOP_MENU_SELECTOR = '[class*="menuWrapper"], [class*="_menuWrapper_"]'; // Global Settings

    // --- STATE ---
    let currentSettings = loadSettings();
    let uiObserver = null;

    // --- HELPER FUNCTIONS ---
    function debugLog(...args) { if (DEBUG_MODE) console.log('[DEBUG]', ...args); }

    function loadSettings() {
        const saved = localStorage.getItem(SETTINGS_KEY);
        let parsed = saved ? JSON.parse(saved) : {};
        if (parsed.buttonPosition === 'inline') {
            parsed.buttonPosition = 'inline-top';
        }
        return { ...DEFAULT_SETTINGS, ...parsed };
    }

    function saveSettings(newSettings) {
        localStorage.setItem(SETTINGS_KEY, JSON.stringify(newSettings));
        currentSettings = newSettings;
        cleanupUI();
        initUI();
    }

    function waitForElement(selector, timeoutMs = 5000) {
        return new Promise(resolve => {
            let el = document.querySelector(selector);
            if (el) return resolve(el);
            const startTime = Date.now();
            const observer = new MutationObserver(() => {
                el = document.querySelector(selector);
                if (el) { observer.disconnect(); resolve(el); }
                else if (Date.now() - startTime > timeoutMs) { observer.disconnect(); resolve(null); }
            });
            observer.observe(document.body, { childList: true, subtree: true, attributes: true });
        });
    }

    // --- TEXT PROCESSING ---
    function processText(text) {
        // 1. Remove Em Dashes (If enabled)
        if (currentSettings.removeEmDashes) {
            // Replace em dash with a space, then collapse any double spaces created
            text = text.replace(/—/g, ' ').replace(/ {2,}/g, ' ');
        }

        if (currentSettings.removeThinkTags) {
            text = text.replace(/\n?\s*<(thought|thoughts)>[\s\S]*?<\/(thought|thoughts)>\s*\n?/g, '');
            text = text.replace(/<(system|response)>|<\/response>/g, '');
            text = text.replace(/\n?\s*<think>[\s\S]*?<\/think>\s*\n?/g, '');
            text = text.replace('</think>', '');
        }
        if (currentSettings.removeGeneralTags) {
            text = text.replace(/<\/?[a-zA-Z0-9:-]+(\s[^>]*)?>/g, '');
        }
        if (currentSettings.removeSystemPrompt) {
            text = removeSystemPrompt(text);
        }

        let narrWrap = '';
        let dialWrap = '';
        switch (currentSettings.styleMode) {
            case 'italic': narrWrap = '*'; break;
            case 'bold': narrWrap = '**'; break;
            case 'bold-italic': narrWrap = '*'; dialWrap = '**'; break;
            case 'bold-plain': narrWrap = ''; dialWrap = '**'; break;
            case 'none': default: break;
        }

        const normalizedText = text.replace(/[«“”„‟⹂❞❝]/g, '"');
        const lines = normalizedText.split('\n');

        const processedLines = lines.map(line => {
            let trimmedLine = line.trim();
            if (trimmedLine === '') return '';
            const cleanLine = trimmedLine.replace(/\*/g, '');

            if (/^["*]+$/.test(cleanLine)) return null; 
            if (cleanLine === '---') return '---';

            if (cleanLine.includes('"') || cleanLine.includes('`')) {
                const fragments = cleanLine.split(/("[\s\S]*?"|`[\s\S]*?`)/);
                return fragments.map(frag => {
                    const isQuote = (frag.startsWith('"') && frag.endsWith('"')) || (frag.startsWith('`') && frag.endsWith('`'));
                    if (isQuote) return frag.trim() !== '' ? `${dialWrap}${frag}${dialWrap}` : '';
                    return frag.trim() !== '' ? `${narrWrap}${frag.trim()}${narrWrap}` : '';
                }).filter(Boolean).join(' ');
            }
            return `${narrWrap}${cleanLine}${narrWrap}`;
        });

        return processedLines.filter(l => l !== null).join('\n');
    }

    function removeSystemPrompt(text) {
        if (!text.trim().toLowerCase().includes('theuser')) return text;
        const splitPointIndex = text.search(/[^\s\*]\*[^\s\*]/);
        if (splitPointIndex !== -1) return text.substring(splitPointIndex + 1);
        return text;
    }

    // --- EXECUTION ---
    async function executeFormat() {
        debugLog('Start Format');
        try {
            const allEditButtons = document.querySelectorAll(EDIT_BUTTON_SELECTOR);
            if (allEditButtons.length === 0) return debugLog('No edit buttons');

            const lastEditButton = allEditButtons[allEditButtons.length - 1];
            lastEditButton.click();
            
            await new Promise(r => setTimeout(r, 600));

            const textField = await waitForElement(TEXT_AREA_SELECTOR);
            if (!textField) return debugLog('Text field not found');

            const newText = processText(textField.value);

            const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
            nativeInputValueSetter.call(textField, newText);
            textField.dispatchEvent(new Event('input', { bubbles: true }));

            const confirmButton = await waitForElement(CONFIRM_BUTTON_SELECTOR);
            if (confirmButton) confirmButton.click();

        } catch (error) {
            console.error('JanitorFormatter Error:', error);
        }
    }

    // --- UI MANAGER ---

    function initUI() {
        injectGlobalSettings();

        if (currentSettings.buttonPosition === 'floating') {
            createFloatingUI();
        } else {
            injectInlineButton();
        }

        if (!uiObserver) {
            uiObserver = new MutationObserver(() => {
                injectGlobalSettings();
                if (currentSettings.buttonPosition.includes('inline')) {
                    injectInlineButton();
                }
            });
            uiObserver.observe(document.body, { childList: true, subtree: true });
        }
        
        initKeyboardFix();
    }

    function cleanupUI() {
        const floatContainer = document.getElementById('janitor-editor-container');
        if (floatContainer) floatContainer.remove();

        document.querySelectorAll('.janitor-formatter-btn-inline').forEach(el => el.remove());
        
        const settingsTrigger = document.getElementById('janitor-settings-btn-inline');
        if (settingsTrigger) settingsTrigger.remove();
        
        if (uiObserver) {
            uiObserver.disconnect();
            uiObserver = null;
        }
    }

    // --- SETTINGS BUTTON (Top Menu) ---
    function injectGlobalSettings() {
        const menuWrapper = document.querySelector(TOP_MENU_SELECTOR);
        if (menuWrapper && !document.getElementById('janitor-settings-btn-inline')) {
            const settingsBtn = document.createElement('button');
            settingsBtn.id = 'janitor-settings-btn-inline';
            settingsBtn.innerHTML = currentSettings.showSettingsText ? '⚙️ Formatter' : '⚙️';
            settingsBtn.title = 'Formatting Settings';
            settingsBtn.addEventListener('click', (e) => {
                e.preventDefault(); e.stopPropagation();
                createSettingsModal();
            });
            menuWrapper.prepend(settingsBtn);
        }
    }

    // --- INLINE BUTTON (Top or Bottom) ---
    function injectInlineButton() {
        const pos = currentSettings.buttonPosition; 
        
        // --- INLINE TOP (Controls) ---
        if (pos === 'inline-top') {
            const allTargets = document.querySelectorAll(MSG_CONTROLS_SELECTOR);
            if (allTargets.length === 0) return;
            const lastTarget = allTargets[allTargets.length - 1];

            if (lastTarget.querySelector('.janitor-formatter-btn-inline')) return; 

            document.querySelectorAll('.janitor-formatter-btn-inline').forEach(btn => btn.remove());

            const btn = document.createElement('button');
            btn.className = 'janitor-formatter-btn-inline';
            btn.innerHTML = currentSettings.showButtonText ? '✏️ Auto-Format' : '✏️';
            btn.title = 'Clean and Format';
            btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); executeFormat(); });
            
            lastTarget.prepend(btn);
        }

        // --- INLINE BOTTOM (Message Footer / Next Button) ---
        else if (pos === 'inline-bottom') {
            const allNextButtons = document.querySelectorAll(MSG_NEXT_BTN_SELECTOR);
            if (allNextButtons.length === 0) return;
            const lastNextBtn = allNextButtons[allNextButtons.length - 1];
            const targetContainer = lastNextBtn.parentElement; 

            if (targetContainer.querySelector('.janitor-formatter-btn-inline')) return; 

            document.querySelectorAll('.janitor-formatter-btn-inline').forEach(btn => btn.remove());

            const btn = document.createElement('button');
            btn.className = 'janitor-formatter-btn-inline';
            btn.innerHTML = currentSettings.showButtonText ? '✏️ Auto-Format' : '✏️';
            btn.title = 'Clean and Format';
            btn.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); executeFormat(); });

            targetContainer.style.display = 'flex';
            targetContainer.style.flexDirection = 'row';
            targetContainer.style.alignItems = 'center';
            targetContainer.style.gap = '8px';

            targetContainer.prepend(btn);
        }
    }

    // --- FLOATING UI ---
    function createFloatingUI() {
        const container = document.createElement('div');
        container.id = 'janitor-editor-container';
        
        const size = currentSettings.floatSize || 50;
        const opacity = (currentSettings.floatOpacity || 50) / 100;
        
        container.style.opacity = opacity;
        document.body.appendChild(container);

        const formatBtn = document.createElement('button');
        formatBtn.innerHTML = '✏️';
        formatBtn.id = 'formatter-btn';
        formatBtn.style.width = `${size}px`;
        formatBtn.style.height = `${size}px`;
        formatBtn.style.fontSize = `${Math.max(14, size * 0.45)}px`;
        formatBtn.title = 'Format (Click) / Move (Drag)';
        container.appendChild(formatBtn);

        makeDraggable(container, formatBtn);
    }

    // --- SETTINGS MODAL ---
    function createSettingsModal() {
        if (document.getElementById('janitor-settings-modal')) return;

        const modalOverlay = document.createElement('div');
        modalOverlay.id = 'janitor-settings-modal';
        
        modalOverlay.innerHTML = `
            <div class="janitor-modal-content">
                <h3>Formatter Settings</h3>
                
                <div class="setting-scroll-area">
                    <div class="setting-section">
                        <h4>Interface</h4>
                        <div class="setting-group">
                            <label>Button Position:</label>
                            <select id="setting-pos">
                                <option value="floating">Floating (Draggable)</option>
                                <option value="inline-top">Inline Top (Message Controls)</option>
                                <option value="inline-bottom">Inline Bottom (Message Footer)</option>
                            </select>
                        </div>

                        <div class="setting-group checkbox">
                            <input type="checkbox" id="setting-showsettings-text" ${currentSettings.showSettingsText ? 'checked' : ''}>
                            <label for="setting-showsettings-text">Show Text on Settings Button (Top Right)</label>
                        </div>
                        
                        <div id="inline-text-option" class="setting-group checkbox" style="display:none;">
                            <input type="checkbox" id="setting-showtext" ${currentSettings.showButtonText ? 'checked' : ''}>
                            <label for="setting-showtext">Show Text on Format Button (Inline)</label>
                        </div>

                        <div id="float-options" style="display:none; padding-left:10px; border-left:2px solid #444;">
                            <div class="setting-group">
                                <button id="reset-float-pos" style="background:#444; margin-bottom:10px;">Reset Position to Center</button>
                            </div>
                            <div class="setting-group">
                                <label>Size: <span id="size-val">${currentSettings.floatSize}</span>px</label>
                                <input type="range" id="setting-size" min="30" max="100" value="${currentSettings.floatSize}">
                            </div>
                            <div class="setting-group">
                                <label>Opacity: <span id="op-val">${currentSettings.floatOpacity}</span>%</label>
                                <input type="range" id="setting-opacity" min="10" max="100" value="${currentSettings.floatOpacity}">
                            </div>
                        </div>
                    </div>

                    <div class="setting-section">
                        <h4>Text Style</h4>
                        <div class="setting-group">
                            <label>Narration Format:</label>
                            <select id="setting-style">
                                <option value="italic">Classic (*Narr* "Dial")</option>
                                <option value="bold">Bold Narration (**Narr** "Dial")</option>
                                <option value="bold-italic">Bold Dial + Italic Narr</option>
                                <option value="bold-plain">Bold Dial + Plain Narr</option>
                                <option value="none">Plain Text (No formatting)</option>
                            </select>
                        </div>
                    </div>

                    <div class="setting-section">
                        <h4>Cleaning</h4>
                        <div class="setting-group checkbox">
                            <input type="checkbox" id="setting-think" ${currentSettings.removeThinkTags ? 'checked' : ''}>
                            <label for="setting-think">Remove &lt;think&gt; content</label>
                        </div>
                        <div class="setting-group checkbox">
                            <input type="checkbox" id="setting-gentags" ${currentSettings.removeGeneralTags !== false ? 'checked' : ''}>
                            <label for="setting-gentags">Clean other tags (keep content)</label>
                        </div>
                        <div class="setting-group checkbox">
                            <input type="checkbox" id="setting-prompt" ${currentSettings.removeSystemPrompt ? 'checked' : ''}>
                            <label for="setting-prompt">Remove System Prompts</label>
                        </div>
                        <div class="setting-group checkbox">
                            <input type="checkbox" id="setting-emdashes" ${currentSettings.removeEmDashes ? 'checked' : ''}>
                            <label for="setting-emdashes">Remove Em Dashes (—)</label>
                        </div>
                    </div>
                </div>

                <div class="modal-buttons">
                    <button id="save-settings">Save & Close</button>
                    <button id="cancel-settings" style="background:#555">Cancel</button>
                </div>
            </div>
        `;

        document.body.appendChild(modalOverlay);

        const posSelect = document.getElementById('setting-pos');
        const floatOpts = document.getElementById('float-options');
        const inlineTextOpt = document.getElementById('inline-text-option');
        const sizeInput = document.getElementById('setting-size');
        const opInput = document.getElementById('setting-opacity');

        posSelect.value = currentSettings.buttonPosition;
        document.getElementById('setting-style').value = currentSettings.styleMode;

        const updateVis = () => {
            const val = posSelect.value;
            floatOpts.style.display = val === 'floating' ? 'block' : 'none';
            inlineTextOpt.style.display = val.includes('inline') ? 'flex' : 'none';
        };
        posSelect.addEventListener('change', updateVis);
        updateVis();

        sizeInput.addEventListener('input', (e) => document.getElementById('size-val').innerText = e.target.value);
        opInput.addEventListener('input', (e) => document.getElementById('op-val').innerText = e.target.value);

        document.getElementById('reset-float-pos').onclick = () => {
            const container = document.getElementById('janitor-editor-container');
            if (container) {
                const centerX = (window.innerWidth / 2) - (container.offsetWidth / 2);
                const centerY = (window.innerHeight / 2) - (container.offsetHeight / 2);
                container.style.left = centerX + 'px';
                container.style.top = centerY + 'px';
                container.style.right = 'auto';
                container.style.bottom = 'auto';
                localStorage.setItem(POSITION_KEY, JSON.stringify({ left: container.style.left, top: container.style.top }));
            }
        };

        document.getElementById('save-settings').onclick = () => {
            saveSettings({
                styleMode: document.getElementById('setting-style').value,
                removeThinkTags: document.getElementById('setting-think').checked,
                removeGeneralTags: document.getElementById('setting-gentags').checked,
                removeSystemPrompt: document.getElementById('setting-prompt').checked,
                removeEmDashes: document.getElementById('setting-emdashes').checked,
                buttonPosition: posSelect.value,
                showButtonText: document.getElementById('setting-showtext').checked,
                showSettingsText: document.getElementById('setting-showsettings-text').checked,
                floatSize: parseInt(sizeInput.value),
                floatOpacity: parseInt(opInput.value)
            });
            modalOverlay.remove();
        };
        document.getElementById('cancel-settings').onclick = () => modalOverlay.remove();
    }

    // --- DRAG LOGIC ---
    function makeDraggable(container, handle) {
        let isDragging = false;
        let wasDragged = false;
        let startX, startY, initialLeft, initialTop;

        const savedPos = localStorage.getItem(POSITION_KEY);
        if (savedPos) {
            const { left, top } = JSON.parse(savedPos);
            container.style.left = left;
            container.style.top = top;
            container.style.right = 'auto';
            container.style.bottom = 'auto';
        }

        function onStart(e) {
            isDragging = true; wasDragged = false;
            handle.classList.add('is-dragging');
            container.style.opacity = '1'; 
            const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
            const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
            startX = clientX; startY = clientY;
            const rect = container.getBoundingClientRect();
            initialLeft = rect.left; initialTop = rect.top;
            document.addEventListener('mousemove', onMove);
            document.addEventListener('touchmove', onMove, { passive: false });
            document.addEventListener('mouseup', onEnd);
            document.addEventListener('touchend', onEnd);
            e.preventDefault();
        }

        function onMove(e) {
            if (!isDragging) return;
            wasDragged = true; e.preventDefault();
            const clientX = e.type.includes('touch') ? e.touches[0].clientX : e.clientX;
            const clientY = e.type.includes('touch') ? e.touches[0].clientY : e.clientY;
            const dx = clientX - startX;
            const dy = clientY - startY;
            let newLeft = Math.max(0, Math.min(initialLeft + dx, window.innerWidth - container.offsetWidth));
            let newTop = Math.max(0, Math.min(initialTop + dy, window.innerHeight - container.offsetHeight));
            container.style.left = `${newLeft}px`;
            container.style.top = `${newTop}px`;
            container.style.right = 'auto'; container.style.bottom = 'auto';
        }

        function onEnd() {
            isDragging = false;
            handle.classList.remove('is-dragging');
            container.style.opacity = (currentSettings.floatOpacity || 50) / 100;
            document.removeEventListener('mousemove', onMove);
            document.removeEventListener('touchmove', onMove);
            document.removeEventListener('mouseup', onEnd);
            document.removeEventListener('touchend', onEnd);
            if (wasDragged) {
                localStorage.setItem(POSITION_KEY, JSON.stringify({ left: container.style.left, top: container.style.top }));
            } else {
                executeFormat();
            }
        }
        handle.addEventListener('mousedown', onStart);
        handle.addEventListener('touchstart', onStart, { passive: false });
    }

    // --- KEYBOARD FIX ---
    async function initKeyboardFix() {
        const input = await waitForElement('textarea[placeholder^="Type a message"]');
        const container = document.getElementById('janitor-editor-container');
        if (input && container) {
            input.addEventListener('focus', () => container.style.display = 'none');
            input.addEventListener('blur', () => setTimeout(() => container.style.display = 'flex', 200));
        }
    }

    // --- STYLES ---
    GM_addStyle(`
        /* Floating Button */
        #janitor-editor-container { position: fixed; z-index: 9999; display: flex; align-items: flex-end; gap: 5px; transition: opacity 0.2s; }
        #janitor-editor-container button { border: none; border-radius: 50%; color: white; cursor: pointer; box-shadow: 0 4px 8px rgba(0,0,0,0.3); display: flex; justify-content: center; align-items: center; }
        #formatter-btn { background-color: #c9226e; }
        #formatter-btn.is-dragging { transform: scale(1.1); box-shadow: 0 8px 16px rgba(0,0,0,0.5); }
        
        /* Inline Button - Common */
        .janitor-formatter-btn-inline {
            background: transparent; border: none !important; color: #fff;
            border-radius: 4px; cursor: pointer; width: auto; height: 30px;
            display: inline-flex; justify-content: center; align-items: center;
            font-size: 14px; margin-right: 8px; flex-shrink: 0; padding: 0 5px;
        }
        .janitor-formatter-btn-inline:hover { background: rgba(201, 34, 110, 0.2); }

        /* Ensure Flex for controls */
        [class*="messageControls"] { display: flex !important; flex-direction: row !important; align-items: center !important; }

        /* Settings Trigger */
        #janitor-settings-btn-inline {
            background: transparent; border: none; color: #aaa; cursor: pointer;
            font-size: 14px; margin-right: 15px; font-weight: bold; padding: 5px;
            white-space: nowrap; display: flex; align-items: center;
        }
        #janitor-settings-btn-inline:hover { color: white; }
        
        /* Modal Styles */
        #janitor-settings-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 10000; display: flex; justify-content: center; align-items: center; }
        .janitor-modal-content { background: #1f1f1f; color: white; padding: 20px; border-radius: 12px; width: 90%; max-width: 400px; box-shadow: 0 10px 25px rgba(0,0,0,0.5); font-family: sans-serif; display: flex; flex-direction: column; max-height: 90vh; }
        .setting-scroll-area { overflow-y: auto; flex: 1; margin-bottom: 10px; }
        .setting-section { margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px solid #333; }
        .setting-section h4 { margin: 0 0 10px 0; color: #c9226e; text-transform: uppercase; font-size: 12px; }
        .setting-group { margin-bottom: 15px; display: flex; flex-direction: column; }
        .setting-group.checkbox { flex-direction: row; align-items: center; gap: 10px; }
        .setting-group select, .setting-group input[type="range"] { padding: 8px; border-radius: 4px; background: #333; color: white; border: 1px solid #555; width: 100%; box-sizing: border-box; }
        .modal-buttons { display: flex; gap: 10px; margin-top: auto; }
        .modal-buttons button { flex: 1; padding: 10px; border: none; border-radius: 4px; cursor: pointer; color: white; background: #c9226e; font-weight: bold; }
        
        @media (min-width: 769px) { #janitor-editor-container { right: 27%; bottom: 12%; } }
        @media (max-width: 768px) { #janitor-editor-container { right: 5%; bottom: 20%; } }
    `);

    initUI();
    console.log('Janitor Formatter v10.7 Loaded');
})();