├── .gitignore ├── assets ├── icon.png ├── under-18.png ├── watch-page.png ├── icons │ ├── logo-128.png │ ├── logo-16.png │ ├── logo-32.png │ ├── logo-34.png │ └── logo-48.png ├── shortstab-removed.png ├── watch-page-focussed.png └── installation │ ├── loaded-image.png │ ├── developer-mode.png │ ├── load-unpacked.png │ └── clear-space-zip.png ├── src ├── js │ ├── messaging.js │ ├── popup.js │ ├── block-features.js │ └── block-websites.js ├── html │ ├── redirect.html │ ├── popup.html │ ├── siteblockmanager.html │ └── blocked-features.html ├── styles │ ├── redirect.css │ ├── popup.css │ ├── siteblockmanager.css │ └── blocked-features.css └── scripts │ ├── background.js │ └── main.js ├── docs └── installation.md ├── LICENSE ├── manifest.json ├── contributing.md └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /_metadata/ 2 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/under-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/under-18.png -------------------------------------------------------------------------------- /assets/watch-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/watch-page.png -------------------------------------------------------------------------------- /assets/icons/logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icons/logo-128.png -------------------------------------------------------------------------------- /assets/icons/logo-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icons/logo-16.png -------------------------------------------------------------------------------- /assets/icons/logo-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icons/logo-32.png -------------------------------------------------------------------------------- /assets/icons/logo-34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icons/logo-34.png -------------------------------------------------------------------------------- /assets/icons/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/icons/logo-48.png -------------------------------------------------------------------------------- /assets/shortstab-removed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/shortstab-removed.png -------------------------------------------------------------------------------- /assets/watch-page-focussed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/watch-page-focussed.png -------------------------------------------------------------------------------- /src/js/messaging.js: -------------------------------------------------------------------------------- 1 | export const GET_ADULT_WEBSITES = 'getAdultWebsites'; 2 | export const SETTINGS_UPDATED = 'settingsUpdated'; -------------------------------------------------------------------------------- /assets/installation/loaded-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/installation/loaded-image.png -------------------------------------------------------------------------------- /assets/installation/developer-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/installation/developer-mode.png -------------------------------------------------------------------------------- /assets/installation/load-unpacked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/installation/load-unpacked.png -------------------------------------------------------------------------------- /assets/installation/clear-space-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Santhoshmani1/Clear-Space/HEAD/assets/installation/clear-space-zip.png -------------------------------------------------------------------------------- /src/js/popup.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', () => { 2 | console.log('Popup script loaded'); 3 | 4 | const blockedFeaturesLink = document.querySelector('#blocked-features a'); 5 | const blockedSitesLink = document.querySelector('#blocked-sites a'); 6 | 7 | function openInNewTab(event) { 8 | event.preventDefault(); 9 | const url = event.currentTarget.href; 10 | chrome.tabs.create({ url }); 11 | } 12 | 13 | if (blockedFeaturesLink) { 14 | blockedFeaturesLink.addEventListener('click', openInNewTab); 15 | } 16 | 17 | if (blockedSitesLink) { 18 | blockedSitesLink.addEventListener('click', openInNewTab); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /src/html/redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ClearSpace: You have been redirected 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

ClearSpace: You have been redirected!

16 |

17 | Time to stay Focussed 18 | verified 19 |

20 |

