diff --git a/README.md b/README.md index b78d67c..e127bb3 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,28 @@ # 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**: Focused mode hides recommendations, comments, and suggestion rails while preserving video playback. Unfocused mode restores the standard page around the video player. +- **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 +The popup's `Open Settings` action opens the separate extension settings page. +That page is scaffold-only today and does not expose user controls yet. ## Installation @@ -36,7 +30,7 @@ Click the extension icon to access settings: 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 @@ -50,11 +44,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 diff --git a/build.js b/build.js index 98ceb85..dcc575a 100644 --- a/build.js +++ b/build.js @@ -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', @@ -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'); diff --git a/reports/selector-health/selector-health-report.json b/reports/selector-health/selector-health-report.json index 7f31497..5480619 100644 --- a/reports/selector-health/selector-health-report.json +++ b/reports/selector-health/selector-health-report.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-03-09T15:43:48.835Z", + "generatedAt": "2026-03-10T19:05:41.743Z", "thresholds": { "warn": { "maxWarningRatio": 0.2, diff --git a/src/manifest.json b/src/manifest.json index ed33aee..f7ed2d2 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -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", diff --git a/src/options/options.css b/src/options/options.css new file mode 100644 index 0000000..0f34f10 --- /dev/null +++ b/src/options/options.css @@ -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; + } +} diff --git a/src/options/options.html b/src/options/options.html new file mode 100644 index 0000000..9f1e17c --- /dev/null +++ b/src/options/options.html @@ -0,0 +1,17 @@ + + + + + Focused Browsing Settings + + + +
+
+
Focused Browsing / Settings
+

Settings

