Last active
October 14, 2025 23:29
-
-
Save kiranwayne/33c7340b4169617116cc8068cf10cb26 to your computer and use it in GitHub Desktop.
Customize width & justification (main/secondary panels via slider/input/checkbox)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Grok Enhanced | |
| // @namespace https://gist.github.com/kiranwayne | |
| // @version 0.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. | |
| // @author kiranwayne | |
| // @match https://grok.com/* | |
| // @updateURL https://gist.githubusercontent.com/kiranwayne/33c7340b4169617116cc8068cf10cb26/raw/grok_enhanced.js | |
| // @downloadURL https://gist.githubusercontent.com/kiranwayne/33c7340b4169617116cc8068cf10cb26/raw/grok_enhanced.js | |
| // @grant GM_getValue | |
| // @grant GM_setValue | |
| // @grant GM_registerMenuCommand | |
| // @grant GM_unregisterMenuCommand | |
| // @run-at document-end | |
| // ==/UserScript== | |
| (async () => { | |
| 'use strict'; | |
| // --- Configuration & Constants --- | |
| const SCRIPT_NAME = 'Grok Enhanced'; | |
| const SCRIPT_VERSION = '0.5'; | |
| const SCRIPT_AUTHOR = 'kiranwayne'; | |
| // Config Keys | |
| const CONFIG_PREFIX = 'grokEnhancedControls_v6_'; // Updated prefix to reset settings after update | |
| // Main Panel | |
| const MAIN_WIDTH_PX_KEY = CONFIG_PREFIX + 'mainWidthPx'; | |
| const USE_DEFAULT_MAIN_WIDTH_KEY = CONFIG_PREFIX + 'useDefaultMainWidth'; | |
| const MAIN_JUSTIFY_KEY = CONFIG_PREFIX + 'mainJustifyEnabled'; | |
| // Secondary Panel | |
| const SECONDARY_WIDTH_PX_KEY = CONFIG_PREFIX + 'secondaryWidthPx'; | |
| const USE_DEFAULT_SECONDARY_WIDTH_KEY = CONFIG_PREFIX + 'useDefaultSecondaryWidth'; | |
| const SECONDARY_JUSTIFY_KEY = CONFIG_PREFIX + 'secondaryJustifyEnabled'; | |
| // Shared Settings | |
| const UI_VISIBLE_KEY = CONFIG_PREFIX + 'uiVisible'; | |
| // Style/Panel IDs | |
| const STYLE_ID = 'vm-grok-combined-style-v6'; | |
| const SETTINGS_PANEL_ID = 'grok-userscript-settings-panel-v6'; | |
| // Target Selectors | |
| const MAIN_PANEL_SELECTOR = 'div.breakout[class*="--content-max-width"]'; // [FIX] Updated for May 2024 Grok UI. | |
| const SECONDARY_PANEL_SELECTOR = '.max-w-4xl'; // NOTE: This selector might also change in the future. | |
| // Justification targets paragraphs within each panel selector | |
| const MAIN_JUSTIFY_TARGET_SELECTOR = `${MAIN_PANEL_SELECTOR} p`; | |
| const SECONDARY_JUSTIFY_TARGET_SELECTOR = `${SECONDARY_PANEL_SELECTOR} p`; | |
| // Slider/Input Ranges & Defaults | |
| const DEFAULT_MAIN_WIDTH_PX = 1100; const MIN_MAIN_WIDTH_PX = 500; const MAX_MAIN_WIDTH_PX = 2000; | |
| const DEFAULT_SECONDARY_WIDTH_PX = 900; const MIN_SECONDARY_WIDTH_PX = 500; const MAX_SECONDARY_WIDTH_PX = 1500; | |
| const STEP_WIDTH_PX = 1; | |
| // Layout Constants (Unchanged) - Keep if needed for other features | |
| const LAYOUT_CONFIG = { /* ... Z-indexes, etc. ... */ }; | |
| // --- State --- | |
| let config = { | |
| mainWidthPx: DEFAULT_MAIN_WIDTH_PX, | |
| useDefaultMainWidth: false, | |
| mainJustifyEnabled: false, | |
| secondaryWidthPx: DEFAULT_SECONDARY_WIDTH_PX, | |
| useDefaultSecondaryWidth: false, | |
| secondaryJustifyEnabled: false, | |
| uiVisible: false | |
| }; | |
| let styleElement = null; | |
| let settingsPanel = null; | |
| // Main Panel UI | |
| let mainWidthSlider=null, mainWidthLabel=null, mainWidthInput=null, useDefaultMainCheckbox=null, mainJustifyCheckbox=null; | |
| // Secondary Panel UI | |
| let secondaryWidthSlider=null, secondaryWidthLabel=null, secondaryWidthInput=null, useDefaultSecondaryCheckbox=null, secondaryJustifyCheckbox=null; | |
| // Shared UI | |
| let menuCommandId_ToggleUI = null; | |
| const allStyleRoots = new Set(); | |
| // --- Helper Functions --- | |
| async function loadSettings() { | |
| // Load Main Panel Settings | |
| config.mainWidthPx = await GM_getValue(MAIN_WIDTH_PX_KEY, DEFAULT_MAIN_WIDTH_PX); | |
| config.mainWidthPx = Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, config.mainWidthPx)); | |
| config.useDefaultMainWidth = await GM_getValue(USE_DEFAULT_MAIN_WIDTH_KEY, false); | |
| config.mainJustifyEnabled = await GM_getValue(MAIN_JUSTIFY_KEY, false); | |
| // Load Secondary Panel Settings | |
| config.secondaryWidthPx = await GM_getValue(SECONDARY_WIDTH_PX_KEY, DEFAULT_SECONDARY_WIDTH_PX); | |
| config.secondaryWidthPx = Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, config.secondaryWidthPx)); | |
| config.useDefaultSecondaryWidth = await GM_getValue(USE_DEFAULT_SECONDARY_WIDTH_KEY, false); | |
| config.secondaryJustifyEnabled = await GM_getValue(SECONDARY_JUSTIFY_KEY, false); | |
| // Load Shared Settings | |
| config.uiVisible = await GM_getValue(UI_VISIBLE_KEY, false); | |
| } | |
| async function saveSetting(key, value) { | |
| let keyToSave = key; | |
| let valueToSave = value; | |
| // Handle Width Keys | |
| if (key === MAIN_WIDTH_PX_KEY || key === SECONDARY_WIDTH_PX_KEY) { | |
| const numValue = parseInt(value, 10); | |
| if (!isNaN(numValue)) { | |
| let clampedValue = numValue; | |
| if (key === MAIN_WIDTH_PX_KEY) { clampedValue = Math.max(MIN_MAIN_WIDTH_PX, Math.min(MAX_MAIN_WIDTH_PX, numValue)); config.mainWidthPx = clampedValue; } | |
| else { clampedValue = Math.max(MIN_SECONDARY_WIDTH_PX, Math.min(MAX_SECONDARY_WIDTH_PX, numValue)); config.secondaryWidthPx = clampedValue; } | |
| valueToSave = clampedValue; | |
| } else { return; } | |
| } | |
| // Handle Boolean Keys | |
| else if (key === USE_DEFAULT_MAIN_WIDTH_KEY) config.useDefaultMainWidth = value; | |
| else if (key === USE_DEFAULT_SECONDARY_WIDTH_KEY) config.useDefaultSecondaryWidth = value; | |
| else if (key === MAIN_JUSTIFY_KEY) config.mainJustifyEnabled = value; | |
| else if (key === SECONDARY_JUSTIFY_KEY) config.secondaryJustifyEnabled = value; | |
| else if (key === UI_VISIBLE_KEY) config.uiVisible = value; | |
| await GM_setValue(keyToSave, valueToSave); | |
| } | |
| // --- Style Generation Functions --- | |
| function generateCss() { | |
| // Spinner Fix CSS | |
| const spinnerCss = ` | |
| #${SETTINGS_PANEL_ID} input[type=number] { -moz-appearance: textfield !important; } | |
| #${SETTINGS_PANEL_ID} input[type=number]::-webkit-inner-spin-button, | |
| #${SETTINGS_PANEL_ID} input[type=number]::-webkit-outer-spin-button { | |
| -webkit-appearance: inner-spin-button !important; opacity: 1 !important; cursor: pointer; | |
| } | |
| `; | |
| // --- Main Panel Width --- | |
| // [FIX] Changed from setting 'max-width' to overriding the '--content-max-width' CSS variable to work with the new Grok UI. | |
| let mainWidthRule = !config.useDefaultMainWidth ? `${MAIN_PANEL_SELECTOR} { --content-max-width: ${config.mainWidthPx}px !important; }` : '/* Main width default */'; | |
| // --- Main Panel Justify --- | |
| let mainJustifyRule = config.mainJustifyEnabled ? `${MAIN_JUSTIFY_TARGET_SELECTOR} { text-align: justify !important; hyphens: auto; }` : '/* Main justify off */'; | |
| // --- Secondary Panel Width --- | |
| let secondaryWidthRule = !config.useDefaultSecondaryWidth ? `${SECONDARY_PANEL_SELECTOR} { max-width: ${config.secondaryWidthPx}px !important; }` : '/* Secondary width default */'; | |
| // --- Secondary Panel Justify --- | |
| let secondaryJustifyRule = config.secondaryJustifyEnabled ? `${SECONDARY_JUSTIFY_TARGET_SELECTOR} { text-align: justify !important; hyphens: auto; }` : '/* Secondary justify off */'; | |
| // Combine all rules | |
| return ` | |
| /* Grok Enhanced Styles v${SCRIPT_VERSION} */ | |
| ${spinnerCss} | |
| ${mainWidthRule} | |
| ${mainJustifyRule} | |
| ${secondaryWidthRule} | |
| ${secondaryJustifyRule} | |
| `; | |
| } | |
| // --- Style Injection / Update Function --- | |
| function applyStylesToAllRoots() { | |
| // Apply the combined CSS to head and all shadow roots | |
| const css = generateCss(); | |
| allStyleRoots.forEach(root => { if (root) injectOrUpdateStyle(root, STYLE_ID, css); }); | |
| } | |
| 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(); } } | |
| // --- UI State Update --- | |
| function updateUIState() { | |
| if (!settingsPanel || !document.body.contains(settingsPanel)) return; | |
| // Update Main Panel Controls | |
| if(useDefaultMainCheckbox && mainWidthSlider && mainWidthInput && mainWidthLabel && mainJustifyCheckbox) { | |
| useDefaultMainCheckbox.checked = config.useDefaultMainWidth; | |
| const isMainCustom = !config.useDefaultMainWidth; | |
| mainWidthSlider.disabled = !isMainCustom; mainWidthInput.disabled = !isMainCustom; | |
| mainWidthSlider.value = config.mainWidthPx; mainWidthInput.value = config.mainWidthPx; | |
| mainWidthLabel.textContent = `${config.mainWidthPx}px`; | |
| mainWidthLabel.style.opacity = isMainCustom ? 1 : 0.5; mainWidthSlider.style.opacity = isMainCustom ? 1 : 0.5; mainWidthInput.style.opacity = isMainCustom ? 1 : 0.5; | |
| mainJustifyCheckbox.checked = config.mainJustifyEnabled; | |
| } | |
| // Update Secondary Panel Controls | |
| if(useDefaultSecondaryCheckbox && secondaryWidthSlider && secondaryWidthInput && secondaryWidthLabel && secondaryJustifyCheckbox) { | |
| useDefaultSecondaryCheckbox.checked = config.useDefaultSecondaryWidth; | |
| const isSecondaryCustom = !config.useDefaultSecondaryWidth; | |
| secondaryWidthSlider.disabled = !isSecondaryCustom; secondaryWidthInput.disabled = !isSecondaryCustom; | |
| secondaryWidthSlider.value = config.secondaryWidthPx; secondaryWidthInput.value = config.secondaryWidthPx; | |
| secondaryWidthLabel.textContent = `${config.secondaryWidthPx}px`; | |
| secondaryWidthLabel.style.opacity = isSecondaryCustom ? 1 : 0.5; secondaryWidthSlider.style.opacity = isSecondaryCustom ? 1 : 0.5; secondaryWidthInput.style.opacity = isSecondaryCustom ? 1 : 0.5; | |
| secondaryJustifyCheckbox.checked = config.secondaryJustifyEnabled; | |
| } | |
| } | |
| // --- Click Outside Handler (Standard) --- | |
| 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(); } } } | |
| // --- UI Creation/Removal --- | |
| function removeSettingsUI() { | |
| document.removeEventListener('click', handleClickOutside, true); | |
| settingsPanel = document.getElementById(SETTINGS_PANEL_ID); | |
| if (settingsPanel) { | |
| settingsPanel.remove(); settingsPanel = null; | |
| mainWidthSlider=mainWidthLabel=mainWidthInput=useDefaultMainCheckbox=mainJustifyCheckbox=null; | |
| secondaryWidthSlider=secondaryWidthLabel=secondaryWidthInput=useDefaultSecondaryCheckbox=secondaryJustifyCheckbox=null; | |
| } | |
| document.removeEventListener('click', handleClickOutside, true); | |
| } | |
| function createSettingsUI() { | |
| if (document.getElementById(SETTINGS_PANEL_ID) || !config.uiVisible) return; | |
| const body = document.body || document.getElementsByTagName('body')[0]; | |
| if (!body) { console.error("[Grok Enhanced] Cannot find body."); return; } | |
| document.removeEventListener('click', handleClickOutside, true); | |
| settingsPanel = document.createElement('div'); | |
| settingsPanel.id = SETTINGS_PANEL_ID; | |
| 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' }); | |
| // --- Header --- | |
| 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); | |
| // --- Main Panel Width Section --- | |
| const mainWidthSection = document.createElement('div'); mainWidthSection.style.marginTop = '10px'; mainWidthSection.style.marginBottom = '15px'; | |
| const mainSectionTitle = document.createElement('div'); mainSectionTitle.textContent = 'Main Panel'; mainSectionTitle.style.fontWeight = 'bold'; mainSectionTitle.style.marginBottom = '8px'; mainSectionTitle.style.fontSize = '0.9em'; | |
| const mainDefaultDiv = document.createElement('div'); mainDefaultDiv.style.marginBottom = '8px'; | |
| useDefaultMainCheckbox = document.createElement('input'); useDefaultMainCheckbox.type = 'checkbox'; useDefaultMainCheckbox.id = 'grok-main-default-toggle'; | |
| 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'; | |
| mainDefaultDiv.appendChild(useDefaultMainCheckbox); mainDefaultDiv.appendChild(mainDefaultLabel); | |
| const mainSliderInputDiv = document.createElement('div'); mainSliderInputDiv.style.display = 'flex'; mainSliderInputDiv.style.alignItems = 'center'; mainSliderInputDiv.style.gap = '10px'; mainSliderInputDiv.style.marginBottom = '8px'; | |
| mainWidthLabel = document.createElement('span'); mainWidthLabel.style.cssText = 'min-width: 50px; font-family: monospace; text-align: right;'; | |
| 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;'; | |
| 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;'; | |
| mainSliderInputDiv.appendChild(mainWidthLabel); mainSliderInputDiv.appendChild(mainWidthSlider); mainSliderInputDiv.appendChild(mainWidthInput); | |
| const mainJustifyDiv = document.createElement('div'); | |
| mainJustifyCheckbox = document.createElement('input'); mainJustifyCheckbox.type = 'checkbox'; mainJustifyCheckbox.id = 'grok-main-justify-toggle'; | |
| 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'; | |
| mainJustifyDiv.appendChild(mainJustifyCheckbox); mainJustifyDiv.appendChild(mainJustifyLabel); | |
| mainWidthSection.appendChild(mainSectionTitle); mainWidthSection.appendChild(mainDefaultDiv); mainWidthSection.appendChild(mainSliderInputDiv); mainWidthSection.appendChild(mainJustifyDiv); | |
| settingsPanel.appendChild(mainWidthSection); | |
| // --- Secondary Panel Width Section --- | |
| const secondaryWidthSection = document.createElement('div'); secondaryWidthSection.style.borderTop = '1px dashed #565869'; secondaryWidthSection.style.paddingTop = '15px'; secondaryWidthSection.style.marginTop = '15px'; secondaryWidthSection.style.marginBottom = '15px'; | |
| const secondarySectionTitle = document.createElement('div'); secondarySectionTitle.textContent = 'Secondary Panel'; secondarySectionTitle.style.fontWeight = 'bold'; secondarySectionTitle.style.marginBottom = '8px'; secondarySectionTitle.style.fontSize = '0.9em'; | |
| const secondaryDefaultDiv = document.createElement('div'); secondaryDefaultDiv.style.marginBottom = '8px'; | |
| useDefaultSecondaryCheckbox = document.createElement('input'); useDefaultSecondaryCheckbox.type = 'checkbox'; useDefaultSecondaryCheckbox.id = 'grok-secondary-default-toggle'; | |
| 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'; | |
| secondaryDefaultDiv.appendChild(useDefaultSecondaryCheckbox); secondaryDefaultDiv.appendChild(secondaryDefaultLabel); | |
| const secondarySliderInputDiv = document.createElement('div'); secondarySliderInputDiv.style.display = 'flex'; secondarySliderInputDiv.style.alignItems = 'center'; secondarySliderInputDiv.style.gap = '10px'; secondarySliderInputDiv.style.marginBottom = '8px'; | |
| secondaryWidthLabel = document.createElement('span'); secondaryWidthLabel.style.cssText = 'min-width: 50px; font-family: monospace; text-align: right;'; | |
| 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;'; | |
| 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;'; | |
| secondarySliderInputDiv.appendChild(secondaryWidthLabel); secondarySliderInputDiv.appendChild(secondaryWidthSlider); secondarySliderInputDiv.appendChild(secondaryWidthInput); | |
| const secondaryJustifyDiv = document.createElement('div'); | |
| secondaryJustifyCheckbox = document.createElement('input'); secondaryJustifyCheckbox.type = 'checkbox'; secondaryJustifyCheckbox.id = 'grok-secondary-justify-toggle'; | |
| 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'; | |
| secondaryJustifyDiv.appendChild(secondaryJustifyCheckbox); secondaryJustifyDiv.appendChild(secondaryJustifyLabel); | |
| secondaryWidthSection.appendChild(secondarySectionTitle); secondaryWidthSection.appendChild(secondaryDefaultDiv); secondaryWidthSection.appendChild(secondarySliderInputDiv); secondaryWidthSection.appendChild(secondaryJustifyDiv); | |
| settingsPanel.appendChild(secondaryWidthSection); | |
| body.appendChild(settingsPanel); | |
| // --- Event Listeners --- | |
| // Main Panel | |
| useDefaultMainCheckbox.addEventListener('change', async (e) => { await saveSetting(USE_DEFAULT_MAIN_WIDTH_KEY, e.target.checked); applyStylesToAllRoots(); updateUIState(); }); | |
| 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(); }); | |
| mainWidthSlider.addEventListener('change', async (e) => { if (config.useDefaultMainWidth) return; const fw=parseInt(e.target.value,10); await saveSetting(MAIN_WIDTH_PX_KEY, fw); }); | |
| 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(); }); | |
| 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(); }); | |
| mainJustifyCheckbox.addEventListener('change', async (e) => { await saveSetting(MAIN_JUSTIFY_KEY, e.target.checked); applyStylesToAllRoots(); }); | |
| // Secondary Panel | |
| useDefaultSecondaryCheckbox.addEventListener('change', async (e) => { await saveSetting(USE_DEFAULT_SECONDARY_WIDTH_KEY, e.target.checked); applyStylesToAllRoots(); updateUIState(); }); | |
| 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(); }); | |
| secondaryWidthSlider.addEventListener('change', async (e) => { if (config.useDefaultSecondaryWidth) return; const fw=parseInt(e.target.value,10); await saveSetting(SECONDARY_WIDTH_PX_KEY, fw); }); | |
| 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(); }); | |
| 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(); }); | |
| secondaryJustifyCheckbox.addEventListener('change', async (e) => { await saveSetting(SECONDARY_JUSTIFY_KEY, e.target.checked); applyStylesToAllRoots(); }); | |
| // --- Final UI Setup --- | |
| updateUIState(); | |
| setTimeout(() => { if (document) document.addEventListener('click', handleClickOutside, true); }, 0); | |
| if (document.head) injectOrUpdateStyle(document.head, STYLE_ID, generateCss()); | |
| } | |
| // --- Tampermonkey Menu (Standard) --- | |
| 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(); }); } | |
| // --- Shadow DOM Handling (Standard) --- | |
| function getShadowRoot(element) { try { return element.shadowRoot; } catch (e) { return null; } } | |
| 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; } | |
| // --- Initialization (Standard) --- | |
| console.log('[Grok Enhanced] Script starting (run-at=document-end)...'); | |
| if (document.head) allStyleRoots.add(document.head); else { const rootNode = document.documentElement || document; allStyleRoots.add(rootNode); console.warn("[Grok Enhanced] document.head not found."); } | |
| await loadSettings(); | |
| applyStylesToAllRoots(); | |
| 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.`); | |
| if (config.uiVisible) createSettingsUI(); | |
| updateTampermonkeyMenu(); | |
| 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); } }); }); }); | |
| observer.observe(document.documentElement || document.body || document, { childList: true, subtree: true }); | |
| console.log('[Grok Enhanced] Initialization complete.'); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment