Blurs sensitive information to protect your privacy on grok.com.
// ==UserScript==
// @name Grok Streamer Mode
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Blurs sensitive information to protect your privacy on grok.com.
// @author Ported by Blanklspeaker, Adapted from original plugin by Prism (https://github.com/imjustprism/Grokness)
// @match https://grok.com/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const logger = {
error: (...args) => console.error('[StreamerMode]', ...args)
};
let styleElement = null;
const baseStyles = `
.streamer-mode-active img.aspect-square.h-full.w-full[alt="pfp"] {
filter: blur(8px) !important;
}
.streamer-mode-active .sidebar-user-info .display-name,
.streamer-mode-active [data-sidebar="menu"] .group\\/conversation-item span.flex-1.select-none,
.streamer-mode-active span.flex-1.select-none.text-nowrap.max-w-full.overflow-hidden.inline-block {
filter: blur(5px) !important;
padding: 0 4px !important;
margin: 0 -4px !important;
display: inline-block !important;
width: calc(100% + 8px) !important;
position: relative !important;
}
.streamer-mode-active .p-1.min-w-0.text-sm .text-sm.font-medium,
.streamer-mode-active .p-1.min-w-0.text-sm .text-secondary.truncate {
filter: blur(5px) !important;
padding: 0 4px !important;
margin: 0 -4px !important;
display: inline-block !important;
width: calc(100% + 8px) !important;
position: relative !important;
}
.streamer-mode-active div.text-xs.flex.flex-row.gap-1.p-3.text-secondary.opacity-30 {
filter: blur(5px) !important;
padding: 0 4px !important;
margin: 0 -4px !important;
display: inline-block !important;
width: calc(100% + 8px) !important;
position: relative !important;
}
.streamer-mode-active #grok-feature-flags-menu-item {
filter: blur(5px) !important;
}
`;
function getDynamicStyles(emailOnly) {
if (emailOnly) {
return `
html.streamer-mode-active .p-1.min-w-0.text-sm .text-secondary.truncate {
filter: blur(5px) !important;
padding: 0 4px !important;
margin: 0 -4px !important;
display: inline-block !important;
width: calc(100% + 8px) !important;
position: relative !important;
}
html.streamer-mode-active div.text-xs.flex.flex-row.gap-1.p-3.text-secondary.opacity-30 {
filter: blur(5px) !important;
padding: 0 4px !important;
margin: 0 -4px !important;
display: inline-block !important;
width: calc(100% + 8px) !important;
position: relative !important;
}
`;
}
return baseStyles.replace(/\.streamer-mode-active/g, 'html.streamer-mode-active');
}
function updateStylesheetAndClass() {
try {
const isEnabled = GM_getValue('enabled', true);
const emailOnly = GM_getValue('emailOnly', false);
if (styleElement) {
styleElement.remove();
styleElement = null;
}
document.documentElement.classList.remove('streamer-mode-active');
let dynamicStyles = '';
if (emailOnly) {
dynamicStyles = getDynamicStyles(true);
document.documentElement.classList.add('streamer-mode-active');
} else if (isEnabled) {
dynamicStyles = getDynamicStyles(false);
document.documentElement.classList.add('streamer-mode-active');
}
if (dynamicStyles) {
styleElement = GM_addStyle(dynamicStyles);
}
} catch (err) {
logger.error('Failed to update stylesheet or class:', err);
}
}
function createToggle(initialChecked, labelId) {
const button = document.createElement('button');
button.type = 'button';
button.role = 'switch';
button.className = 'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-[1px] border-transparent transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background ring-card-border ring-1 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-dove dark:data-[state=checked]:bg-ivory dark:data-[state=unchecked]:bg-button-secondary-selected';
button.value = 'on';
if (labelId) {
button.setAttribute('aria-labelledby', labelId);
}
const span = document.createElement('span');
span.className = 'pointer-events-none block h-4 w-4 rounded-full bg-white dark:bg-background shadow-lg ring-0 transition-transform data-[state=unchecked]:translate-x-0 ms-0.5 data-[state=checked]:translate-x-5 rtl:data-[state=checked]:-translate-x-5 dark:data-[state=unchecked]:bg-overlay';
button.appendChild(span);
function setChecked(checked) {
button.setAttribute('aria-checked', checked ? 'true' : 'false');
button.dataset.state = checked ? 'checked' : 'unchecked';
span.dataset.state = checked ? 'checked' : 'unchecked';
}
setChecked(initialChecked);
button.addEventListener('click', () => {
const current = button.getAttribute('aria-checked') === 'true';
setChecked(!current);
});
return button;
}
function injectSettings() {
const targetLabelText = 'Show Conversation Previews in History';
const observer = new MutationObserver((mutations) => {
const labels = document.querySelectorAll('div.text-sm.font-medium');
for (let label of labels) {
if (label.innerText.trim() === targetLabelText) {
const labelWrapper = label.parentElement; // max-w-sm min-w-0
const previewRow = labelWrapper.parentElement; // flex flex-row ...
if (previewRow && !document.getElementById('streamer-mode-row')) {
const container = previewRow.parentElement; // flex-col gap-6
// Create row for Streamer Mode
const streamerRow = document.createElement('div');
streamerRow.className = previewRow.className;
streamerRow.id = 'streamer-mode-row';
const streamerLabelWrapper = document.createElement('div');
streamerLabelWrapper.className = labelWrapper.className;
const streamerLabelId = 'streamer-mode-label';
streamerLabelWrapper.id = streamerLabelId;
const streamerLabel = document.createElement('div');
streamerLabel.className = 'text-sm font-medium';
streamerLabel.innerText = 'Streamer Mode';
streamerLabelWrapper.appendChild(streamerLabel);
streamerRow.appendChild(streamerLabelWrapper);
const streamerToggleWrapper = document.createElement('div');
streamerToggleWrapper.className = 'text-right min-w-24';
const streamerToggle = createToggle(GM_getValue('enabled', true), streamerLabelId);
streamerToggle.addEventListener('click', () => {
const checked = streamerToggle.getAttribute('aria-checked') === 'true';
GM_setValue('enabled', checked);
updateStylesheetAndClass();
});
streamerToggleWrapper.appendChild(streamerToggle);
streamerRow.appendChild(streamerToggleWrapper);
container.insertBefore(streamerRow, previewRow.nextSibling);
// Create row for Blur Email Only
const emailRow = document.createElement('div');
emailRow.className = previewRow.className;
emailRow.id = 'email-only-row';
const emailLabelWrapper = document.createElement('div');
emailLabelWrapper.className = labelWrapper.className;
const emailLabelId = 'email-only-label';
emailLabelWrapper.id = emailLabelId;
const emailLabel = document.createElement('div');
emailLabel.className = 'text-sm font-medium';
emailLabel.innerText = 'Blur Email Only';
emailLabelWrapper.appendChild(emailLabel);
emailRow.appendChild(emailLabelWrapper);
const emailToggleWrapper = document.createElement('div');
emailToggleWrapper.className = 'text-right min-w-24';
const emailToggle = createToggle(GM_getValue('emailOnly', false), emailLabelId);
emailToggle.addEventListener('click', () => {
const checked = emailToggle.getAttribute('aria-checked') === 'true';
GM_setValue('emailOnly', checked);
updateStylesheetAndClass();
});
emailToggleWrapper.appendChild(emailToggle);
emailRow.appendChild(emailToggleWrapper);
container.insertBefore(emailRow, streamerRow.nextSibling);
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
// Run the update function and inject settings
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
updateStylesheetAndClass();
injectSettings();
}, { once: true });
} else {
updateStylesheetAndClass();
injectSettings();
}
// Expose functions to toggle and configure via console
window.toggleStreamerMode = function() {
const currentEnabled = GM_getValue('enabled', true);
GM_setValue('enabled', !currentEnabled);
updateStylesheetAndClass();
console.log('[StreamerMode] Enabled:', !currentEnabled);
};
window.setStreamerModeEmailOnly = function(value) {
GM_setValue('emailOnly', !!value);
updateStylesheetAndClass();
console.log('[StreamerMode] Email Only:', !!value);
};
console.log('[StreamerMode] Loaded. Use toggleStreamerMode() in console to toggle on/off. Use setStreamerModeEmailOnly(true/false) to toggle blurring only email.');
})();