+
+
+ + + diff --git a/src/options/options.html.d.ts b/src/options/options.html.d.ts new file mode 100644 index 0000000..7cac97f --- /dev/null +++ b/src/options/options.html.d.ts @@ -0,0 +1,2 @@ +declare const content: string; +export default content; diff --git a/src/options/options.test.ts b/src/options/options.test.ts new file mode 100644 index 0000000..3fbd704 --- /dev/null +++ b/src/options/options.test.ts @@ -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(); + }); +}); diff --git a/src/options/options.ts b/src/options/options.ts new file mode 100644 index 0000000..60c5350 --- /dev/null +++ b/src/options/options.ts @@ -0,0 +1,2 @@ +// Placeholder module for future options page logic. +export {}; diff --git a/src/popup/popup.css b/src/popup/popup.css index e9bd889..9ace550 100644 --- a/src/popup/popup.css +++ b/src/popup/popup.css @@ -1,4 +1,11 @@ -/* Reset and base styles */ +:root { + --paper: #f5f7fa; + --ink: #0f172a; + --muted: #5b6474; + --rule: #dbe4f0; + --accent: #2563eb; +} + * { margin: 0; padding: 0; @@ -6,328 +13,109 @@ } body { - width: 360px; - min-height: 460px; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; - background: #ffffff; - color: #1f2937; - line-height: 1.5; + width: 320px; + min-height: 270px; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + background: var(--paper); + color: var(--ink); + line-height: 1.35; } -.popup-container { - padding: 20px; +.popup-shell { + padding: 18px; display: flex; flex-direction: column; - gap: 18px; -} - -/* Header Section */ -.header { - text-align: center; - padding-bottom: 16px; - border-bottom: 1px solid #e5e7eb; + gap: 16px; } -.brand { +.index-row { display: flex; - align-items: center; - justify-content: center; - gap: 12px; - margin-bottom: 6px; + justify-content: flex-start; + padding: 0 2px; } -.logo-placeholder { - width: 32px; - height: 32px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 8px; - flex-shrink: 0; -} - -.title { - font-size: 24px; +.index-label, +.info-label { + font-size: 11px; font-weight: 700; - color: #111827; - letter-spacing: -0.025em; -} - -.tagline { - color: #6b7280; - font-size: 14px; - font-weight: 500; -} - -/* Shortcut Section */ -.shortcut-section { - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 12px; - padding: 16px; - text-align: center; -} - -.shortcut-label { - font-size: 14px; - font-weight: 600; - color: #374151; - margin-bottom: 10px; -} - -.shortcut-keys { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - margin-bottom: 6px; -} - -.key { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 32px; - height: 32px; - padding: 0 8px; - background: #ffffff; - border: 2px solid #d1d5db; - border-radius: 6px; - font-family: inherit; - font-size: 14px; - font-weight: 600; - color: #374151; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--accent); } -.plus { - color: #9ca3af; - font-weight: 600; - font-size: 16px; +.top-rule { + width: 100%; + height: 4px; + border-radius: 999px; + background: var(--accent); } -.shortcut-hint { - color: #6b7280; - font-size: 12px; - font-weight: 500; +.title { + font-size: 38px; + line-height: 1.04; + letter-spacing: -0.06em; + max-width: 250px; + margin-top: 6px; } -/* Settings Section */ -.settings-section { +.info-grid { display: flex; flex-direction: column; - gap: 14px; -} - -.section-title { - font-size: 16px; - font-weight: 600; - color: #111827; - margin-bottom: 2px; + gap: 2px; + margin-top: 4px; } -.setting-row { - position: relative; +.info-row { display: flex; - align-items: flex-start; justify-content: space-between; gap: 16px; padding: 12px 0; - border-bottom: 1px solid #f3f4f6; + border-bottom: 1px solid var(--rule); } -.setting-row:last-child { +.info-row:last-child { border-bottom: none; } -.setting-info { - flex: 1; - display: flex; - flex-direction: column; - gap: 4px; -} - -.setting-label { +.info-value { + text-align: right; font-size: 14px; font-weight: 500; - color: #111827; - cursor: pointer; + color: var(--ink); } -.setting-description { - font-size: 13px; - color: #6b7280; +.summary { + max-width: 240px; + font-size: 15px; line-height: 1.4; + color: var(--muted); } -/* Toggle Switch */ -.toggle { - position: relative; - display: inline-block; - width: 48px; - height: 24px; - cursor: pointer; - flex-shrink: 0; -} - -.toggle input[type="checkbox"] { - opacity: 0; - width: 0; - height: 0; -} - -.toggle-slider { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #d1d5db; - border-radius: 24px; - transition: all 0.2s ease-in-out; -} - -.toggle-slider:before { - position: absolute; - content: ''; - height: 20px; - width: 20px; - left: 2px; - bottom: 2px; - background: white; - border-radius: 50%; - transition: all 0.2s ease-in-out; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.toggle input:checked + .toggle-slider { - background: #3b82f6; -} - -.toggle input:checked + .toggle-slider:before { - transform: translateX(24px); -} - -.toggle:hover .toggle-slider { - background: #9ca3af; -} - -.toggle:hover input:checked + .toggle-slider { - background: #2563eb; -} - -/* Loading spinner that replaces the toggle */ -.toggle.loading { - cursor: not-allowed; -} - -.toggle.loading .toggle-slider, -.toggle.loading .toggle-slider:before { - opacity: 0; - pointer-events: none; -} - -.toggle.loading::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 20px; - height: 20px; - margin: -10px 0 0 -10px; - border: 2px solid #e5e7eb; - border-top: 2px solid #3b82f6; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -/* Size Selector */ -.size-selector { - display: flex; - gap: 4px; - flex-shrink: 0; -} - -.size-option { - display: flex; - align-items: center; - justify-content: center; - width: 36px; - height: 32px; - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 6px; +.options-button { + padding: 0; + border: 0; + background: transparent; + color: var(--accent); + font: inherit; font-size: 13px; - font-weight: 600; - color: #6b7280; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + text-align: right; cursor: pointer; - transition: all 0.2s ease-in-out; -} - -.size-option:hover { - background: #f3f4f6; - border-color: #d1d5db; - color: #374151; -} - -.size-option.active { - background: #3b82f6; - border-color: #3b82f6; - color: white; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.size-option:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.size-option:disabled:hover { - background: #f9fafb; - border-color: #e5e7eb; - color: #6b7280; -} - -/* Conditional visibility for font size setting */ -#font-size-row { - opacity: 1; - transition: opacity 0.2s ease-in-out; -} - -#font-size-row.disabled { - opacity: 0.4; -} - -#font-size-row.disabled input, -#font-size-row.disabled label, -#font-size-row.disabled .size-option { - cursor: not-allowed; } -/* Focus states for accessibility */ -.toggle input:focus + .toggle-slider { - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -.slider-container input[type="range"]:focus { - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Sub-setting rows (indented, e.g. custom focus under LinkedIn) */ -.setting-row.sub-setting { - padding-left: 20px; +.actions { + display: flex; + justify-content: flex-end; } -.setting-row.sub-setting.disabled { - opacity: 0.4; - pointer-events: none; +.options-button:hover, +.options-button:focus-visible { + color: var(--ink); } -.setting-label:focus { - outline: 2px solid #3b82f6; - outline-offset: 2px; - border-radius: 4px; +.options-button:focus-visible { + outline: 2px solid var(--ink); + outline-offset: 3px; } diff --git a/src/popup/popup.html b/src/popup/popup.html index f22cbfa..404fca2 100644 --- a/src/popup/popup.html +++ b/src/popup/popup.html @@ -6,101 +6,32 @@ -