Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 16 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,37 @@
# Focused Browsing

A browser extension that hides distracting content on LinkedIn and YouTube, letting you use these sites productively without getting pulled into endless scrolling.
A browser extension that hides distracting content on LinkedIn, YouTube, and X, letting you use these sites productively without getting pulled into endless scrolling.

## What it does

Instead of blocking entire websites, Focused Browsing selectively hides the parts that waste your time while keeping the parts you actually need.

### LinkedIn
- **Full focus mode**: Hides the feed and side panels (news, trending, "Add to your feed"). Replaces the feed with an inspirational quote.
- **Custom focus mode**: Hides only the side panels while keeping the main feed visible. Useful when you want to browse your feed without sidebar distractions.

### YouTube
- **Focus mode**: Hides recommended videos, comments, and suggestions while preserving video playback.
### Supported sites
- **LinkedIn**: Focused mode hides the feed and side panels. Unfocused mode restores the feed but still hides the side panels.
- **YouTube**: Hides recommended videos, comments, and suggestions while preserving video playback.
- **X**: Focused mode hides the home feed and sidebar. Unfocused mode restores the feed but still hides the sidebar.

## How to use it

### Toggle focus mode
Press **Left Shift + Right Shift** on any supported site to cycle through focus modes.

With custom focus enabled for LinkedIn, the cycle is:
**Full Focus** → **Custom Focus** → **Unfocused** → **Full Focus**

Without custom focus, it toggles between **Full Focus** and **Unfocused**.
Press **Left Shift + Right Shift** on any supported site to switch between **Focused** and **Unfocused**.
On LinkedIn and X, **Unfocused** still keeps the side panels hidden.

### Extension popup
Click the extension icon to access settings:
- **LinkedIn** / **YouTube** toggles: Enable or disable the extension per site
- **Custom focus** (under LinkedIn): Enable the custom focus mode that hides only side panels
- **Show inspirational quotes**: Toggle motivational quotes that replace hidden feeds
- **Text size**: Adjust quote text size (S / M / L / XL)
Click the extension icon for a compact status view and shortcut reference.

### Settings page
Advanced controls will live on a separate extension page. The scaffold is now
part of the extension so future customization can move there without bloating
the popup.

## Installation

1. Clone this repo and run `npm install`
2. Run `npm run build`
3. Open your browser's extension page (`chrome://extensions`, `edge://extensions`, or `brave://extensions`)
4. Enable "Developer mode"
5. Click "Load unpacked" and select the `dist` folder
5. Click "Load unpacked" and select the `extension-build` folder

## Browser support

Expand All @@ -50,11 +45,10 @@ Click the extension icon to access settings:
## Troubleshooting

**Extension not working?**
- Make sure the site is enabled in the popup
- Try refreshing the page after enabling a site
- Refresh the current page on a supported site

**Keyboard shortcut not working?**
- Confirm you're on a supported site with the extension enabled for it
- Confirm you're on a supported site

**Content not hiding?**
- Try toggling the extension off and on for that site
Expand Down
7 changes: 7 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async function build() {
'background': 'src/ts/background.ts',
'focus': 'src/ts/focus/focus.ts',
'popup': 'src/popup/popup.ts',
'options': 'src/options/options.ts',
},
bundle: true,
outdir: 'extension-build',
Expand Down Expand Up @@ -56,6 +57,12 @@ async function build() {
}
fs.copyFileSync('src/popup/popup.html', 'extension-build/popup/popup.html');
fs.copyFileSync('src/popup/popup.css', 'extension-build/popup/popup.css');

if (!fs.existsSync('extension-build/options')) {
fs.mkdirSync('extension-build/options');
}
fs.copyFileSync('src/options/options.html', 'extension-build/options/options.html');
fs.copyFileSync('src/options/options.css', 'extension-build/options/options.css');

fs.copyFileSync('src/manifest.json', 'extension-build/manifest.json');

Expand Down
2 changes: 1 addition & 1 deletion reports/selector-health/selector-health-report.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-03-09T15:43:48.835Z",
"generatedAt": "2026-03-09T19:47:54.410Z",
"thresholds": {
"warn": {
"maxWarningRatio": 0.2,
Expand Down
4 changes: 4 additions & 0 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
"default_title": "Focused Browsing",
"default_popup": "popup/popup.html"
},
"options_ui": {
"page": "options/options.html",
"open_in_tab": true
},
"icons": {
"16": "icons/logox16.png",
"48": "icons/logox48.png",
Expand Down
54 changes: 54 additions & 0 deletions src/options/options.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
:root {
--paper: #f5f7fa;
--ink: #0f172a;
--muted: #5b6474;
--rule: #dbe4f0;
--accent: #3b82f6;
}

* {
box-sizing: border-box;
margin: 0;
padding: 0;
}

body {
min-height: 100vh;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: var(--paper);
color: var(--ink);
}

.page {
width: min(960px, calc(100vw - 48px));
margin: 0 auto;
padding: 48px 0 72px;
}

.hero {
display: grid;
gap: 14px;
padding: 0 0 32px;
}

.hero-index {
font-size: 12px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
}

.hero-title {
max-width: 720px;
font-size: clamp(40px, 7vw, 72px);
line-height: 0.96;
letter-spacing: -0.05em;
}

@media (max-width: 800px) {
.page {
width: min(100vw - 32px, 960px);
padding-top: 32px;
}
}
17 changes: 17 additions & 0 deletions src/options/options.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Focused Browsing Settings</title>
<link rel="stylesheet" type="text/css" href="options.css" />
</head>
<body>
<main class="page">
<header class="hero">
<div class="hero-index">Focused Browsing / Settings</div>
<h1 class="hero-title">Settings</h1>
</header>
</main>
<script src="../options.js"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions src/options/options.html.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const content: string;
export default content;
21 changes: 21 additions & 0 deletions src/options/options.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import optionsHtml from './options.html';

import './options';

describe('options.ts', () => {
beforeEach(() => {
document.body.innerHTML = '';
const options = document.createElement('div');
options.innerHTML = optionsHtml;
document.body.appendChild(options);
});

it('renders the separate settings scaffold', () => {
const title = document.querySelector('.hero-title') as HTMLElement;
expect(title.textContent).toBe('Settings');
});

it('does not render a placeholder settings container', () => {
expect(document.querySelector('.surface')).toBeNull();
});
});
2 changes: 2 additions & 0 deletions src/options/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Placeholder module for future options page logic.
export {};
Loading