├── .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 | 
12 |
13 | 2. Go to [browser://extensions](browser://extensions/) page and turn on developer mode.
14 |
15 | 
16 |
17 | 3. Click on load unpacked and select the clear space folder.
18 |
19 | 
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 | 
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 |
17 |
18 |
19 |
30 |
31 |
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 |
28 |
29 |
30 |
31 | Blocked websites
32 |
33 |
34 |
35 |
36 |
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 | 
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 | 
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 | 
25 |
26 | 
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 |
71 |
123 |
183 |
227 |
271 |
272 |
273 |
279 |
280 |
281 |
282 |
283 |
284 |
--------------------------------------------------------------------------------