21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /src/styles/redirect.css: -------------------------------------------------------------------------------- 1 | body.clearspace-redirect-body { 2 | background-color: black; 3 | margin: 0; 4 | padding: 0; 5 | font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", 6 | "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 7 | } 8 | 9 | div.clearspace-redirect-container { 10 | width: 600px; 11 | margin: 5em auto; 12 | padding: 2em; 13 | background-color: #1e1e1e; 14 | border-radius: 0.5em; 15 | box-shadow: 2px 3px 7px 2px rgba(0, 0, 0, 0.02); 16 | display: flex; 17 | flex-direction: column; 18 | align-items: center; 19 | } 20 | 21 | h1#clearspace-main-heading { 22 | color: #069aed; 23 | font-size: 30px; 24 | line-height: 12px; 25 | } 26 | 27 | p#clearspace-stay-focussed { 28 | width: 80%; 29 | text-align: center; 30 | font-size: 1.4rem; 31 | color: white; 32 | } 33 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | **Clear space is currently submitted for review on chrome web store and will be available to users soon.** 4 | 5 | ### Installation on Chromium-based browsers 6 | 7 | To install Clear Space in your browser, follow the steps mentioned below : 8 | 9 | 1. Download [zip folder](https://github.com/Santhoshmani1/Clear-Space/archive/refs/heads/main.zip) of the repository and extract the file contents. 10 | 11 | ![Clear-space-zip](../assets/installation/clear-space-zip.png) 12 | 13 | 2. Go to [browser://extensions](browser://extensions/) page and turn on developer mode. 14 | 15 | ![](../assets/installation//developer-mode.png) 16 | 17 | 3. Click on load unpacked and select the clear space folder. 18 | 19 | ![Load Unpacked](../assets/installation/load-unpacked.png) 20 | 21 | 4. Once the extension is installed, you will see the Clear Space icon in the top right corner of your browser. 22 | 23 | 5. Tap on the icon and allow the extension to access Youtube. 24 | 25 | ![extension-loaded-image](../assets/installation/loaded-image.png) 26 | 27 | --- 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Santhosh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Clear Space", 4 | "version": "0.0.1", 5 | "description": "Remove distractions from short form content and helps to focus on the productive things that matter.", 6 | "icons": { 7 | "16": "assets/icons/logo-16.png", 8 | "32": "assets/icons/logo-32.png", 9 | "48": "assets/icons/logo-48.png", 10 | "128": "assets/icons/logo-128.png" 11 | }, 12 | "permissions": [ 13 | "tabs", 14 | "storage", 15 | "activeTab" 16 | ], 17 | "host_permissions": [ 18 | "*://*.youtube.com/*", 19 | "*://*.instagram.com/*", 20 | "*://*.twitter.com/*", 21 | "*://*.x.com/*", 22 | "*://*.facebook.com/*", 23 | "*://*.linkedin.com/*", 24 | "https://blocklistproject.github.io/" 25 | ], 26 | "background": { 27 | "service_worker": "src/scripts/background.js" 28 | }, 29 | "content_scripts": [ 30 | { 31 | "matches": [ 32 | "" 33 | ], 34 | "js": [ 35 | "src/scripts/main.js" 36 | ], 37 | "css": [ 38 | "src/styles/popup.css" 39 | ] 40 | } 41 | ], 42 | "action": { 43 | "default_popup": "src/html/popup.html", 44 | "default_title": "Clear Space" 45 | }, 46 | "incognito": "split" 47 | } -------------------------------------------------------------------------------- /src/html/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Clear Space 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Clear Space

16 |
17 | 18 |
19 |
20 |
21 | apps 22 | Blocked features 23 |
24 | 25 |
26 | public_off 27 | Blocked sites 28 |
29 |
30 | 31 |
32 |

Support us

33 |

Help us spread the word and share our mission to minimise digital distractions for a wider audience

34 | 35 | Give Review on Chrome web store ↗️ 36 | Give a Star ⭐ on Github ↗️ 37 |
38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /src/html/siteblockmanager.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Clear Space - site block Manager 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |

Clear Space - Site block Manager

16 | 17 | 18 |
19 | 23 | 27 |
28 | 29 | 30 |
31 |

Blocked websites

32 |
33 |
34 | 35 | 36 |
37 |

Suggested

38 |
39 |
40 | 41 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to Clear Space 2 | 3 | Thank you for your interest in contributing to Clear Space! We welcome all contributions and appreciate your help in making this project better. 4 | 5 | --- 6 | 7 | ## Getting Started 8 | 9 | To get started with contributing to Clear Space, follow these steps: 10 | 11 | 1. Clone the repository to your local machine. 12 | 13 | ```bash 14 | git clone https://github.com/Santhoshmani1/Clear-Space.git/ 15 | ``` 16 | 17 | 2. Navigate to the project directory. 18 | 19 | ```bash 20 | cd Clear-Space 21 | ``` 22 | 23 | 3. Set the upstream repository: 24 | 25 | ```bash 26 | git remote add upstream https://github.com/Santhoshmani1/Clear-Space.git 27 | ``` 28 | 29 | 4. Create a new branch for your changes. 30 | Make your changes and commit them to your branch. 31 | 32 | ```bash 33 | git checkout -b 34 | ``` 35 | 36 | 5. Push your changes to the remote upstream repository: 37 | 38 | ```bash 39 | git commit -m "feat: implemented feature x" 40 | git push origin 41 | ``` 42 | 43 | --- 44 | 45 | 46 | ## Resources for developers 47 | 48 | - Get Started with [Extensions 101](https://developer.chrome.com/docs/extensions/mv3/overview/) 49 | - Learn the basics of Chrome extensions with [Development basics for chrome extensions](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/) 50 | - Handling APIs via [Extensions permissions](https://developer.chrome.com/docs/extensions/permissions/) in Browser. 51 | - Hands on tutorial on [Developing a chrome extension](https://www.youtube.com/watch?v=0n809nd4Zu4&t=686s) by FreeCode Camp 52 | 53 | --- 54 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Clear space 2 | 3 | ![Clear space icon](./assets/icon.png) 4 | 5 | ## What is Clear Space ? 6 | 7 | Clear Space is a chrome extension which helps you to remove distractions from short form video content and focus on the productive things that matter. 8 | 9 | --- 10 | 11 | ## Features 12 | 13 | - **Blocks YouTube Shorts:** 14 | blocks the Shorts tab on YouTube and prevent users from getting distracted by short videos. 15 | 16 | ![Shorts-tab-removed-image](./assets/shortstab-removed.png) 17 | 18 | - **Blocks Comments:** 19 | blocks the comments section on YouTube and prevents you from getting distracted by comments. 20 | 21 | - **Blocks Recommendations:** 22 | This feature blocks the recommendations section on YouTube and prevent users from getting distracted by recommended videos. 23 | 24 | ![youtube-watch-page](./assets/watch-page.png) 25 | 26 | ![watch-page-with-clear-space](./assets/watch-page-focussed.png) 27 | 28 | --- 29 | 30 | ## Installation 31 | 32 | See [Installation guide](./docs/installation.md) for detailed step by step instructions on how to install the latest version. 33 | 34 | ## What's next for clear-space 35 | 36 | - Making the extension accessible for firefox and support for other major browsers. 37 | - Increasing functionality for other major social media platforms. 38 | 39 | ## Contributing 40 | 41 | All contributions are welcome. 42 | 43 | Please read our [CONTRIBUTING.md](https://github.com/santhoshmani1/Clear-space/blob/main/contributing.md) for additional help and resources for getting started. 44 | 45 | ## LICENSE 46 | 47 | Clear Space is licensed under the MIT License. See the LICENSE file for more information. 48 | -------------------------------------------------------------------------------- /src/styles/popup.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Montserrat&family=Open+Sans&family=Roboto&display=swap"); 2 | 3 | body { 4 | background-color: #000; 5 | } 6 | 7 | h1#clearspace-main-heading { 8 | color: #069aed; 9 | font-family: "Open Sans", sans-serif; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | font-weight: 700; 14 | padding: 20px; 15 | } 16 | 17 | main#popup-main { 18 | min-width: 400px; 19 | height: 60vw; 20 | } 21 | 22 | .feats { 23 | display: flex; 24 | flex-direction: column; 25 | justify-content: space-evenly; 26 | color: white; 27 | } 28 | 29 | .feats > section { 30 | display: flex; 31 | justify-content: center; 32 | align-items: center; 33 | text-align: center; 34 | margin: 12px; 35 | padding: 12px; 36 | border: 2px solid #069aed; 37 | border-radius: 12px; 38 | } 39 | 40 | .feats > section > a { 41 | color: #fff; 42 | text-decoration: none; 43 | font-size: 18px; 44 | padding: 12px; 45 | font-weight: 600; 46 | } 47 | 48 | .feats > section:hover { 49 | background-color: #069aed; 50 | } 51 | 52 | 53 | .support-us { 54 | margin: 20px; 55 | padding: 20px; 56 | } 57 | 58 | .support-us > h3 { 59 | font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; 60 | text-align: center; 61 | color: #069aed; 62 | font-size: 18px; 63 | padding: 12px; 64 | font-weight: bold; 65 | } 66 | 67 | .support-us > h4 { 68 | color: white; 69 | font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 70 | Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 71 | font-size: 14px; 72 | padding: 5px; 73 | font-weight: 400; 74 | } 75 | 76 | .support-us a { 77 | font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; 78 | color: #bababa; 79 | display: block; 80 | font-size: 14px; 81 | padding: 8px; 82 | text-decoration: underline; 83 | } 84 | 85 | .support-us a:hover { 86 | color: #fff; 87 | text-decoration: underline; 88 | } 89 | 90 | 91 | @keyframes slidein { 92 | from { 93 | opacity: 0; 94 | transform: translate(-100vw); 95 | } 96 | 97 | to { 98 | opacity: 1; 99 | transform: translate(0px); 100 | } 101 | } 102 | 103 | @keyframes popup { 104 | from { 105 | opacity: 0; 106 | } 107 | 108 | to { 109 | opacity: 1; 110 | } 111 | } -------------------------------------------------------------------------------- /src/styles/siteblockmanager.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: black; 3 | color: white; 4 | } 5 | 6 | main { 7 | width: 60vw; 8 | margin: auto; 9 | } 10 | 11 | h1 { 12 | text-align: center; 13 | font-size: 36px; 14 | margin: 20px; 15 | color: #00a2ff; 16 | } 17 | 18 | h2 { 19 | text-align: center; 20 | font-size: 24px; 21 | margin: 20px; 22 | } 23 | 24 | .blockable-website { 25 | display: flex; 26 | justify-content: space-evenly; 27 | align-items: center; 28 | font-size: 18px; 29 | color: #fff; 30 | padding: 2px; 31 | margin: 18px; 32 | border: 2px dotted #a4a4a4; 33 | border-radius: 12px; 34 | } 35 | 36 | section#adder { 37 | width: 60%; 38 | margin: auto; 39 | display: flex; 40 | justify-content: space-evenly; 41 | align-items: center; 42 | } 43 | 44 | label { 45 | display: flex; 46 | justify-content: center; 47 | align-items: center; 48 | letter-spacing: 0.2px; 49 | } 50 | 51 | input#new-website { 52 | min-width: 100%; 53 | font-size: 18px; 54 | margin: 12px 20px; 55 | padding: 12px 18px; 56 | border: none; 57 | background-color: black; 58 | color: #eee; 59 | border: 1px solid #a4a4a4; 60 | border-radius: 12px; 61 | } 62 | 63 | button.add-website-btn { 64 | display: flex; 65 | justify-content: center; 66 | align-items: center; 67 | padding: 4px 16px; 68 | margin: 12px; 69 | border: none; 70 | border-radius: 12px; 71 | } 72 | 73 | button.add-website-btn:hover { 74 | border-radius: 12px; 75 | cursor: pointer; 76 | } 77 | 78 | #add-website, 79 | #suggestions-container { 80 | width: 60%; 81 | margin: auto; 82 | } 83 | 84 | button.add-website-btn { 85 | background-color: white; 86 | color: black; 87 | padding: 8px 24px; 88 | margin: 12px; 89 | border: none; 90 | border-radius: 12px; 91 | } 92 | 93 | button.add-website-btn:hover { 94 | background-color: #28b0ff; 95 | color: white; 96 | transition-duration: 400ms; 97 | } 98 | 99 | button.delete-website-btn { 100 | display: flex; 101 | justify-content: center; 102 | align-items: center; 103 | padding: 8px 24px; 104 | margin: 12px; 105 | border: none; 106 | border-radius: 12px; 107 | } 108 | 109 | button.delete-website-btn:hover { 110 | border-radius: 12px; 111 | cursor: pointer; 112 | background-color: #ff6565; 113 | transition-duration: 400ms; 114 | } 115 | 116 | img { 117 | width: 24px; 118 | height: 24px; 119 | margin: 12px; 120 | } 121 | -------------------------------------------------------------------------------- /src/scripts/background.js: -------------------------------------------------------------------------------- 1 | const ADULT_GROUP = "__adult__"; 2 | const GET_ADULT_WEBSITES = 'getAdultWebsites'; // Action for messaging 3 | const PORN_LIST_URL = 'https://blocklistproject.github.io/Lists/alt-version/porn-nl.txt'; 4 | 5 | let topAdultWebsites = []; 6 | 7 | async function fetchAdultWebsites() { 8 | if (topAdultWebsites.length > 0) return topAdultWebsites; 9 | 10 | try { 11 | const response = await fetch(PORN_LIST_URL); 12 | if (!response.ok) { 13 | console.error('Failed to fetch adult websites list:', response.statusText); 14 | return []; 15 | } 16 | const text = await response.text(); 17 | topAdultWebsites = text.split('\n').filter(line => line && !line.startsWith('#')); 18 | return topAdultWebsites; 19 | } catch (error) { 20 | console.error('Error fetching adult websites list:', error); 21 | return []; 22 | } 23 | } 24 | 25 | async function getBlockedWebsites() { 26 | const { websites } = await chrome.storage.local.get("websites"); 27 | if (websites == null) { 28 | return []; 29 | } 30 | 31 | if (websites.includes(ADULT_GROUP)) { 32 | const adultSites = await fetchAdultWebsites(); 33 | const otherSites = websites.filter(site => site !== ADULT_GROUP); 34 | return [...otherSites, ...adultSites]; 35 | } 36 | 37 | return websites.flatMap((site) => 38 | site === ADULT_GROUP ? [] : site, 39 | ); 40 | } 41 | 42 | // extract root domain from a URL 43 | function getRootDomain(url) { 44 | const slds = new Set([ 45 | "co", "com", "org", "net", "gov", "edu", "ac", "ltd", "plc", "me", "mod", "mil", "nic", "nhs" 46 | ]); 47 | 48 | try { 49 | const { hostname } = new URL(url); 50 | const parts = hostname.split("."); 51 | 52 | if (parts.length <= 2) { 53 | return hostname; 54 | } 55 | 56 | // Check for domains like 'sub.domain.co.uk' 57 | if (parts.length > 2 && slds.has(parts[parts.length - 2])) { 58 | return parts.slice(-3).join("."); 59 | } 60 | 61 | return parts.slice(-2).join("."); 62 | } catch (error) { 63 | console.error("Invalid URL:", url); 64 | return null; 65 | } 66 | } 67 | 68 | // Check if the given URL is in the blocked list 69 | async function isBlocked(url) { 70 | const rootDomain = getRootDomain(url); 71 | if (!rootDomain) return false; 72 | 73 | const websites = await getBlockedWebsites(); 74 | // Use exact match to prevent unintended blocking (e.g., blocking 'tube.com' should not block 'youtube.com') 75 | return websites.some((website) => rootDomain === website); 76 | } 77 | 78 | // Send page type message based on URL 79 | function getPageType(url) { 80 | if (!url) return null; 81 | if (url.includes("youtube.com/watch")) return "watch"; 82 | if (url.includes("youtube.com/shorts")) return "shorts"; 83 | if (url.includes("youtube.com/")) return "home"; 84 | if (url.includes("instagram.com/reels")) return "reels"; 85 | return null; 86 | } 87 | 88 | // 89 | chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { 90 | const url = tab?.url; 91 | const pageType = getPageType(url); 92 | 93 | // Only act on http or https URLs to avoid affecting internal pages 94 | if (!url || !url.startsWith('http')) return; 95 | 96 | if (url) { 97 | console.log("Tab updated:", tabId, changeInfo, url); 98 | 99 | const rootDomain = getRootDomain(url); 100 | if (rootDomain) { 101 | isBlocked(url).then((blocked) => { 102 | if (blocked) { 103 | console.log("Blocked URL:", url); 104 | chrome.tabs.update(tabId, { url: "src/html/redirect.html" }); 105 | } else { 106 | console.log("Allowed URL:", url); 107 | } 108 | }); 109 | } 110 | } 111 | 112 | if (pageType) { 113 | console.log(`Navigated to ${pageType} page:`, url); 114 | chrome.tabs.sendMessage(tabId, { page: pageType }); 115 | } 116 | }); 117 | 118 | // Listen for messages from other parts of the extension 119 | chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { 120 | if (message.action === GET_ADULT_WEBSITES) { 121 | fetchAdultWebsites().then(websites => { 122 | sendResponse(websites); 123 | }); 124 | // Return true to indicate you wish to send a response asynchronously 125 | return true; 126 | } 127 | }); 128 | -------------------------------------------------------------------------------- /src/js/block-features.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const saveButton = document.getElementById('saveSettings'); 3 | const selectAllButtons = document.querySelectorAll('.select-all-btn'); 4 | const checkboxes = document.querySelectorAll('input[type="checkbox"]'); 5 | const switchLabels = document.querySelectorAll('.switch-label'); 6 | 7 | document.addEventListener('DOMContentLoaded', () => { 8 | loadSavedSettings(); 9 | 10 | selectAllButtons.forEach(button => { 11 | button.addEventListener('click', handleSelectAll); 12 | }); 13 | 14 | saveButton.addEventListener('click', saveSettings); 15 | 16 | switchLabels.forEach((label) => { 17 | label.addEventListener('click', (e) => handleToggle(e, label)); 18 | }); 19 | }); 20 | 21 | function loadSavedSettings() { 22 | chrome.storage.sync.get(null, (result) => { 23 | checkboxes.forEach(checkbox => { 24 | if (result[checkbox.name] !== undefined) { 25 | checkbox.checked = result[checkbox.name]; 26 | } 27 | }); 28 | updateSelectAllButtonsState(); 29 | }); 30 | } 31 | 32 | function handleSelectAll(event) { 33 | const button = event.currentTarget; 34 | const sectionPrefix = button.dataset.section; 35 | const sectionCheckboxes = document.querySelectorAll(`input[name^="${sectionPrefix}"]`); 36 | 37 | const allChecked = Array.from(sectionCheckboxes).every(cb => cb.checked); 38 | 39 | sectionCheckboxes.forEach(checkbox => { 40 | checkbox.checked = !allChecked; 41 | }); 42 | 43 | updateSelectAllButtonText(button, !allChecked); 44 | } 45 | 46 | function handleToggle(event, label) { 47 | if (event.target.nodeName === 'INPUT') { 48 | return; 49 | } 50 | 51 | const checkbox = label.querySelector('input[type="checkbox"]'); 52 | if (checkbox) { 53 | checkbox.checked = !checkbox.checked; 54 | } 55 | 56 | if (navigator.vibrate) { 57 | navigator.vibrate(50); 58 | } 59 | 60 | label.classList.add('pop'); 61 | label.addEventListener('animationend', () => label.classList.remove('pop'), { once: true }); 62 | } 63 | 64 | function updateSelectAllButtonText(button, allChecked) { 65 | const textSpan = button.querySelector('span:not(.material-icons)'); 66 | if (allChecked) { 67 | textSpan.textContent = 'Deselect All'; 68 | button.querySelector('.material-icons').textContent = 'remove_done'; 69 | } else { 70 | textSpan.textContent = 'Select All'; 71 | button.querySelector('.material-icons').textContent = 'done_all'; 72 | } 73 | } 74 | 75 | function updateSelectAllButtonsState() { 76 | selectAllButtons.forEach(button => { 77 | const sectionPrefix = button.dataset.section; 78 | const sectionCheckboxes = document.querySelectorAll(`input[name^="${sectionPrefix}"]`); 79 | const allChecked = Array.from(sectionCheckboxes).every(cb => cb.checked); 80 | updateSelectAllButtonText(button, allChecked); 81 | }); 82 | } 83 | 84 | function saveSettings() { 85 | const settings = {}; 86 | saveButton.disabled = true; 87 | saveButton.classList.add('saving'); 88 | saveButton.innerHTML = 'syncSaving...'; 89 | 90 | checkboxes.forEach((checkbox) => { 91 | settings[checkbox.name] = checkbox.checked; 92 | }); 93 | 94 | setTimeout(() => { 95 | chrome.storage.sync.set(settings, () => { 96 | saveButton.classList.remove('saving'); 97 | saveButton.classList.add('saved'); 98 | saveButton.innerHTML = 'check_circleSaved!'; 99 | 100 | setTimeout(() => { 101 | saveButton.classList.remove('saved'); 102 | saveButton.innerHTML = 'saveSave Settings'; 103 | saveButton.disabled = false; 104 | }, 1500); 105 | 106 | chrome.runtime.sendMessage({ action: 'settingsUpdated', settings }); 107 | }); 108 | }, 600); 109 | } 110 | })(); -------------------------------------------------------------------------------- /src/js/block-websites.js: -------------------------------------------------------------------------------- 1 | const recommendationsDiv = document.querySelector("#suggestions"); 2 | const blockedWebsitesSection = document.querySelector("#add-website"); 3 | const addWebsiteBtn = document.querySelector("#new-website-adder-btn"); 4 | 5 | const ADULT_GROUP = "__adult__"; 6 | const GET_ADULT_WEBSITES = 'getAdultWebsites'; // Action for messaging 7 | 8 | async function fetchAdultWebsites() { 9 | // Request the list from the background script 10 | return chrome.runtime.sendMessage({ action: GET_ADULT_WEBSITES }); 11 | } 12 | 13 | // Returns the root domain address by trimming the protocol 14 | function sanitizeWebsite(url) { 15 | return url.replace(/^(http\|https?:\/\/)?(www\.)?/, "").replace(/\/$/, ""); 16 | } 17 | 18 | function isValidWebsite(url) { 19 | return /^(?!:\/\/)([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(\/.*)?$/.test(url); 20 | } 21 | 22 | const Storage = { 23 | async getRawList() { 24 | const { websites } = await chrome.storage.local.get({ websites: [] }); 25 | return websites; 26 | }, 27 | async getWebsites() { 28 | const rawList = await this.getRawList(); 29 | 30 | if (rawList.includes(ADULT_GROUP)) { 31 | const adultSites = await fetchAdultWebsites(); 32 | const otherSites = rawList.filter(site => site !== ADULT_GROUP); 33 | return [...otherSites, ...adultSites]; 34 | } 35 | 36 | return rawList; 37 | }, 38 | async addWebsite(website) { 39 | const rawList = await this.getRawList(); 40 | if (website === ADULT_GROUP) { 41 | if (!rawList.includes(ADULT_GROUP)) rawList.push(ADULT_GROUP); 42 | } else if (!rawList.includes(website)) { 43 | rawList.push(website); 44 | } 45 | await chrome.storage.local.set({ websites: rawList }); 46 | }, 47 | async removeWebsite(website) { 48 | const rawList = await this.getRawList(); 49 | const updated = rawList.filter((w) => w !== website); 50 | await chrome.storage.local.set({ websites: updated }); 51 | }, 52 | }; 53 | 54 | // DOM Element Creation 55 | function createWebsiteElement(website, action, onClickHandler) { 56 | const wrapper = document.createElement("div"); 57 | wrapper.classList.add("blockable-website"); 58 | 59 | const img = document.createElement("img"); 60 | img.src = 61 | website === ADULT_GROUP 62 | ? "../../../assets/under-18.png" 63 | : `https://www.google.com/s2/favicons?domain=${website}`; 64 | img.alt = website; 65 | 66 | const label = document.createElement("label"); 67 | label.textContent = website === ADULT_GROUP ? "Adult Websites" : website; 68 | 69 | const button = document.createElement("button"); 70 | button.classList.add(`${action}-website-btn`); 71 | button.innerHTML = ` 72 | ${action} 73 | ${action === "delete" ? "remove" : "Add"} 74 | `; 75 | button.addEventListener("click", onClickHandler); 76 | 77 | wrapper.append(img, label, button); 78 | return wrapper; 79 | } 80 | 81 | async function renderBlockedWebsites() { 82 | const rawList = await Storage.getRawList(); 83 | 84 | let container = blockedWebsitesSection.querySelector(".blocked-list"); 85 | if (!container) { 86 | container = document.createElement("div"); 87 | container.className = "blocked-list"; 88 | blockedWebsitesSection.appendChild(container); 89 | } 90 | container.innerHTML = ""; 91 | 92 | const fragment = document.createDocumentFragment(); 93 | for (const website of rawList) { 94 | fragment.appendChild( 95 | createWebsiteElement(website, "delete", async () => { 96 | await Storage.removeWebsite(website); 97 | refreshUI(); 98 | }) 99 | ); 100 | } 101 | container.appendChild(fragment); 102 | } 103 | 104 | async function createRecommendations() { 105 | const rawList = await Storage.getRawList(); 106 | const recommended = [ 107 | ADULT_GROUP, 108 | "instagram.com", 109 | "linkedin.com", 110 | "facebook.com", 111 | "youtube.com", 112 | ]; 113 | 114 | recommendationsDiv.innerHTML = ""; 115 | const fragment = document.createDocumentFragment(); 116 | 117 | recommended 118 | .filter((site) => !rawList.includes(site)) 119 | .forEach((site) => { 120 | fragment.appendChild( 121 | createWebsiteElement(site, "add", async () => { 122 | await Storage.addWebsite(site); 123 | refreshUI(); 124 | }) 125 | ); 126 | }); 127 | 128 | recommendationsDiv.appendChild(fragment); 129 | } 130 | 131 | function refreshUI() { 132 | renderBlockedWebsites(); 133 | createRecommendations(); 134 | } 135 | 136 | async function addNewWebsite() { 137 | const input = document.querySelector("#new-website"); 138 | const rawInput = input?.value.trim(); 139 | const sanitized = sanitizeWebsite(rawInput); 140 | 141 | if (!rawInput || !isValidWebsite(sanitized)) { 142 | alert("Please enter a valid website."); 143 | return; 144 | } 145 | 146 | const websites = await Storage.getWebsites(); 147 | if (websites.includes(sanitized)) { 148 | alert(`This website (${sanitized}) is already in the blocklist.`); 149 | return; 150 | } 151 | 152 | await Storage.addWebsite(sanitized); 153 | input.value = ""; 154 | refreshUI(); 155 | } 156 | 157 | addWebsiteBtn.addEventListener("click", addNewWebsite); 158 | refreshUI(); 159 | -------------------------------------------------------------------------------- /src/styles/blocked-features.css: -------------------------------------------------------------------------------- 1 | /* --- Base & Typography --- */ 2 | :root { 3 | --bg-color: #121212; 4 | --surface-color: #1e1e1e; 5 | --primary-color: #007aff; 6 | --primary-variant-color: #0056b3; 7 | --secondary-color: #03dac6; 8 | --on-bg-primary-text: #e1e1e1; 9 | --on-bg-secondary-text: #a0a0a0; 10 | --on-surface-text: #ffffff; 11 | --border-color: #2c2c2c; 12 | --shadow-color: rgba(0, 0, 0, 0.4); 13 | --success-color: #4caf50; 14 | } 15 | 16 | body { 17 | font-family: 'Poppins', sans-serif; 18 | background-color: var(--bg-color); 19 | color: var(--on-bg-primary-text); 20 | margin: 0; 21 | padding: 2rem; 22 | display: flex; 23 | justify-content: center; 24 | align-items: flex-start; 25 | min-height: 100vh; 26 | } 27 | 28 | .container { 29 | width: 100%; 30 | max-width: 1200px; 31 | } 32 | 33 | /* --- Header --- */ 34 | .page-header { 35 | text-align: center; 36 | margin-bottom: 3rem; 37 | } 38 | 39 | .page-header h1 { 40 | font-size: 2.5rem; 41 | font-weight: 700; 42 | color: var(--on-surface-text); 43 | margin-bottom: 0.5rem; 44 | } 45 | 46 | .page-header p { 47 | font-size: 1.1rem; 48 | color: var(--on-bg-secondary-text); 49 | max-width: 600px; 50 | margin: 0 auto; 51 | } 52 | 53 | /* --- Main Layout --- */ 54 | .sections-container { 55 | display: grid; 56 | grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); 57 | gap: 2rem; 58 | } 59 | 60 | /* --- Feature Section Cards --- */ 61 | .feature-section { 62 | background-color: var(--surface-color); 63 | border-radius: 16px; 64 | padding: 1.5rem; 65 | border: 1px solid transparent; 66 | box-shadow: 0 8px 24px var(--shadow-color); 67 | transition: border-color 0.3s ease, box-shadow 0.3s ease; 68 | } 69 | 70 | .feature-section:hover { 71 | border-color: var(--primary-color); 72 | } 73 | 74 | .section-header { 75 | display: flex; 76 | align-items: center; 77 | margin-bottom: 1.5rem; 78 | padding-bottom: 1rem; 79 | border-bottom: 1px solid var(--border-color); 80 | } 81 | 82 | .section-header img { 83 | width: 24px; 84 | height: 24px; 85 | margin-right: 0.75rem; 86 | } 87 | 88 | .section-header h2 { 89 | font-size: 1.5rem; 90 | font-weight: 500; 91 | color: var(--on-surface-text); 92 | margin: 0; 93 | flex-grow: 1; 94 | } 95 | 96 | /* --- Grid Options --- */ 97 | .grid-options { 98 | display: grid; 99 | grid-template-columns: 1fr; 100 | gap: 1rem; 101 | } 102 | 103 | /* --- Switch --- */ 104 | .switch-label { 105 | display: flex; 106 | justify-content: space-between; 107 | align-items: center; 108 | padding: 0.75rem 1rem; 109 | background-color: rgba(255, 255, 255, 0.05); 110 | border-radius: 12px; 111 | cursor: pointer; 112 | transition: background-color 0.3s ease; 113 | } 114 | 115 | .switch-label:hover { 116 | background-color: rgba(255, 255, 255, 0.1); 117 | } 118 | 119 | .feature-name { 120 | font-size: 1rem; 121 | font-weight: 400; 122 | } 123 | 124 | .switch input { 125 | display: none; 126 | } 127 | 128 | .slider { 129 | position: relative; 130 | width: 38px; 131 | height: 22px; 132 | background-color: #4d4d4d; 133 | border-radius: 11px; 134 | transition: background-color 0.3s ease; 135 | } 136 | 137 | /* The checkmark tick */ 138 | .slider::before { 139 | content: ''; 140 | position: absolute; 141 | left: 50%; 142 | top: 50%; 143 | width: 4px; 144 | height: 8px; 145 | border: solid white; 146 | border-width: 0 2px 2px 0; 147 | transform: translate(-50%, -55%) rotate(45deg) scale(0); 148 | opacity: 0; 149 | transition: transform 0.3s ease, opacity 0.3s ease; 150 | z-index: 2; 151 | } 152 | 153 | /* The circle around the tick */ 154 | .slider::after { 155 | content: ''; 156 | position: absolute; 157 | left: 50%; 158 | top: 50%; 159 | width: 16px; 160 | height: 16px; 161 | border: 2px solid white; 162 | border-radius: 50%; 163 | transform: translate(-50%, -50%) scale(0); 164 | opacity: 0; 165 | transition: transform 0.3s ease, opacity 0.3s ease; 166 | z-index: 1; 167 | } 168 | 169 | .switch input:checked+.slider { 170 | background-color: var(--primary-color); 171 | } 172 | 173 | .switch input:checked+.slider::after { 174 | transform: translate(-50%, -50%) scale(1); 175 | opacity: 1; 176 | } 177 | 178 | .switch input:checked+.slider::before { 179 | transform: translate(-50%, -55%) rotate(45deg) scale(1); 180 | opacity: 1; 181 | } 182 | 183 | /* Highlight for selected feature */ 184 | .switch input:checked~.feature-name { 185 | color: var(--on-surface-text); 186 | } 187 | 188 | .switch-label:has(input:checked) { 189 | background-color: rgba(0, 122, 255, 0.15); 190 | } 191 | 192 | /* --- Buttons --- */ 193 | .select-all-btn { 194 | background-color: rgba(255, 255, 255, 0.08); 195 | border: 1px solid var(--primary-variant-color); 196 | color: var(--on-bg-secondary-text); 197 | padding: 0.5rem 0.75rem; 198 | border-radius: 8px; 199 | cursor: pointer; 200 | display: flex; 201 | align-items: center; 202 | font-size: 0.85rem; 203 | transition: background-color 0.3s, color 0.3s, transform 0.2s; 204 | } 205 | 206 | .select-all-btn:hover { 207 | background-color: var(--primary-color); 208 | color: #000; 209 | border-color: var(--primary-color); 210 | } 211 | 212 | .select-all-btn .material-icons { 213 | font-size: 1rem; 214 | margin-right: 0.25rem; 215 | } 216 | 217 | .page-footer { 218 | position: sticky; 219 | bottom: 0; 220 | left: 0; 221 | width: 100%; 222 | padding: 1.5rem 0; 223 | background: linear-gradient(to top, var(--bg-color) 70%, transparent); 224 | display: flex; 225 | justify-content: center; 226 | margin-top: 2rem; 227 | } 228 | 229 | .save-button { 230 | background-color: var(--primary-color); 231 | color: #fff; 232 | border: none; 233 | border-radius: 28px; 234 | padding: 0.8rem 2rem; 235 | font-size: 1rem; 236 | font-weight: 700; 237 | cursor: pointer; 238 | display: flex; 239 | align-items: center; 240 | justify-content: center; 241 | box-shadow: 0 4px 15px rgba(0, 122, 255, 0.2); 242 | transition: all 0.3s ease; 243 | } 244 | 245 | .save-button:hover:not(:disabled) { 246 | transform: translateY(-3px); 247 | box-shadow: 0 6px 20px rgba(0, 122, 255, 0.3); 248 | } 249 | 250 | .save-button:disabled { 251 | cursor: not-allowed; 252 | opacity: 0.8; 253 | } 254 | 255 | .save-button .material-icons { 256 | margin-right: 0.5rem; 257 | } 258 | 259 | /* --- Save Button States & Animations --- */ 260 | .save-button.saving { 261 | background-color: #555; 262 | color: var(--on-bg-secondary-text); 263 | } 264 | 265 | .save-button.saved { 266 | background-color: var(--primary-color); 267 | color: white; 268 | } 269 | 270 | @keyframes rotate { 271 | from { 272 | transform: rotate(0deg); 273 | } 274 | 275 | to { 276 | transform: rotate(360deg); 277 | } 278 | } 279 | 280 | .rotating { 281 | animation: rotate 1s linear infinite; 282 | } 283 | 284 | /* Animation for double-press feedback */ 285 | @keyframes pop { 286 | 0% { 287 | transform: scale(1); 288 | } 289 | 290 | 50% { 291 | transform: scale(1.03); 292 | } 293 | 294 | 100% { 295 | transform: scale(1); 296 | } 297 | } 298 | 299 | .switch-label.pop { 300 | animation: pop 0.2s ease-out; 301 | } 302 | 303 | /* --- Responsive --- */ 304 | @media (max-width: 768px) { 305 | body { 306 | padding: 1rem; 307 | } 308 | 309 | .sections-container { 310 | grid-template-columns: 1fr; 311 | } 312 | 313 | .page-header h1 { 314 | font-size: 2rem; 315 | } 316 | } -------------------------------------------------------------------------------- /src/scripts/main.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | // Store user settings 3 | let userSettings = {}; 4 | 5 | // Load settings first 6 | loadSettings(); 7 | 8 | // Listen for messages from background script 9 | chrome.runtime.onMessage.addListener((obj, sender, response) => { 10 | // listening for tab switches from background.js 11 | // To access Youtube main page , watch page and shorts page. 12 | const { page } = obj; 13 | console.log("on " + page + " page"); 14 | 15 | // Handle settings update messages 16 | if (obj.action === 'settingsUpdated') { 17 | userSettings = obj.settings; 18 | applySettings(page); 19 | return; 20 | } 21 | 22 | applySettings(page); 23 | }); 24 | 25 | // Load user settings from storage 26 | function loadSettings() { 27 | chrome.storage.sync.get(null, (result) => { 28 | userSettings = result; 29 | }); 30 | } 31 | 32 | // Apply settings based on the current page 33 | function applySettings(page) { 34 | if (page === "watch") { 35 | cleanWatchPage(); 36 | } else if (page === "home") { 37 | cleanHomePage(); 38 | } else if (page === "shorts") { 39 | blockShortsPage(); 40 | } else if (page === "reels") { 41 | blockReelsPage(); 42 | } else if (page === "twitter" || page === "x") { 43 | handleTwitterPage(); 44 | } else if (page === "facebook") { 45 | handleFacebookPage(); 46 | } else if (page === "linkedin") { 47 | handleLinkedInPage(); 48 | } else if (page === "instagram") { 49 | handleInstagramPage(); 50 | } 51 | } 52 | })(); 53 | 54 | function cleanWatchPage() { 55 | // Load settings to check which features to block 56 | chrome.storage.sync.get(['ytRecommendations', 'ytComments'], (settings) => { 57 | // Remove recommendations if enabled 58 | if (settings.ytRecommendations) { 59 | const secondaryShelf = document.querySelector("#related"); 60 | if (secondaryShelf) { 61 | secondaryShelf.remove(); 62 | console.log("Removed recommendations sidebar"); 63 | } 64 | } 65 | 66 | // Remove comments if enabled 67 | if (settings.ytComments) { 68 | const commentShelf = document.querySelector("ytd-comments"); 69 | if (commentShelf) { 70 | commentShelf.remove(); 71 | console.log("Removed comments section"); 72 | } 73 | } 74 | }); 75 | } 76 | 77 | function cleanHomePage() { 78 | // Load settings to check which features to block 79 | chrome.storage.sync.get(['ytFeed', 'ytShorts', 'ytSubscriptions'], (settings) => { 80 | // If feed is enabled to be blocked 81 | if (settings.ytFeed) { 82 | // Removing the tags container (chips) 83 | const chipsContainer = document.querySelector("#chips-wrapper"); 84 | if (chipsContainer) { 85 | chipsContainer.remove(); 86 | console.log("chipsContainer removed successfully"); 87 | } 88 | 89 | // Remove main feed content 90 | const primaryContent = document.querySelector("ytd-rich-grid-renderer"); 91 | if (primaryContent) { 92 | // Replace with a focus message instead of completely removing 93 | const focusMessage = document.createElement("div"); 94 | focusMessage.className = "focus-message"; 95 | focusMessage.style.textAlign = "center"; 96 | focusMessage.style.padding = "100px 20px"; 97 | focusMessage.style.fontSize = "20px"; 98 | focusMessage.style.color = "#fff"; 99 | focusMessage.innerHTML = ` 100 |

Feed blocked by ClearSpace

101 |

Stay focused on your goals!

102 | `; 103 | primaryContent.innerHTML = ''; 104 | primaryContent.appendChild(focusMessage); 105 | } 106 | 107 | // Remove banners and promotional content 108 | const banners = document.querySelectorAll(".ytd-statement-banner-renderer"); 109 | banners.forEach((banner) => { 110 | banner.remove(); 111 | }); 112 | 113 | const dismissibleFeaturedBanner = document.querySelectorAll( 114 | ".ytd-brand-video-shelf-renderer", 115 | ); 116 | dismissibleFeaturedBanner.forEach((banner) => { 117 | banner.remove(); 118 | }); 119 | } 120 | 121 | // If shorts is enabled to be blocked 122 | if (settings.ytShorts) { 123 | const shortsShelfs = document.querySelectorAll("ytd-rich-shelf-renderer"); 124 | console.log(shortsShelfs); 125 | 126 | if (shortsShelfs) { 127 | shortsShelfs.forEach((shelf) => { 128 | console.log(shelf); 129 | shelf.remove(); 130 | }); 131 | console.log("removed shorts shelfs successfully"); 132 | } 133 | 134 | const shortsNavItem = document.querySelector("#endpoint[title='Shorts']"); 135 | if (shortsNavItem && shortsNavItem.getAttribute("aria-label") === "Shorts") { 136 | shortsNavItem.remove(); 137 | console.log("removed shorts nav item successfully"); 138 | } 139 | 140 | const miniShorts = document.querySelector( 141 | '.ytd-mini-guide-entry-renderer[title="Shorts"]', 142 | ); 143 | if (miniShorts) { 144 | miniShorts.remove(); 145 | } 146 | } 147 | 148 | // If subscriptions option is enabled to be blocked 149 | if (settings.ytSubscriptions) { 150 | const subscriptionsNav = document.querySelector("#endpoint[title='Subscriptions']"); 151 | if (subscriptionsNav) { 152 | subscriptionsNav.remove(); 153 | console.log("removed subscriptions nav item"); 154 | } 155 | } 156 | 157 | // Set up a mutation observer to handle dynamically loaded elements 158 | const observer = new MutationObserver((mutations) => { 159 | mutations.forEach(() => { 160 | // Block shorts navigation item if it appears 161 | if (settings.ytShorts) { 162 | const navItems = document.querySelectorAll( 163 | "#items>ytd-guide-entry-renderer", 164 | ); 165 | if (navItems[1] && navItems[1].getAttribute("aria-label") === "Shorts") { 166 | navItems[1].remove(); 167 | } 168 | } 169 | 170 | // Block explore items and promotional links (Not required) 171 | const exploreItems = document.querySelectorAll( 172 | "ytd-guide-entry-renderer", 173 | ); 174 | // remove items from index 21 to last 175 | // Below 21 items are related to Navigation and channel subscriptions 176 | // from index 21 to last are explore items and Promotional links 177 | for (let i = 21; i < exploreItems.length; i++) { 178 | exploreItems[i].remove(); 179 | } 180 | }); 181 | }); 182 | 183 | observer.observe(document.body, { childList: true, subtree: true }); 184 | }); 185 | } 186 | 187 | function blockShortsPage() { 188 | // Check if shorts blocking is enabled 189 | chrome.storage.sync.get(['ytShorts'], (settings) => { 190 | if (settings.ytShorts) { 191 | // Adding time interval to eliminate execution of scripting 192 | // before DOM is completely loaded. 193 | const observer = new MutationObserver((mutations) => { 194 | mutations.forEach(() => { 195 | const shorts = document.querySelector( 196 | "#page-manager > ytd-shorts > div.navigation-container.style-scope.ytd-shorts", 197 | ); 198 | if (shorts) { 199 | shorts.remove(); 200 | document.location.href = "/404"; 201 | observer.disconnect(); 202 | } 203 | }); 204 | }); 205 | 206 | observer.observe(document.body, { childList: true, subtree: true }); 207 | } 208 | }); 209 | } 210 | 211 | function blockReelsPage() { 212 | // Check if reels/shorts blocking is enabled 213 | chrome.storage.sync.get(['igReels'], (settings) => { 214 | if (settings.igReels) { 215 | document.location.href = "src/html/redirect.html"; 216 | } 217 | }); 218 | } 219 | 220 | function handleTwitterPage() { 221 | chrome.storage.sync.get(['twFeed', 'twTrends', 'twNotifications', 'twMessages', 'twLists'], (settings) => { 222 | // Create and insert CSS based on settings 223 | let css = ''; 224 | 225 | if (settings.twFeed) { 226 | css += ` 227 | div[data-testid="primaryColumn"] > div > div {display: none !important;} 228 | div[aria-label="Timeline: Your Home Timeline"] {display: none !important;} 229 | `; 230 | } 231 | 232 | if (settings.twTrends) { 233 | css += ` 234 | div[data-testid="sidebarColumn"] div[aria-label="Trending"] {display: none !important;} 235 | div[data-testid="sidebarColumn"] section[aria-label="What's happening"] {display: none !important;} 236 | `; 237 | } 238 | 239 | if (settings.twNotifications) { 240 | css += ` 241 | a[data-testid="AppTabBar_Notifications_Link"] {display: none !important;} 242 | div[aria-label="Timeline: Notifications"] {display: none !important;} 243 | `; 244 | } 245 | 246 | if (settings.twMessages) { 247 | css += ` 248 | a[data-testid="AppTabBar_DirectMessage_Link"] {display: none !important;} 249 | div[aria-label="Timeline: Messages"] {display: none !important;} 250 | `; 251 | } 252 | 253 | if (settings.twLists) { 254 | css += ` 255 | a[href="/i/lists"] {display: none !important;} 256 | `; 257 | } 258 | 259 | if (css) { 260 | const style = document.createElement('style'); 261 | style.id = 'clear-space-twitter-styles'; 262 | style.innerHTML = css; 263 | document.head.appendChild(style); 264 | } 265 | }); 266 | } 267 | 268 | function handleFacebookPage() { 269 | chrome.storage.sync.get(['fbFeed', 'fbGroups', 'fbMarketplace', 'fbEvents', 'fbStories', 'fbNotifications'], (settings) => { 270 | // Create and insert CSS based on settings 271 | let css = ''; 272 | 273 | if (settings.fbFeed) { 274 | css += ` 275 | div[role="main"] {display: none !important;} 276 | div[data-pagelet="FeedUnit"] {display: none !important;} 277 | `; 278 | } 279 | 280 | if (settings.fbGroups) { 281 | css += ` 282 | a[href^="/groups/"] {display: none !important;} 283 | `; 284 | } 285 | 286 | if (settings.fbMarketplace) { 287 | css += ` 288 | a[href^="/marketplace"] {display: none !important;} 289 | `; 290 | } 291 | 292 | if (settings.fbEvents) { 293 | css += ` 294 | a[href^="/events"] {display: none !important;} 295 | `; 296 | } 297 | 298 | if (settings.fbStories) { 299 | css += ` 300 | div[data-pagelet="Stories"] {display: none !important;} 301 | `; 302 | } 303 | 304 | if (settings.fbNotifications) { 305 | css += ` 306 | a[href^="/notifications"] {display: none !important;} 307 | div[aria-label="Notifications"] {display: none !important;} 308 | `; 309 | } 310 | 311 | if (css) { 312 | const style = document.createElement('style'); 313 | style.id = 'clear-space-facebook-styles'; 314 | style.innerHTML = css; 315 | document.head.appendChild(style); 316 | } 317 | }); 318 | } 319 | 320 | function handleLinkedInPage() { 321 | chrome.storage.sync.get(['liStories', 'liFeed', 'liMessaging', 'liNotifications'], (settings) => { 322 | // Create and insert CSS based on settings 323 | let css = ''; 324 | 325 | if (settings.liStories) { 326 | css += ` 327 | div.stories-container {display: none !important;} 328 | `; 329 | } 330 | 331 | if (settings.liFeed) { 332 | css += ` 333 | div.feed-shared-update-v2 {display: none !important;} 334 | div.scaffold-finite-scroll__content {display: none !important;} 335 | `; 336 | } 337 | 338 | if (settings.liMessaging) { 339 | css += ` 340 | a[href^="/messaging"] {display: none !important;} 341 | li.msg-overlay-list-bubble {display: none !important;} 342 | `; 343 | } 344 | 345 | if (settings.liNotifications) { 346 | css += ` 347 | a[href^="/notifications"] {display: none !important;} 348 | `; 349 | } 350 | 351 | if (css) { 352 | const style = document.createElement('style'); 353 | style.id = 'clear-space-linkedin-styles'; 354 | style.innerHTML = css; 355 | document.head.appendChild(style); 356 | } 357 | }); 358 | } 359 | 360 | function handleInstagramPage() { 361 | chrome.storage.sync.get(['igStories', 'igReels', 'igFeed', 'igExtras'], (settings) => { 362 | // Create and insert CSS based on settings 363 | let css = ''; 364 | 365 | if (settings.igStories) { 366 | css += ` 367 | div[role="menu"] section {display: none !important;} 368 | `; 369 | } 370 | 371 | if (settings.igReels) { 372 | css += ` 373 | a[href="/reels/"] {display: none !important;} 374 | `; 375 | } 376 | 377 | if (settings.igFeed) { 378 | css += ` 379 | article[role="presentation"] {display: none !important;} 380 | `; 381 | } 382 | 383 | if (settings.igExtras) { 384 | css += ` 385 | a[href="/direct/inbox/"] {display: none !important;} 386 | a[href^="/accounts/activity"] {display: none !important;} 387 | div[aria-label="Notifications"] {display: none !important;} 388 | `; 389 | } 390 | 391 | if (css) { 392 | const style = document.createElement('style'); 393 | style.id = 'clear-space-instagram-styles'; 394 | style.innerHTML = css; 395 | document.head.appendChild(style); 396 | } 397 | }); 398 | } 399 | -------------------------------------------------------------------------------- /src/html/blocked-features.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ClearSpace: Focus Settings 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 18 | 19 |
20 |
21 |
22 | YouTube Logo 23 |

YouTube

24 | 28 |
29 |
30 | 37 | 45 | 53 | 61 | 69 |
70 |
71 |
72 |
73 | Twitter Logo 74 |

Twitter

75 | 79 |
80 |
81 | 89 | 97 | 105 | 113 | 121 |
122 |
123 |
124 |
125 | Facebook Logo 126 |

Facebook

127 | 131 |
132 |
133 | 141 | 149 | 157 | 165 | 173 | 181 |
182 |
183 |
184 |
185 | LinkedIn Logo 186 |

LinkedIn

187 | 191 |
192 |
193 | 201 | 209 | 217 | 225 |
226 |
227 |
228 |
229 | Instagram Logo 230 |

Instagram

231 | 235 |
236 |
237 | 245 | 253 | 261 | 269 |
270 |
271 |
272 | 273 |
274 | 278 |
279 |
280 | 281 | 282 | 283 | 284 | --------------------------------------------------------------------------------