diff --git a/Youtube_Minimal_Plus.user.js b/Youtube_Minimal_Plus.user.js new file mode 100644 index 0000000..7bd6e32 --- /dev/null +++ b/Youtube_Minimal_Plus.user.js @@ -0,0 +1,324 @@ +// ==UserScript== +// @name Minimal YouTube +// @version 1.0.0 +// @description A minimal YouTube experience with a customizable interface. +// @run-at document-start +// @match https://www.youtube.com/* +// @exclude https://*.youtube.com/live_chat* +// @exclude https://*.youtube.com/embed* +// @exclude https://*.youtube.com/tv* +// @grant GM.getValue +// @grant GM.setValue +// @updateURL https://github.com/fznhq/userscript-collection/raw/main/Youtube_Minimal_Plus.user.js +// @downloadURL https://github.com/fznhq/userscript-collection/raw/main/Youtube_Minimal_Plus.user.js +// @author Vishwamz (https://github.com/Vishwamz) +// @collaborator Fznhq +// @namespace https://github.com/fznhq +// @homepageURL https://github.com/fznhq/userscript-collection +// @homepage https://github.com/fznhq/userscript-collection +// @license GNU GPLv3 +// ==/UserScript== + +(function () { + "use strict"; + + const options = { + minimal_homepage: { + label: "Minimal Homepage", + value: true, + icon: ``, + }, + hide_comments: { + label: "Hide Comments", + value: true, + icon: ``, + }, + hide_secondary: { + label: "Hide Recommendations", + value: true, + icon: ``, + }, + hide_related: { + label: "Hide Related Videos", + value: true, + icon: ``, + }, + hide_sidebar: { + label: "Hide Sidebar", + value: true, + icon: ``, + }, + hide_top_bar_buttons: { + label: "Hide Top Bar Buttons", + value: true, + icon: ``, + }, + }; + + const policyOptions = { createHTML: (html) => html.trim() }; + const policy = window.trustedTypes + ? window.trustedTypes.createPolicy("ytmp-policy", policyOptions) + : policyOptions; + + function create(html) { + const container = document.createElement("div"); + container.innerHTML = policy.createHTML(html.replace(/>\s+<")); + return container.firstElementChild; + } + + const menuItems = {}; + const popup = createPopup(); + const homeContainer = createHomeContainer(); + + async function loadOptions() { + for (const key in options) { + options[key].value = await GM.getValue(key, options[key].value); + } + } + + function saveOption(key, value) { + options[key].value = value; + GM.setValue(key, value); + } + + const prefix = "ytmp-"; + const attrId = "-" + Date.now().toString(36).slice(-4); + const attr = { + is_homepage: "is-homepage", + minimal_homepage: "minimal-homepage", + hide_comments: "hide-comments", + hide_secondary: "hide-secondary", + hide_related: "hide-related", + hide_sidebar: "hide-sidebar", + hide_top_bar_buttons: "hide-top-bar-buttons", + }; + + function setHtmlAttr(attrName, state) { + document.documentElement.toggleAttribute( + prefix + attrName + attrId, + state + ); + } + + function applyStyles() { + setHtmlAttr(attr.is_homepage, window.location.pathname === "/"); + setHtmlAttr(attr.minimal_homepage, options.minimal_homepage.value); + setHtmlAttr(attr.hide_comments, options.hide_comments.value); + setHtmlAttr(attr.hide_secondary, options.hide_secondary.value); + setHtmlAttr(attr.hide_related, options.hide_related.value); + setHtmlAttr(attr.hide_sidebar, options.hide_sidebar.value); + setHtmlAttr( + attr.hide_top_bar_buttons, + options.hide_top_bar_buttons.value + ); + } + + function createMenuItem(key) { + const option = options[key]; + const item = create(/*html*/ ` +
+
${option.icon}
+
${option.label}
+
+
+
+
+ `); + item.addEventListener("click", () => { + const value = !option.value; + item.setAttribute("aria-checked", value); + saveOption(key, value); + applyStyles(); + showToast(`${option.label} ${value ? "enabled" : "disabled"}`); + }); + return item; + } + + function createPopup() { + const container = create(/*html*/ ` + + `); + const menu = container.querySelector(".yt-minimal-menu"); + + for (const key in options) { + menuItems[key] = menu.appendChild(createMenuItem(key)); + } + + container.addEventListener("click", (e) => { + if (e.target.contains(container)) togglePopup(); + }); + + window.addEventListener("keydown", (e) => { + if (e.key === "Escape" && document.body.contains(container)) { + e.preventDefault(); + togglePopup(); + } + }); + + return container; + } + + function togglePopup() { + const body = document.body; + if (body.contains(popup)) popup.remove(); + else body.append(popup); + } + + function createHomeContainer() { + const container = create(/*html*/ ` +
+
+ +
+
+
+ + +
+
+
+ `); + + const input = container.querySelector("input"); + const form = container.querySelector("form"); + const submit = create(``); + + function search(e) { + e.preventDefault(); + const value = input.value.trim(); + if (!value) return showToast("Enter a search term"); + + const searchForm = document.querySelector("form[action*=result]"); + const searchInput = searchForm.querySelector("input"); + searchInput.value = value; + searchForm.appendChild(submit).click(); + } + + form.addEventListener("submit", search); + return container; + } + + const toast = create(`
`); + + function showToast(message) { + toast.textContent = message; + document.body.append(toast); + setTimeout(() => toast.remove(), 2000); + } + + function isActiveEditable() { + const active = document.activeElement; + return ( + active.tagName === "TEXTAREA" || + active.tagName === "INPUT" || + active.isContentEditable + ); + } + + function initCSS() { + const css = document.head.appendChild(document.createElement("style")); + css.textContent = /* css */ ` + /* Popup Styles */ + .yt-minimal-popup-container { display: flex; justify-content: center; align-items: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); z-index: 9999; } + .yt-minimal-menu.ytp-panel-menu { background: var(--yt-spec-base-background, #181818); width: 400px; padding: 10px; fill: var(--yt-spec-text-primary, #eee); font-family: "Roboto", "Arial", sans-serif; border-radius: 12px; } + .ytp-menuitem { display: flex; align-items: center; padding: 12px; cursor: pointer; } + .ytp-menuitem:hover { background-color: var(--yt-spec-10-percent-layer, #383838); } + .ytp-menuitem-icon { width: 24px; height: 24px; margin-right: 16px; } + .ytp-menuitem-label { color: var(--yt-spec-text-primary, #eee); font-size: 14px; flex-grow: 1; } + .ytp-menuitem-content { margin-left: auto; } + .ytp-menuitem-toggle-checkbox { background-color: var(--yt-spec-text-disabled, rgb(144, 144, 144)); border-radius: 12px; height: 16px; width: 32px; position: relative; } + .ytp-menuitem-toggle-checkbox::after { background-color: var(--yt-spec-base-background, #181818); border-radius: 50%; content: ""; height: 24px; width: 24px; position: absolute; top: -4px; left: -2px; transition: left 0.1s cubic-bezier(0.4, 0, 1, 1); } + .ytp-menuitem[aria-checked=true] .ytp-menuitem-toggle-checkbox { background-color: var(--yt-spec-call-to-action, #6ea8ff); } + .ytp-menuitem[aria-checked=true] .ytp-menuitem-toggle-checkbox::after { left: 10px; } + + /* Minimal YouTube Styles */ + html[is-homepage][minimal-homepage], html[is-homepage][minimal-homepage] body { overflow: hidden !important; height: 100vh; background-color: var(--yt-spec-base-background, #0f0f0f); } + html[is-homepage][minimal-homepage] ytd-app > :not(.home-container):not(yt-page-navigation-progress) { display: none !important; } + html[is-homepage][minimal-homepage] .home-container { display: flex !important; } + .home-container { display: none; flex-direction: column; gap: 24px; align-items: center; justify-content: center; height: 100%; position: absolute; top: 0; left: 0; width: 100%; background: var(--yt-spec-base-background, #0f0f0f); z-index: 1000; } + .logo-full { width: 200px; margin-bottom: 20px; color: var(--yt-spec-wordmark-text); } + .search-group { height: 40px; display: flex; position: relative; } + .search-input { font-size: 16px; padding: 8px 16px; max-width: 500px; width: calc(100vw - 100px); border-radius: 40px 0 0 40px; border: 1px solid var(--yt-spec-10-percent-layer, #303030); background: var(--ytd-searchbox-background, #121212); color: var(--ytd-searchbox-text-color, white); } + .search-input:focus { outline: none; border-color: var(--yt-spec-call-to-action, #1c62b9); } + .search-btn { border: 1px solid var(--yt-spec-10-percent-layer, #303030); border-left: none; height: 40px; cursor: pointer; border-radius: 0 40px 40px 0; padding: 0 24px; background: var(--ytd-searchbox-legacy-button-color, #222222); } + .search-icon-container { height: 24px; width: 24px; fill: var(--yt-spec-icon-active-other, #909090); } + + /* Hide element styles */ + html[hide-comments] #comments { display: none !important; } + html[hide-secondary] #secondary { display: none !important; } + html[hide-related] #related { display: none !important; } + html[hide-sidebar] #guide, html[hide-sidebar] tp-yt-app-drawer, html[hide-sidebar] ytd-mini-guide-renderer { display: none !important; } + html[hide-top-bar-buttons] #guide-button, html[hide-top-bar-buttons] #voice-search-button, html[hide-top-bar-buttons] .ytd-masthead #end #buttons { display: none !important; } + /* FIX: Reclaim sidebar space */ + html[hide-sidebar] ytd-page-manager.ytd-app { margin-left: 0 !important; } + + /* FIX: Overlapping Playlist */ + html[hide-sidebar] ytd-browse[role=main] ytd-playlist-header-renderer:not([hidden]), + html[hide-sidebar] ytd-browse[role=main] yt-page-header-renderer:not([hidden]) { left: 0 !important; } + + /* Toast */ + .yt-minimal-toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: var(--yt-spec-base-background, #181818); color: var(--yt-spec-text-primary, #eee); padding: 10px 20px; border-radius: 4px; z-index: 10000; opacity: 0.9; transition: opacity 0.3s; } + + /* Responsive */ + @media (max-width: 600px) { + .logo-full { width: 150px; } + .search-input { width: 100%; max-width: none; border-radius: 40px 40px 0 0; } + .search-btn { border-radius: 0 0 40px 40px; border-top: none; width: 100%; padding: 8px; } + .search-group { flex-direction: column; height: auto; } + } + `; + + for (const key in attr) { + css.textContent = css.textContent.replaceAll( + "[" + attr[key] + "]", + "[" + prefix + attr[key] + attrId + "]" + ); + } + } + + function init() { + initCSS(); + + function keyTogglePopup(e) { + if ( + !e.ctrlKey && + e.key.toLowerCase() === "b" && + !isActiveEditable() + ) { + togglePopup(); + } + } + + async function syncOptions() { + await loadOptions(); + applyStyles(); + for (const key in options) { + menuItems[key].setAttribute("aria-checked", options[key].value); + } + } + + loadOptions().then(() => { + applyStyles(); + window.addEventListener("keydown", keyTogglePopup, true); + window.addEventListener("yt-navigate-finish", applyStyles); + window.addEventListener("popstate", applyStyles); + window.addEventListener("focus", syncOptions); + }); + } + + new MutationObserver((_, observe) => { + const app = document.querySelector("ytd-app"); + + if (app) { + observe.disconnect(); + document.body.append(homeContainer); + init(); + } + }).observe(document, { subtree: true, childList: true }); +})();