Skip to content
Merged
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
27 changes: 23 additions & 4 deletions options/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ h1 {
.tab-navigation {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px;
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
Expand All @@ -208,6 +207,12 @@ h1 {
scrollbar-width: thin;
}

.tab-list {
display: flex;
flex-direction: column;
gap: 4px;
Comment on lines +210 to +213

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore mobile tab layout for wrapped tab buttons

The new .tab-list wrapper is always forced to flex-direction: column, but the mobile breakpoint only updates .tab-navigation to a row layout; since all tab buttons now sit inside .tab-list, they stay vertically stacked on narrow screens instead of using the previous horizontal tab strip, which makes settings navigation significantly harder on mobile-width viewports. Add a mobile override for .tab-list so it follows the responsive row behavior.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a mobile override for .tab-list so the tab strip switches back to a horizontal, scrollable layout on narrow screens.

}

.sidebar-header {
padding: 16px 16px 12px;
margin-bottom: 8px;
Expand Down Expand Up @@ -284,11 +289,14 @@ h1 {

/* Tab Panels */
.tab-panel {
display: none;
animation: fadeIn 0.2s ease-in;
}

.tab-panel.active {
.tab-panel[hidden] {
display: none;
}

.tab-panel:not([hidden]) {
display: block;
}

Expand Down Expand Up @@ -321,6 +329,11 @@ h1 {
padding: 4px;
}

.tab-list {
flex-direction: row;
min-width: max-content;
}

.tab-button {
flex-direction: column;
gap: 4px;
Expand Down Expand Up @@ -862,6 +875,8 @@ body.dark-mode .notification-toggle input:checked + .toggle-slider {

.setup-step.clickable {
cursor: pointer;
color: inherit;
text-decoration: none;
}

.setup-step:hover {
Expand Down Expand Up @@ -910,6 +925,11 @@ body.dark-mode .notification-toggle input:checked + .toggle-slider {
transform: none;
}

.setup-step.clickable:focus-visible {
outline: 2px solid var(--link-color);
outline-offset: 2px;
}

.step-all-content {
flex: 1;
}
Expand Down Expand Up @@ -2921,4 +2941,3 @@ body.dark-mode .setup-btn:hover {
padding: 20px 0 15px;
}
}

102 changes: 52 additions & 50 deletions options/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,54 @@
</head>
<body>
<div class="container">
<nav class="tab-navigation" role="navigation" aria-label="Settings tabs">
<nav class="tab-navigation" aria-label="Settings">
<div class="sidebar-header">
<h2>Settings</h2>
</div>
<button class="tab-button active" data-tab="setup" aria-selected="true">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
</svg>
<span>Setup</span>
</button>
<button class="tab-button" data-tab="repositories" aria-selected="false">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
</svg>
<span>Repositories</span>
</button>
<button class="tab-button" data-tab="filters" aria-selected="false">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
</svg>
<span>Activity Filters</span>
</button>
<button class="tab-button" data-tab="preferences" aria-selected="false">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg>
<span>Appearance & Behavior</span>
</button>
<button class="tab-button" data-tab="advanced" aria-selected="false">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<span>Advanced</span>
</button>
<button class="tab-button" data-tab="help" aria-selected="false">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Help</span>
</button>
<div class="tab-list" role="tablist" aria-label="Settings sections" aria-orientation="vertical">
<button class="tab-button active" type="button" id="tab-setup" role="tab" data-tab="setup" aria-selected="true" aria-controls="panel-setup" tabindex="0">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
</svg>
<span>Setup</span>
</button>
<button class="tab-button" type="button" id="tab-repositories" role="tab" data-tab="repositories" aria-selected="false" aria-controls="panel-repositories" tabindex="-1">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
</svg>
<span>Repositories</span>
</button>
<button class="tab-button" type="button" id="tab-filters" role="tab" data-tab="filters" aria-selected="false" aria-controls="panel-filters" tabindex="-1">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
</svg>
<span>Activity Filters</span>
</button>
<button class="tab-button" type="button" id="tab-preferences" role="tab" data-tab="preferences" aria-selected="false" aria-controls="panel-preferences" tabindex="-1">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
</svg>
<span>Appearance & Behavior</span>
</button>
<button class="tab-button" type="button" id="tab-advanced" role="tab" data-tab="advanced" aria-selected="false" aria-controls="panel-advanced" tabindex="-1">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<span>Advanced</span>
</button>
<button class="tab-button" type="button" id="tab-help" role="tab" data-tab="help" aria-selected="false" aria-controls="panel-help" tabindex="-1">
<svg class="tab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>Help</span>
</button>
</div>
</nav>

<div class="content-wrapper">
<!-- Tab Panel 1: Setup -->
<div class="tab-panel active" data-tab="setup">
<div class="tab-panel" id="panel-setup" role="tabpanel" data-tab="setup" aria-labelledby="tab-setup">
<section class="section">
<h2>Getting Started</h2>
<p class="help-text">Follow these steps to set up GitHub DevWatch</p>
Expand Down Expand Up @@ -104,29 +106,29 @@ <h3>Create a GitHub Token</h3>
</div>
</div>

<div class="setup-step clickable" data-tab="repositories">
<a class="setup-step clickable" href="#repositories" data-tab="repositories">
<div class="step-number">2</div>
<div class="step-content">
<h3>Add Repositories</h3>
<p>Go to the <strong>Repositories</strong> tab to add repositories you want to monitor. You can add them manually or import from your GitHub account.</p>
</div>
</div>
</a>

<div class="setup-step clickable" data-tab="filters">
<a class="setup-step clickable" href="#filters" data-tab="filters">
<div class="step-number">3</div>
<div class="step-content">
<h3>Configure Filters</h3>
<p>Visit the <strong>Activity Filters</strong> tab to choose which types of activity you want to see (Pull Requests, Issues, Releases) and enable notifications.</p>
</div>
</div>
</a>

<div class="setup-step clickable" data-tab="help">
<a class="setup-step clickable" href="#help" data-tab="help">
<div class="step-number">4</div>
<div class="step-content">
<h3>View Help & Changelog</h3>
<p>Visit the <strong>Help</strong> tab to find documentation, support resources, and the changelog to see what's new in the latest version.</p>
</div>
</div>
</a>
</div>

<div class="info-box info-box-compact">
Expand All @@ -141,7 +143,7 @@ <h3>View Help & Changelog</h3>
</div>

<!-- Tab Panel 2: Repositories -->
<div class="tab-panel" data-tab="repositories">
<div class="tab-panel" id="panel-repositories" role="tabpanel" data-tab="repositories" aria-labelledby="tab-repositories" hidden>
<section class="section" id="repositories">
<!-- Add New Repository Card -->
<div class="repo-section-card add-repo-card">
Expand Down Expand Up @@ -263,7 +265,7 @@ <h2>Your Watched Repositories <span class="repo-count-badge" id="repoCountBadge"
</div>

<!-- Tab Panel 3: Activity Filters -->
<div class="tab-panel" data-tab="filters">
<div class="tab-panel" id="panel-filters" role="tabpanel" data-tab="filters" aria-labelledby="tab-filters" hidden>
<section class="section" id="filters">
<h2>Activity Filters & Notifications</h2>
<p class="help-text">Choose which types of activity to show and get notified about</p>
Expand Down Expand Up @@ -372,7 +374,7 @@ <h3>Releases</h3>
</div>

<!-- Tab Panel 4: Appearance & Behavior -->
<div class="tab-panel" data-tab="preferences">
<div class="tab-panel" id="panel-preferences" role="tabpanel" data-tab="preferences" aria-labelledby="tab-preferences" hidden>
<section class="section" id="feed-management">
<h2>Feed Management</h2>
<p class="help-text">Control how long activity items are stored in your feed and archive</p>
Expand Down Expand Up @@ -542,7 +544,7 @@ <h3>Currently Snoozed</h3>
</div>

<!-- Tab Panel 5: Advanced -->
<div class="tab-panel" data-tab="advanced">
<div class="tab-panel" id="panel-advanced" role="tabpanel" data-tab="advanced" aria-labelledby="tab-advanced" hidden>
<section class="section" id="data-management">
<h2>Data Management</h2>
<p class="help-text">Manage your extension data, cache, and settings</p>
Expand Down Expand Up @@ -661,7 +663,7 @@ <h2>Import/Export Settings</h2>
</div>

<!-- Tab Panel 6: Help -->
<div class="tab-panel" data-tab="help">
<div class="tab-panel" id="panel-help" role="tabpanel" data-tab="help" aria-labelledby="tab-help" hidden>
<section class="section">
<div class="help-hero">
<svg class="help-logo" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
Expand Down
69 changes: 58 additions & 11 deletions options/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,44 +50,92 @@ if (typeof document !== 'undefined') {
// Theme listener imported from controllers/theme-controller.js

function setupTabNavigation() {
const tabButtons = document.querySelectorAll('.tab-button');
const tabPanels = document.querySelectorAll('.tab-panel');
const tabButtons = Array.from(document.querySelectorAll('.tab-button'));
const tabPanels = Array.from(document.querySelectorAll('.tab-panel'));

if (tabButtons.length === 0 || tabPanels.length === 0) {
return;
}

const validTabs = tabButtons
.map(button => button.dataset.tab)
.filter(Boolean);

function getTabButton(tabName) {
return tabButtons.find(button => button.dataset.tab === tabName);
}

function switchTab(tabName, { focusTab = false } = {}) {
if (!validTabs.includes(tabName)) {
return;
}

// Function to switch tabs
function switchTab(tabName) {
// Update buttons
tabButtons.forEach(btn => {
const isActive = btn.dataset.tab === tabName;
btn.classList.toggle('active', isActive);
btn.setAttribute('aria-selected', isActive);
btn.setAttribute('aria-selected', isActive ? 'true' : 'false');
btn.tabIndex = isActive ? 0 : -1;
});

// Update panels
tabPanels.forEach(panel => {
panel.classList.toggle('active', panel.dataset.tab === tabName);
const isActive = panel.dataset.tab === tabName;
panel.hidden = !isActive;
});

// Save to localStorage
localStorage.setItem('activeTab', tabName);

// Update URL hash without scrolling
history.replaceState(null, null, `#${tabName}`);

if (focusTab) {
getTabButton(tabName)?.focus();
}
}

// Add click listeners to tab buttons
tabButtons.forEach(button => {
tabButtons.forEach((button, index) => {
button.addEventListener('click', () => {
switchTab(button.dataset.tab);
});

button.addEventListener('keydown', (event) => {
let targetIndex = null;

switch (event.key) {
case 'ArrowUp':
case 'ArrowLeft':
targetIndex = (index - 1 + tabButtons.length) % tabButtons.length;
break;
case 'ArrowDown':
case 'ArrowRight':
targetIndex = (index + 1) % tabButtons.length;
break;
case 'Home':
targetIndex = 0;
break;
case 'End':
targetIndex = tabButtons.length - 1;
break;
default:
return;
}

event.preventDefault();
switchTab(tabButtons[targetIndex].dataset.tab, { focusTab: true });
});
});

// Add click listeners to clickable setup steps
const clickableSetupSteps = document.querySelectorAll('.setup-step.clickable');
clickableSetupSteps.forEach(step => {
step.addEventListener('click', () => {
step.addEventListener('click', (event) => {
event.preventDefault();
const tabName = step.dataset.tab;
if (tabName) {
switchTab(tabName);
switchTab(tabName, { focusTab: true });
}
});
});
Expand All @@ -97,8 +145,6 @@ function setupTabNavigation() {
const savedTab = localStorage.getItem('activeTab');
const initialTab = hash || savedTab || 'setup';

// Check if the hash/saved tab is valid
const validTabs = ['setup', 'repositories', 'filters', 'preferences', 'advanced', 'help'];
const tabToActivate = validTabs.includes(initialTab) ? initialTab : 'setup';

switchTab(tabToActivate);
Expand Down Expand Up @@ -1147,6 +1193,7 @@ setInterval(async () => {
// ES6 exports for tests
export {
state,
setupTabNavigation,
validateToken,
addRepo,
validateRepo,
Expand Down
Loading