kiranwayne

Grok Enhanced v0.5

Jul 11th, 2025
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 25.78 KB | Source Code | 0 0
  1. // ==UserScript==
  2. // @name         Grok Enhanced
  3. // @namespace    http://tampermonkey.net/
  4. // @version      0.5
  5. // @description  Customize width & justification (main/secondary panels via slider/input/checkbox). Fixed for May 2024 Grok UI update. Show/hide via menu on grok.com. Handles Shadow DOM. Header added.
  6. // @author       kiranwayne
  7. // @match        https://grok.com/*
  8. // @grant        GM_getValue
  9. // @grant        GM_setValue
  10. // @grant        GM_registerMenuCommand
  11. // @grant        GM_unregisterMenuCommand
  12. // @run-at       document-end
  13. // ==/UserScript==
  14.  
  15. (async () => {
  16.     'use strict';
  17.  
  18.     // --- Configuration & Constants ---
  19.     const SCRIPT_NAME = 'Grok Enhanced';
  20.     const SCRIPT_VERSION = '0.5';
  21.     const SCRIPT_AUTHOR = 'kiranwayne';
  22.  
  23.     // Config Keys
  24.     const CONFIG_PREFIX = 'grokEnhancedControls_v6_'; // Updated prefix to reset settings after update
  25.     // Main Panel
  26.     const MAIN_WIDTH_PX_KEY = CONFIG_PREFIX + 'mainWidthPx';
  27.     const USE_DEFAULT_MAIN_WIDTH_KEY = CONFIG_PREFIX + 'useDefaultMainWidth';
  28.     const MAIN_JUSTIFY_KEY = CONFIG_PREFIX + 'mainJustifyEnabled';
  29.     // Secondary Panel
  30.     const SECONDARY_WIDTH_PX_KEY = CONFIG_PREFIX + 'secondaryWidthPx';
  31.     const USE_DEFAULT_SECONDARY_WIDTH_KEY = CONFIG_PREFIX + 'useDefaultSecondaryWidth';
  32.     const SECONDARY_JUSTIFY_KEY = CONFIG_PREFIX + 'secondaryJustifyEnabled';
  33.     // Shared Settings
  34.     const UI_VISIBLE_KEY = CONFIG_PREFIX + 'uiVisible';
  35.  
  36.     // Style/Panel IDs
  37.     const STYLE_ID = 'vm-grok-combined-style-v6';
  38.     const SETTINGS_PANEL_ID = 'grok-userscript-settings-panel-v6';
  39.  
  40.     // Target Selectors
  41.     const MAIN_PANEL_SELECTOR = 'div.breakout[class*="--content-max-width"]'; // [FIX] Updated for May 2024 Grok UI.
  42.     const SECONDARY_PANEL_SELECTOR = '.max-w-4xl'; // NOTE: This selector might also change in the future.
  43.     // Justification targets paragraphs within each panel selector
  44.     const MAIN_JUSTIFY_TARGET_SELECTOR = `${MAIN_PANEL_SELECTOR} p`;
  45.     const SECONDARY_JUSTIFY_TARGET_SELECTOR = `${SECONDARY_PANEL_SELECTOR} p`;
  46.  
  47.     // Slider/Input Ranges & Defaults
  48.     const DEFAULT_MAIN_WIDTH_PX = 1100; const MIN_MAIN_WIDTH_PX = 500; const MAX_MAIN_WIDTH_PX = 2000;
  49.     const DEFAULT_SECONDARY_WIDTH_PX = 900; const MIN_SECONDARY_WIDTH_PX = 500; const MAX_SECONDARY_WIDTH_PX = 1500;
  50.     const STEP_WIDTH_PX = 1;
  51.  
  52.     // Layout Constants (Unchanged) - Keep if needed for other features
  53.     const LAYOUT_CONFIG = { /* ... Z-indexes, etc. ... */ };
  54.  
  55.     // --- State ---
  56.     let config = {
  57.         mainWidthPx: DEFAULT_MAIN_WIDTH_PX,
  58.         useDefaultMainWidth: false,
  59.         mainJustifyEnabled: false,
  60.         secondaryWidthPx: DEFAULT_SECONDARY_WIDTH_PX,
  61.         useDefaultSecondaryWidth: false,
  62.         secondaryJustifyEnabled: false,
  63.         uiVisible: false
  64.     };
  65.  
  66.     let styleElement = null;
  67.     let settingsPanel = null;
  68.     // Main Panel UI
  69.     let mainWidthSlider=null, mainWidthLabel=null, mainWidthInput=null, useDefaultMainCheckbox=null, mainJustifyCheckbox=null;
  70.     // Secondary Panel UI
  71.     let secondaryWidthSlider=null, secondaryWidthLabel=null, secondaryWidthInput=null, useDefaultSecondaryCheckbox=null, secondaryJustifyCheckbox=null;
  72.     // Shared UI
  73.     let menuCommandId_ToggleUI = null;
  74.     const allStyleRoots = new Set();
  75.  
  76.     // --- Helper Functions ---
  77.  
  78.     async function loadSettings() {
  79.         // Load Main Panel Settings
  80.         config.mainWidthPx = await GM_getValue(MAIN_WIDTH_PX_KEY, DEFAULT_MAIN_WIDTH_PX);
  81.         config.mainWidthPx = Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, config.mainWidthPx));
  82.         config.useDefaultMainWidth = await GM_getValue(USE_DEFAULT_MAIN_WIDTH_KEY, false);
  83.         config.mainJustifyEnabled = await GM_getValue(MAIN_JUSTIFY_KEY, false);
  84.  
  85.         // Load Secondary Panel Settings
  86.         config.secondaryWidthPx = await GM_getValue(SECONDARY_WIDTH_PX_KEY, DEFAULT_SECONDARY_WIDTH_PX);
  87.         config.secondaryWidthPx = Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, config.secondaryWidthPx));
  88.         config.useDefaultSecondaryWidth = await GM_getValue(USE_DEFAULT_SECONDARY_WIDTH_KEY, false);
  89.         config.secondaryJustifyEnabled = await GM_getValue(SECONDARY_JUSTIFY_KEY, false);
  90.  
  91.         // Load Shared Settings
  92.         config.uiVisible = await GM_getValue(UI_VISIBLE_KEY, false);
  93.     }
  94.  
  95.     async function saveSetting(key, value) {
  96.         let keyToSave = key;
  97.         let valueToSave = value;
  98.  
  99.         // Handle Width Keys
  100.         if (key === MAIN_WIDTH_PX_KEY || key === SECONDARY_WIDTH_PX_KEY) {
  101.              const numValue = parseInt(value, 10);
  102.              if (!isNaN(numValue)) {
  103.                  let clampedValue = numValue;
  104.                  if (key === MAIN_WIDTH_PX_KEY) { clampedValue = Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, numValue)); config.mainWidthPx = clampedValue; }
  105.                  else { clampedValue = Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, numValue)); config.secondaryWidthPx = clampedValue; }
  106.                  valueToSave = clampedValue;
  107.              } else { return; }
  108.         }
  109.         // Handle Boolean Keys
  110.         else if (key === USE_DEFAULT_MAIN_WIDTH_KEY) config.useDefaultMainWidth = value;
  111.         else if (key === USE_DEFAULT_SECONDARY_WIDTH_KEY) config.useDefaultSecondaryWidth = value;
  112.         else if (key === MAIN_JUSTIFY_KEY) config.mainJustifyEnabled = value;
  113.         else if (key === SECONDARY_JUSTIFY_KEY) config.secondaryJustifyEnabled = value;
  114.         else if (key === UI_VISIBLE_KEY) config.uiVisible = value;
  115.  
  116.         await GM_setValue(keyToSave, valueToSave);
  117.     }
  118.  
  119.  
  120.     // --- Style Generation Functions ---
  121.     function generateCss() {
  122.         // Spinner Fix CSS
  123.         const spinnerCss = `
  124.             #${SETTINGS_PANEL_ID} input[type=number] { -moz-appearance: textfield !important; }
  125.             #${SETTINGS_PANEL_ID} input[type=number]::-webkit-inner-spin-button,
  126.             #${SETTINGS_PANEL_ID} input[type=number]::-webkit-outer-spin-button {
  127.                 -webkit-appearance: inner-spin-button !important; opacity: 1 !important; cursor: pointer;
  128.             }
  129.         `;
  130.  
  131.         // --- Main Panel Width ---
  132.         // [FIX] Changed from setting 'max-width' to overriding the '--content-max-width' CSS variable to work with the new Grok UI.
  133.         let mainWidthRule = !config.useDefaultMainWidth ? `${MAIN_PANEL_SELECTOR} { --content-max-width: ${config.mainWidthPx}px !important; }` : '/* Main width default */';
  134.         // --- Main Panel Justify ---
  135.         let mainJustifyRule = config.mainJustifyEnabled ? `${MAIN_JUSTIFY_TARGET_SELECTOR} { text-align: justify !important; hyphens: auto; }` : '/* Main justify off */';
  136.  
  137.         // --- Secondary Panel Width ---
  138.         let secondaryWidthRule = !config.useDefaultSecondaryWidth ? `${SECONDARY_PANEL_SELECTOR} { max-width: ${config.secondaryWidthPx}px !important; }` : '/* Secondary width default */';
  139.         // --- Secondary Panel Justify ---
  140.         let secondaryJustifyRule = config.secondaryJustifyEnabled ? `${SECONDARY_JUSTIFY_TARGET_SELECTOR} { text-align: justify !important; hyphens: auto; }` : '/* Secondary justify off */';
  141.  
  142.         // Combine all rules
  143.         return `
  144.             /* Grok Enhanced Styles v${SCRIPT_VERSION} */
  145.             ${spinnerCss}
  146.             ${mainWidthRule}
  147.             ${mainJustifyRule}
  148.             ${secondaryWidthRule}
  149.             ${secondaryJustifyRule}
  150.         `;
  151.     }
  152.  
  153.     // --- Style Injection / Update Function ---
  154.     function applyStylesToAllRoots() {
  155.         // Apply the combined CSS to head and all shadow roots
  156.         const css = generateCss();
  157.         allStyleRoots.forEach(root => { if (root) injectOrUpdateStyle(root, STYLE_ID, css); });
  158.     }
  159.     function injectOrUpdateStyle(root, styleId, cssContent) { if (!root) return; let style = root.querySelector(`#${styleId}`); if (cssContent) { if (!style) { style = document.createElement('style'); style.id = styleId; style.textContent = cssContent; if (root === document.head || (root.nodeType === Node.ELEMENT_NODE && root.shadowRoot === null) || root.nodeType === Node.DOCUMENT_FRAGMENT_NODE) root.appendChild(style); else if (root.shadowRoot) root.shadowRoot.appendChild(style); } else if (style.textContent !== cssContent) style.textContent = cssContent; } else { if (style) style.remove(); } }
  160.  
  161.      // --- UI State Update ---
  162.      function updateUIState() {
  163.         if (!settingsPanel || !document.body.contains(settingsPanel)) return;
  164.  
  165.         // Update Main Panel Controls
  166.         if(useDefaultMainCheckbox && mainWidthSlider && mainWidthInput && mainWidthLabel && mainJustifyCheckbox) {
  167.             useDefaultMainCheckbox.checked = config.useDefaultMainWidth;
  168.             const isMainCustom = !config.useDefaultMainWidth;
  169.             mainWidthSlider.disabled = !isMainCustom; mainWidthInput.disabled = !isMainCustom;
  170.             mainWidthSlider.value = config.mainWidthPx; mainWidthInput.value = config.mainWidthPx;
  171.             mainWidthLabel.textContent = `${config.mainWidthPx}px`;
  172.             mainWidthLabel.style.opacity = isMainCustom ? 1 : 0.5; mainWidthSlider.style.opacity = isMainCustom ? 1 : 0.5; mainWidthInput.style.opacity = isMainCustom ? 1 : 0.5;
  173.             mainJustifyCheckbox.checked = config.mainJustifyEnabled;
  174.         }
  175.  
  176.         // Update Secondary Panel Controls
  177.          if(useDefaultSecondaryCheckbox && secondaryWidthSlider && secondaryWidthInput && secondaryWidthLabel && secondaryJustifyCheckbox) {
  178.             useDefaultSecondaryCheckbox.checked = config.useDefaultSecondaryWidth;
  179.             const isSecondaryCustom = !config.useDefaultSecondaryWidth;
  180.             secondaryWidthSlider.disabled = !isSecondaryCustom; secondaryWidthInput.disabled = !isSecondaryCustom;
  181.             secondaryWidthSlider.value = config.secondaryWidthPx; secondaryWidthInput.value = config.secondaryWidthPx;
  182.             secondaryWidthLabel.textContent = `${config.secondaryWidthPx}px`;
  183.             secondaryWidthLabel.style.opacity = isSecondaryCustom ? 1 : 0.5; secondaryWidthSlider.style.opacity = isSecondaryCustom ? 1 : 0.5; secondaryWidthInput.style.opacity = isSecondaryCustom ? 1 : 0.5;
  184.             secondaryJustifyCheckbox.checked = config.secondaryJustifyEnabled;
  185.         }
  186.     }
  187.  
  188.     // --- Click Outside Handler (Standard) ---
  189.     async function handleClickOutside(event) { if (event.target?.closest?.('iframe[id^="tampermonkey_"]')) return; if (settingsPanel && document.body.contains(settingsPanel) && !settingsPanel.contains(event.target)) { if (config.uiVisible) { await saveSetting(UI_VISIBLE_KEY, false); removeSettingsUI(); updateTampermonkeyMenu(); } } }
  190.  
  191.     // --- UI Creation/Removal ---
  192.     function removeSettingsUI() {
  193.         document.removeEventListener('click', handleClickOutside, true);
  194.         settingsPanel = document.getElementById(SETTINGS_PANEL_ID);
  195.         if (settingsPanel) {
  196.              settingsPanel.remove(); settingsPanel = null;
  197.              mainWidthSlider=mainWidthLabel=mainWidthInput=useDefaultMainCheckbox=mainJustifyCheckbox=null;
  198.              secondaryWidthSlider=secondaryWidthLabel=secondaryWidthInput=useDefaultSecondaryCheckbox=secondaryJustifyCheckbox=null;
  199.         }
  200.         document.removeEventListener('click', handleClickOutside, true);
  201.     }
  202.  
  203.     function createSettingsUI() {
  204.         if (document.getElementById(SETTINGS_PANEL_ID) || !config.uiVisible) return;
  205.         const body = document.body || document.getElementsByTagName('body')[0];
  206.         if (!body) { console.error("[Grok Enhanced] Cannot find body."); return; }
  207.         document.removeEventListener('click', handleClickOutside, true);
  208.  
  209.         settingsPanel = document.createElement('div');
  210.         settingsPanel.id = SETTINGS_PANEL_ID;
  211.         Object.assign(settingsPanel.style, { position: 'fixed', top: '10px', right: '10px', zIndex: '99999', display: 'block', background: '#343541', color: '#ECECF1', border: '1px solid #565869', borderRadius: '6px', padding: '15px', boxShadow: '0 4px 10px rgba(0,0,0,0.3)', minWidth: '300px' });
  212.  
  213.         // --- Header ---
  214.         const headerDiv = document.createElement('div'); headerDiv.style.marginBottom = '10px'; headerDiv.style.paddingBottom = '10px'; headerDiv.style.borderBottom = '1px solid #565869'; const titleElement = document.createElement('h4'); titleElement.textContent = SCRIPT_NAME; Object.assign(titleElement.style, { margin: '0 0 5px 0', fontSize: '1.1em', fontWeight: 'bold', color: '#FFFFFF'}); const versionElement = document.createElement('p'); versionElement.textContent = `Version: ${SCRIPT_VERSION}`; Object.assign(versionElement.style, { margin: '0 0 2px 0', fontSize: '0.85em', opacity: '0.8'}); const authorElement = document.createElement('p'); authorElement.textContent = `Author: ${SCRIPT_AUTHOR}`; Object.assign(authorElement.style, { margin: '0', fontSize: '0.85em', opacity: '0.8'}); headerDiv.appendChild(titleElement); headerDiv.appendChild(versionElement); headerDiv.appendChild(authorElement); settingsPanel.appendChild(headerDiv);
  215.  
  216.         // --- Main Panel Width Section ---
  217.         const mainWidthSection = document.createElement('div'); mainWidthSection.style.marginTop = '10px'; mainWidthSection.style.marginBottom = '15px';
  218.         const mainSectionTitle = document.createElement('div'); mainSectionTitle.textContent = 'Main Panel'; mainSectionTitle.style.fontWeight = 'bold'; mainSectionTitle.style.marginBottom = '8px'; mainSectionTitle.style.fontSize = '0.9em';
  219.         const mainDefaultDiv = document.createElement('div'); mainDefaultDiv.style.marginBottom = '8px';
  220.         useDefaultMainCheckbox = document.createElement('input'); useDefaultMainCheckbox.type = 'checkbox'; useDefaultMainCheckbox.id = 'grok-main-default-toggle';
  221.         const mainDefaultLabel = document.createElement('label'); mainDefaultLabel.htmlFor = 'grok-main-default-toggle'; mainDefaultLabel.textContent = ' Use Grok Default Width'; mainDefaultLabel.style.cursor = 'pointer'; mainDefaultLabel.style.fontSize = '0.9em';
  222.         mainDefaultDiv.appendChild(useDefaultMainCheckbox); mainDefaultDiv.appendChild(mainDefaultLabel);
  223.         const mainSliderInputDiv = document.createElement('div'); mainSliderInputDiv.style.display = 'flex'; mainSliderInputDiv.style.alignItems = 'center'; mainSliderInputDiv.style.gap = '10px'; mainSliderInputDiv.style.marginBottom = '8px';
  224.         mainWidthLabel = document.createElement('span'); mainWidthLabel.style.cssText = 'min-width: 50px; font-family: monospace; text-align: right;';
  225.         mainWidthSlider = document.createElement('input'); mainWidthSlider.type = 'range'; mainWidthSlider.min = MIN_MAIN_WIDTH_PX; mainWidthSlider.max = MAX_MAIN_WIDTH_PX; mainWidthSlider.step = STEP_WIDTH_PX; mainWidthSlider.style.cssText = 'flex-grow: 1; vertical-align: middle;';
  226.         mainWidthInput = document.createElement('input'); mainWidthInput.type = 'number'; mainWidthInput.min = MIN_MAIN_WIDTH_PX; mainWidthInput.max = MAX_MAIN_WIDTH_PX; mainWidthInput.step = STEP_WIDTH_PX; mainWidthInput.style.cssText = 'width: 60px; padding: 2px 4px; background: #202123; color: #ECECF1; border: 1px solid #565869; border-radius: 4px; vertical-align: middle;';
  227.         mainSliderInputDiv.appendChild(mainWidthLabel); mainSliderInputDiv.appendChild(mainWidthSlider); mainSliderInputDiv.appendChild(mainWidthInput);
  228.         const mainJustifyDiv = document.createElement('div');
  229.         mainJustifyCheckbox = document.createElement('input'); mainJustifyCheckbox.type = 'checkbox'; mainJustifyCheckbox.id = 'grok-main-justify-toggle';
  230.         const mainJustifyLabel = document.createElement('label'); mainJustifyLabel.htmlFor = 'grok-main-justify-toggle'; mainJustifyLabel.textContent = ' Enable Text Justification'; mainJustifyLabel.style.cursor = 'pointer'; mainJustifyLabel.style.fontSize = '0.9em';
  231.         mainJustifyDiv.appendChild(mainJustifyCheckbox); mainJustifyDiv.appendChild(mainJustifyLabel);
  232.         mainWidthSection.appendChild(mainSectionTitle); mainWidthSection.appendChild(mainDefaultDiv); mainWidthSection.appendChild(mainSliderInputDiv); mainWidthSection.appendChild(mainJustifyDiv);
  233.         settingsPanel.appendChild(mainWidthSection);
  234.  
  235.         // --- Secondary Panel Width Section ---
  236.         const secondaryWidthSection = document.createElement('div'); secondaryWidthSection.style.borderTop = '1px dashed #565869'; secondaryWidthSection.style.paddingTop = '15px'; secondaryWidthSection.style.marginTop = '15px'; secondaryWidthSection.style.marginBottom = '15px';
  237.         const secondarySectionTitle = document.createElement('div'); secondarySectionTitle.textContent = 'Secondary Panel'; secondarySectionTitle.style.fontWeight = 'bold'; secondarySectionTitle.style.marginBottom = '8px'; secondarySectionTitle.style.fontSize = '0.9em';
  238.         const secondaryDefaultDiv = document.createElement('div'); secondaryDefaultDiv.style.marginBottom = '8px';
  239.         useDefaultSecondaryCheckbox = document.createElement('input'); useDefaultSecondaryCheckbox.type = 'checkbox'; useDefaultSecondaryCheckbox.id = 'grok-secondary-default-toggle';
  240.         const secondaryDefaultLabel = document.createElement('label'); secondaryDefaultLabel.htmlFor = 'grok-secondary-default-toggle'; secondaryDefaultLabel.textContent = ' Use Grok Default Width'; secondaryDefaultLabel.style.cursor = 'pointer'; secondaryDefaultLabel.style.fontSize = '0.9em';
  241.         secondaryDefaultDiv.appendChild(useDefaultSecondaryCheckbox); secondaryDefaultDiv.appendChild(secondaryDefaultLabel);
  242.         const secondarySliderInputDiv = document.createElement('div'); secondarySliderInputDiv.style.display = 'flex'; secondarySliderInputDiv.style.alignItems = 'center'; secondarySliderInputDiv.style.gap = '10px'; secondarySliderInputDiv.style.marginBottom = '8px';
  243.         secondaryWidthLabel = document.createElement('span'); secondaryWidthLabel.style.cssText = 'min-width: 50px; font-family: monospace; text-align: right;';
  244.         secondaryWidthSlider = document.createElement('input'); secondaryWidthSlider.type = 'range'; secondaryWidthSlider.min = MIN_SECONDARY_WIDTH_PX; secondaryWidthSlider.max = MAX_SECONDARY_WIDTH_PX; secondaryWidthSlider.step = STEP_WIDTH_PX; secondaryWidthSlider.style.cssText = 'flex-grow: 1; vertical-align: middle;';
  245.         secondaryWidthInput = document.createElement('input'); secondaryWidthInput.type = 'number'; secondaryWidthInput.min = MIN_SECONDARY_WIDTH_PX; secondaryWidthInput.max = MAX_SECONDARY_WIDTH_PX; secondaryWidthInput.step = STEP_WIDTH_PX; secondaryWidthInput.style.cssText = 'width: 60px; padding: 2px 4px; background: #202123; color: #ECECF1; border: 1px solid #565869; border-radius: 4px; vertical-align: middle;';
  246.         secondarySliderInputDiv.appendChild(secondaryWidthLabel); secondarySliderInputDiv.appendChild(secondaryWidthSlider); secondarySliderInputDiv.appendChild(secondaryWidthInput);
  247.         const secondaryJustifyDiv = document.createElement('div');
  248.         secondaryJustifyCheckbox = document.createElement('input'); secondaryJustifyCheckbox.type = 'checkbox'; secondaryJustifyCheckbox.id = 'grok-secondary-justify-toggle';
  249.         const secondaryJustifyLabel = document.createElement('label'); secondaryJustifyLabel.htmlFor = 'grok-secondary-justify-toggle'; secondaryJustifyLabel.textContent = ' Enable Text Justification'; secondaryJustifyLabel.style.cursor = 'pointer'; secondaryJustifyLabel.style.fontSize = '0.9em';
  250.         secondaryJustifyDiv.appendChild(secondaryJustifyCheckbox); secondaryJustifyDiv.appendChild(secondaryJustifyLabel);
  251.         secondaryWidthSection.appendChild(secondarySectionTitle); secondaryWidthSection.appendChild(secondaryDefaultDiv); secondaryWidthSection.appendChild(secondarySliderInputDiv); secondaryWidthSection.appendChild(secondaryJustifyDiv);
  252.         settingsPanel.appendChild(secondaryWidthSection);
  253.  
  254.  
  255.         body.appendChild(settingsPanel);
  256.  
  257.         // --- Event Listeners ---
  258.         // Main Panel
  259.         useDefaultMainCheckbox.addEventListener('change', async (e) => { await saveSetting(USE_DEFAULT_MAIN_WIDTH_KEY, e.target.checked); applyStylesToAllRoots(); updateUIState(); });
  260.         mainWidthSlider.addEventListener('input', (e) => { if (config.useDefaultMainWidth) return; const nw=parseInt(e.target.value,10); config.mainWidthPx=nw; if(mainWidthLabel) mainWidthLabel.textContent=`${nw}px`; if(mainWidthInput) mainWidthInput.value=nw; applyStylesToAllRoots(); });
  261.         mainWidthSlider.addEventListener('change', async (e) => { if (config.useDefaultMainWidth) return; const fw=parseInt(e.target.value,10); await saveSetting(MAIN_WIDTH_PX_KEY, fw); });
  262.         mainWidthInput.addEventListener('input', (e) => { if (config.useDefaultMainWidth) return; let nw=parseInt(e.target.value,10); if(isNaN(nw)) return; nw=Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, nw)); config.mainWidthPx=nw; if(mainWidthLabel) mainWidthLabel.textContent=`${nw}px`; if(mainWidthSlider) mainWidthSlider.value=nw; applyStylesToAllRoots(); });
  263.         mainWidthInput.addEventListener('change', async (e) => { if (config.useDefaultMainWidth) return; let fw=parseInt(e.target.value,10); if(isNaN(fw)) fw=config.mainWidthPx; fw=Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, fw)); e.target.value=fw; if(mainWidthSlider) mainWidthSlider.value=fw; if(mainWidthLabel) mainWidthLabel.textContent=`${fw}px`; await saveSetting(MAIN_WIDTH_PX_KEY, fw); applyStylesToAllRoots(); });
  264.         mainJustifyCheckbox.addEventListener('change', async (e) => { await saveSetting(MAIN_JUSTIFY_KEY, e.target.checked); applyStylesToAllRoots(); });
  265.  
  266.         // Secondary Panel
  267.         useDefaultSecondaryCheckbox.addEventListener('change', async (e) => { await saveSetting(USE_DEFAULT_SECONDARY_WIDTH_KEY, e.target.checked); applyStylesToAllRoots(); updateUIState(); });
  268.         secondaryWidthSlider.addEventListener('input', (e) => { if (config.useDefaultSecondaryWidth) return; const nw=parseInt(e.target.value,10); config.secondaryWidthPx=nw; if(secondaryWidthLabel) secondaryWidthLabel.textContent=`${nw}px`; if(secondaryWidthInput) secondaryWidthInput.value=nw; applyStylesToAllRoots(); });
  269.         secondaryWidthSlider.addEventListener('change', async (e) => { if (config.useDefaultSecondaryWidth) return; const fw=parseInt(e.target.value,10); await saveSetting(SECONDARY_WIDTH_PX_KEY, fw); });
  270.         secondaryWidthInput.addEventListener('input', (e) => { if (config.useDefaultSecondaryWidth) return; let nw=parseInt(e.target.value,10); if(isNaN(nw)) return; nw=Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, nw)); config.secondaryWidthPx=nw; if(secondaryWidthLabel) secondaryWidthLabel.textContent=`${nw}px`; if(secondaryWidthSlider) secondaryWidthSlider.value=nw; applyStylesToAllRoots(); });
  271.         secondaryWidthInput.addEventListener('change', async (e) => { if (config.useDefaultSecondaryWidth) return; let fw=parseInt(e.target.value,10); if(isNaN(fw)) fw=config.secondaryWidthPx; fw=Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, fw)); e.target.value=fw; if(secondaryWidthSlider) secondaryWidthSlider.value=fw; if(secondaryWidthLabel) secondaryWidthLabel.textContent=`${fw}px`; await saveSetting(SECONDARY_WIDTH_PX_KEY, fw); applyStylesToAllRoots(); });
  272.         secondaryJustifyCheckbox.addEventListener('change', async (e) => { await saveSetting(SECONDARY_JUSTIFY_KEY, e.target.checked); applyStylesToAllRoots(); });
  273.  
  274.         // --- Final UI Setup ---
  275.         updateUIState();
  276.         setTimeout(() => { if (document) document.addEventListener('click', handleClickOutside, true); }, 0);
  277.         if (document.head) injectOrUpdateStyle(document.head, STYLE_ID, generateCss());
  278.     }
  279.  
  280.     // --- Tampermonkey Menu (Standard) ---
  281.     function updateTampermonkeyMenu() { const cmdId = menuCommandId_ToggleUI; menuCommandId_ToggleUI = null; if (cmdId !== null && typeof GM_unregisterMenuCommand === 'function') try { GM_unregisterMenuCommand(cmdId); } catch (e) { console.warn('Failed unregister', e); } const label = config.uiVisible ? 'Hide Settings Panel' : 'Show Settings Panel'; if (typeof GM_registerMenuCommand === 'function') menuCommandId_ToggleUI = GM_registerMenuCommand(label, async () => { await new Promise(res => setTimeout(res, 50)); const newState = !config.uiVisible; await saveSetting(UI_VISIBLE_KEY, newState); if (newState) createSettingsUI(); else removeSettingsUI(); updateTampermonkeyMenu(); }); }
  282.  
  283.     // --- Shadow DOM Handling (Standard) ---
  284.     function getShadowRoot(element) { try { return element.shadowRoot; } catch (e) { return null; } }
  285.     function processElement(element) { const shadow = getShadowRoot(element); if (shadow && shadow.nodeType === Node.DOCUMENT_FRAGMENT_NODE && !allStyleRoots.has(shadow)) { allStyleRoots.add(shadow); injectOrUpdateStyle(shadow, STYLE_ID, generateCss()); return true; } return false; }
  286.  
  287.     // --- Initialization (Standard) ---
  288.     console.log('[Grok Enhanced] Script starting (run-at=document-end)...');
  289.     if (document.head) allStyleRoots.add(document.head); else { const rootNode = document.documentElement || document; allStyleRoots.add(rootNode); console.warn("[Grok Enhanced] document.head not found."); }
  290.     await loadSettings();
  291.     applyStylesToAllRoots();
  292.     let initialRootsFound = 0; try { document.querySelectorAll('*').forEach(el => { if (processElement(el)) initialRootsFound++; }); } catch(e) { console.error("[Grok Enhanced] Error during initial scan:", e); } console.log(`[Grok Enhanced] Initial scan complete. Found ${initialRootsFound} roots.`);
  293.     if (config.uiVisible) createSettingsUI();
  294.     updateTampermonkeyMenu();
  295.     const observer = new MutationObserver((mutations) => { let processedNewNode = false; mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) try { const elementsToCheck = [node, ...node.querySelectorAll('*')]; elementsToCheck.forEach(el => { if (processElement(el)) processedNewNode = true; }); } catch(e) { console.error("[Grok Enhanced] Error querying descendants:", node, e); } }); }); });
  296.     observer.observe(document.documentElement || document.body || document, { childList: true, subtree: true });
  297.     console.log('[Grok Enhanced] Initialization complete.');
  298.  
  299. })();
Advertisement
Add Comment
Please, Sign In to add comment