Last active
December 7, 2025 16:22
-
-
Save RVZO6/c6c1a3ed69d46afeff7d0ac7503296bd to your computer and use it in GitHub Desktop.
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 YouTube Focus Mode (Zen Workspaces) | |
| // @description Minimalist blocking. No distractions. | |
| // @version 3.0 | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| // settings | |
| const PREFS = { | |
| workspaces: 'youtube.focus.blocked_workspaces', | |
| msg: 'youtube.focus.message', | |
| color: 'youtube.focus.text_color', | |
| color_dark: 'youtube.focus.text_color_dark', | |
| size: 'youtube.focus.font_size', | |
| weight: 'youtube.focus.font_weight', | |
| font: 'youtube.focus.font_family', | |
| }; | |
| const DEFAULTS = { | |
| workspaces: 'Work', | |
| msg: 'lock in brochacho', | |
| color: '#000', | |
| color_dark: '#FFF', | |
| size: '24px', | |
| weight: '400', | |
| font: 'Comic Sans MS, system-ui, sans-serif', | |
| }; | |
| // the css | |
| const CSS_STATIC = ` | |
| :root { | |
| --zf-msg: ""; --zf-color: #555; --zf-color-dark: #e0e0e0; | |
| --zf-size: 24px; --zf-weight: 400; --zf-font: system-ui; | |
| } | |
| /* hide distractions */ | |
| body.zen-active #secondary, | |
| body.zen-active #related, | |
| body.zen-active #comments, | |
| body.zen-active ytd-reel-shelf-renderer, | |
| body.zen-active ytd-shorts, | |
| body.zen-active .ytp-endscreen-content, | |
| body.zen-active .ytp-ce-element, | |
| body.zen-active ytd-browse[page-subtype="home"] #contents { | |
| display: none !important; | |
| } | |
| /* show message (home only) */ | |
| body.zen-active ytd-browse[page-subtype="home"]::after { | |
| content: var(--zf-msg); | |
| position: fixed; top: 50%; left: 50%; | |
| transform: translate(-50%, -50%); | |
| color: var(--zf-color); | |
| font-size: var(--zf-size); | |
| font-weight: var(--zf-weight); | |
| font-family: var(--zf-font); | |
| z-index: 9999; pointer-events: none; | |
| text-align: center; white-space: pre-wrap; | |
| } | |
| /* dark mode */ | |
| html[dark] body.zen-active ytd-browse[page-subtype="home"]::after { | |
| color: var(--zf-color-dark); | |
| } | |
| `; | |
| // helpers | |
| const getPref = (k) => { | |
| try { return Services.prefs.getStringPref(PREFS[k]); } | |
| catch { return DEFAULTS[k]; } | |
| }; | |
| const shouldBlock = () => { | |
| const current = window.gZenWorkspaces?.getActiveWorkspaceFromCache()?.name?.toLowerCase(); | |
| return current && getPref('workspaces').toLowerCase().split(',').map(s => s.trim()).includes(current); | |
| }; | |
| // frame script | |
| const FRAME_SCRIPT = ` | |
| (function() { | |
| const STYLE_ID = 'zen-focus-style'; | |
| const CSS = ${JSON.stringify(CSS_STATIC)}; | |
| function update(data) { | |
| if (!content?.location?.host?.includes('youtube.com') || !content?.document?.body) return; | |
| const doc = content.document; | |
| if (!doc.getElementById(STYLE_ID)) { | |
| const s = doc.createElement('style'); | |
| s.id = STYLE_ID; s.textContent = CSS; | |
| doc.head.appendChild(s); | |
| } | |
| const s = doc.documentElement.style; | |
| s.setProperty('--zf-msg', JSON.stringify(data.cfg.msg)); | |
| s.setProperty('--zf-color', data.cfg.color); | |
| s.setProperty('--zf-color-dark', data.cfg.color_dark); | |
| s.setProperty('--zf-size', data.cfg.size); | |
| s.setProperty('--zf-weight', data.cfg.weight); | |
| s.setProperty('--zf-font', data.cfg.font); | |
| doc.body.classList.toggle('zen-active', data.active); | |
| } | |
| addMessageListener('YouTubeFocus:Update', m => update(m.data)); | |
| const notify = () => { | |
| if (content?.location?.host?.includes('youtube.com')) sendAsyncMessage('YouTubeFocus:Ready'); | |
| }; | |
| addEventListener('DOMContentLoaded', notify, true); | |
| addEventListener('yt-navigate-finish', notify, true); | |
| })(); | |
| `; | |
| // main process | |
| const pushState = (browser) => { | |
| if (!browser?.messageManager || !browser.currentURI?.host?.includes('youtube.com')) return; | |
| const cfg = { | |
| msg: getPref('msg'), color: getPref('color'), color_dark: getPref('color_dark'), | |
| size: getPref('size'), weight: getPref('weight'), font: getPref('font'), | |
| }; | |
| browser.messageManager.sendAsyncMessage('YouTubeFocus:Update', { active: shouldBlock(), cfg }); | |
| }; | |
| const broadcast = () => { | |
| if (gBrowser) gBrowser.tabs.forEach(tab => pushState(tab.linkedBrowser)); | |
| }; | |
| async function init() { | |
| Object.entries(PREFS).forEach(([k, v]) => { | |
| if (!Services.prefs.prefHasUserValue(v)) Services.prefs.setStringPref(v, DEFAULTS[k]); | |
| Services.prefs.addObserver(v, broadcast); | |
| }); | |
| while (!window.gZenWorkspaces) await new Promise(r => setTimeout(r, 100)); | |
| window.messageManager.loadFrameScript('data:,' + encodeURIComponent(FRAME_SCRIPT), true); | |
| window.messageManager.addMessageListener('YouTubeFocus:Ready', m => pushState(m.target)); | |
| window.addEventListener('ZenWorkspacesUIUpdate', broadcast); | |
| gBrowser.tabContainer.addEventListener('TabSelect', () => pushState(gBrowser.selectedBrowser)); | |
| broadcast(); | |
| } | |
| if (document.readyState === 'complete') init(); | |
| else window.addEventListener('load', init, { once: true }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment