Redesign settings UI and add new settings functionality

Revamps the settings section in Big Picture mode with a tabbed interface for Themes, Display, Privacy, and About panels. Adds theme selection with live preview and persistence, display scale adjustment, privacy controls for clearing data/history/search, and an About panel showing app and environment info. Updates main process to handle theme and display scale changes, and implements corresponding renderer logic and styles.
This commit is contained in:
2025-12-28 11:00:20 +13:00
parent 399e8da5b4
commit 1687f34e9b
4 changed files with 1066 additions and 18 deletions
+29
View File
@@ -636,6 +636,18 @@ ipcMain.handle('get-app-version', () => {
return app.getVersion();
});
ipcMain.handle('get-app-info', () => {
return {
version: app.getVersion(),
electron: process.versions.electron,
chrome: process.versions.chrome,
node: process.versions.node,
v8: process.versions.v8,
platform: process.platform,
arch: process.arch
};
});
// --- window control handlers (only registered once now)
ipcMain.handle('window-minimize', event => {
BrowserWindow.fromWebContents(event.sender).minimize();
@@ -706,6 +718,23 @@ ipcMain.on('homepage-changed', (event, url) => {
console.log('[MAIN] homepage-changed →', url);
});
// Handle theme changes - broadcast to all windows
ipcMain.on('theme-changed', (event, theme) => {
console.log('[MAIN] theme-changed →', theme?.name || 'unknown');
// Broadcast theme change to all browser windows
BrowserWindow.getAllWindows().forEach(win => {
if (win.webContents && win.webContents.id !== event.sender.id) {
win.webContents.send('theme-changed', theme);
}
});
});
// Handle display scale changes
ipcMain.on('set-display-scale', (event, scale) => {
console.log('[MAIN] set-display-scale →', scale);
// Could be used to persist scale setting or apply to all webviews
});
// Bookmark management
ipcMain.handle('load-bookmarks', async () => {
try {
+332 -2
View File
@@ -386,7 +386,15 @@ body.mouse-active {
}
.bp-section.active {
display: block;
display: flex;
flex-direction: column;
}
/* Special layout for settings section */
#section-settings.active {
display: flex;
flex-direction: column;
height: 100%;
}
@keyframes fadeIn {
@@ -940,7 +948,7 @@ body.mouse-active {
margin-left: auto;
}
/* Settings grid */
/* Settings grid (legacy) */
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
@@ -984,6 +992,328 @@ body.mouse-active {
color: var(--bp-text);
}
/* New Settings UI */
.settings-tabs {
display: flex;
gap: var(--bp-spacing-sm);
margin-bottom: var(--bp-spacing-lg);
padding-bottom: var(--bp-spacing-md);
border-bottom: 1px solid var(--bp-border);
flex-wrap: wrap;
}
.settings-tab {
display: flex;
align-items: center;
gap: var(--bp-spacing-sm);
padding: var(--bp-spacing-sm) var(--bp-spacing-md);
background: transparent;
border: 2px solid transparent;
border-radius: var(--bp-radius-md);
color: var(--bp-text-muted);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all var(--bp-transition-fast);
}
.settings-tab:hover {
background: var(--bp-surface);
color: var(--bp-text);
}
.settings-tab.active {
background: var(--bp-surface);
border-color: var(--bp-primary);
color: var(--bp-text);
}
.settings-tab:focus {
outline: none;
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
}
.settings-tab.focused {
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
background: var(--bp-surface);
color: var(--bp-text);
}
.settings-tab .material-symbols-outlined {
font-size: 24px;
}
.settings-panels {
flex: 1;
overflow-y: auto;
}
.settings-panel {
display: none;
animation: fadeIn var(--bp-transition-normal);
}
.settings-panel.active {
display: block;
}
.settings-panel-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--bp-text);
margin-bottom: var(--bp-spacing-md);
}
/* Theme Grid */
.theme-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
gap: var(--bp-spacing-md);
}
.theme-card {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--bp-spacing-sm);
padding: var(--bp-spacing-md);
background: var(--bp-surface);
border: 2px solid var(--bp-border);
border-radius: var(--bp-radius-lg);
cursor: pointer;
transition: all var(--bp-transition-fast);
}
.theme-card:hover {
background: var(--bp-surface-hover);
transform: translateY(-2px);
}
.theme-card:focus {
outline: none;
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
transform: translateY(-2px) scale(1.02);
}
.theme-card.focused {
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
transform: translateY(-2px) scale(1.02);
}
.theme-card.active {
border-color: var(--bp-accent);
box-shadow: 0 0 0 2px var(--bp-accent);
}
.theme-preview {
width: 100%;
height: 60px;
border-radius: var(--bp-radius-sm);
box-shadow: inset 0 0 0 1px rgba(255,255,255,0.1);
}
.theme-name {
font-size: 0.9rem;
font-weight: 500;
color: var(--bp-text);
}
/* Settings Options */
.settings-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--bp-spacing-md);
background: var(--bp-surface);
border: 1px solid var(--bp-border);
border-radius: var(--bp-radius-md);
margin-bottom: var(--bp-spacing-sm);
}
.option-info {
display: flex;
align-items: center;
gap: var(--bp-spacing-md);
}
.option-info > .material-symbols-outlined {
font-size: 28px;
color: var(--bp-accent);
}
.option-text {
display: flex;
flex-direction: column;
gap: 2px;
}
.option-label {
font-size: 1rem;
font-weight: 600;
color: var(--bp-text);
}
.option-description {
font-size: 0.85rem;
color: var(--bp-text-muted);
}
.option-control {
display: flex;
align-items: center;
gap: var(--bp-spacing-sm);
}
.scale-btn {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: var(--bp-surface-hover);
border: 2px solid var(--bp-border);
border-radius: var(--bp-radius-sm);
color: var(--bp-text);
cursor: pointer;
transition: all var(--bp-transition-fast);
}
.scale-btn:hover {
background: var(--bp-primary);
border-color: var(--bp-primary);
}
.scale-btn:focus {
outline: none;
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
}
.scale-btn.focused {
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
background: var(--bp-primary);
}
.scale-value {
min-width: 60px;
text-align: center;
font-size: 1.1rem;
font-weight: 600;
color: var(--bp-text);
}
.action-button {
display: flex;
align-items: center;
gap: var(--bp-spacing-sm);
padding: var(--bp-spacing-sm) var(--bp-spacing-md);
background: var(--bp-surface-hover);
border: 2px solid var(--bp-border);
border-radius: var(--bp-radius-md);
color: var(--bp-text);
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: all var(--bp-transition-fast);
}
.action-button:hover {
background: var(--bp-primary);
border-color: var(--bp-primary);
}
.action-button:focus {
outline: none;
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
}
.action-button.focused {
border-color: var(--bp-primary);
box-shadow: var(--bp-focus-ring);
background: var(--bp-primary);
}
.action-button.danger:hover {
background: #dc3545;
border-color: #dc3545;
}
/* About Panel */
.about-info {
display: flex;
flex-direction: column;
gap: var(--bp-spacing-lg);
}
.about-logo {
display: flex;
align-items: center;
gap: var(--bp-spacing-md);
padding: var(--bp-spacing-md);
background: var(--bp-surface);
border-radius: var(--bp-radius-lg);
}
.about-logo-img {
width: 64px;
height: 64px;
}
.about-title h3 {
font-size: 1.5rem;
font-weight: 600;
color: var(--bp-text);
margin: 0;
}
.about-title span {
font-size: 0.9rem;
color: var(--bp-text-muted);
}
.about-details {
display: flex;
flex-direction: column;
gap: var(--bp-spacing-xs);
padding: var(--bp-spacing-md);
background: var(--bp-surface);
border-radius: var(--bp-radius-md);
}
.about-row {
display: flex;
justify-content: space-between;
padding: var(--bp-spacing-xs) 0;
border-bottom: 1px solid var(--bp-border);
}
.about-row:last-child {
border-bottom: none;
}
.about-label {
color: var(--bp-text-muted);
font-size: 0.9rem;
}
.about-value {
color: var(--bp-text);
font-weight: 500;
font-size: 0.9rem;
}
.about-actions {
display: flex;
gap: var(--bp-spacing-md);
flex-wrap: wrap;
}
/* Footer controller hints */
.bp-footer {
position: relative;
+203 -13
View File
@@ -211,22 +211,212 @@
<h1 class="section-title">Settings</h1>
<p class="section-subtitle">Configure your browser</p>
</div>
<div class="settings-grid">
<div class="settings-card" data-focusable tabindex="0" data-action="theme">
<!-- Settings categories navigation -->
<div class="settings-tabs">
<button class="settings-tab active" data-settings-tab="themes" data-focusable tabindex="0">
<span class="material-symbols-outlined">palette</span>
<span class="settings-label">Themes</span>
</div>
<div class="settings-card" data-focusable tabindex="0" data-action="privacy">
<span class="material-symbols-outlined">shield</span>
<span class="settings-label">Privacy</span>
</div>
<div class="settings-card" data-focusable tabindex="0" data-action="display">
<span>Themes</span>
</button>
<button class="settings-tab" data-settings-tab="display" data-focusable tabindex="0">
<span class="material-symbols-outlined">display_settings</span>
<span class="settings-label">Display</span>
<span>Display</span>
</button>
<button class="settings-tab" data-settings-tab="privacy" data-focusable tabindex="0">
<span class="material-symbols-outlined">shield</span>
<span>Privacy</span>
</button>
<button class="settings-tab" data-settings-tab="about" data-focusable tabindex="0">
<span class="material-symbols-outlined">info</span>
<span>About</span>
</button>
</div>
<!-- Settings panels -->
<div class="settings-panels">
<!-- Themes Panel -->
<div class="settings-panel active" id="settings-panel-themes">
<h2 class="settings-panel-title">Theme Presets</h2>
<div class="theme-grid" id="bp-theme-grid">
<button class="theme-card" data-theme="default" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #121418, #1B1035);"></div>
<span class="theme-name">Default</span>
</button>
<button class="theme-card" data-theme="ocean" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #1a365d, #2c5282);"></div>
<span class="theme-name">Ocean</span>
</button>
<button class="theme-card" data-theme="forest" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #1a202c, #2d3748);"></div>
<span class="theme-name">Forest</span>
</button>
<button class="theme-card" data-theme="sunset" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #744210, #c05621);"></div>
<span class="theme-name">Sunset</span>
</button>
<button class="theme-card" data-theme="cyberpunk" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #0a0a0a, #2a0a3a);"></div>
<span class="theme-name">Cyberpunk</span>
</button>
<button class="theme-card" data-theme="midnight-rose" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #1c1820, #3d3046);"></div>
<span class="theme-name">Midnight Rose</span>
</button>
<button class="theme-card" data-theme="arctic-ice" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #f0f8ff, #d1e7ff);"></div>
<span class="theme-name">Arctic Ice</span>
</button>
<button class="theme-card" data-theme="cherry-blossom" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #fff5f8, #ffd4db);"></div>
<span class="theme-name">Cherry Blossom</span>
</button>
<button class="theme-card" data-theme="cosmic-purple" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #0f0524, #2d1b69);"></div>
<span class="theme-name">Cosmic Purple</span>
</button>
<button class="theme-card" data-theme="emerald-dream" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #0d2818, #2d5a44);"></div>
<span class="theme-name">Emerald Dream</span>
</button>
<button class="theme-card" data-theme="mocha-coffee" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #3c2414, #5d3a26);"></div>
<span class="theme-name">Mocha Coffee</span>
</button>
<button class="theme-card" data-theme="lavender-fields" data-focusable tabindex="0">
<div class="theme-preview" style="background: linear-gradient(145deg, #f8f4ff, #e6d8ff);"></div>
<span class="theme-name">Lavender Fields</span>
</button>
</div>
</div>
<div class="settings-card" data-focusable tabindex="0" data-action="exit-bigpicture">
<span class="material-symbols-outlined">desktop_windows</span>
<span class="settings-label">Desktop Mode</span>
<!-- Display Panel -->
<div class="settings-panel" id="settings-panel-display">
<h2 class="settings-panel-title">Display Settings</h2>
<div class="settings-option">
<div class="option-info">
<span class="material-symbols-outlined">zoom_in</span>
<div class="option-text">
<span class="option-label">Display Scale</span>
<span class="option-description">Adjust the default zoom level</span>
</div>
</div>
<div class="option-control">
<button class="scale-btn" id="bp-scale-down" data-focusable tabindex="0">
<span class="material-symbols-outlined">remove</span>
</button>
<span class="scale-value" id="bp-scale-value">100%</span>
<button class="scale-btn" id="bp-scale-up" data-focusable tabindex="0">
<span class="material-symbols-outlined">add</span>
</button>
</div>
</div>
<div class="settings-option">
<div class="option-info">
<span class="material-symbols-outlined">desktop_windows</span>
<div class="option-text">
<span class="option-label">Exit Big Picture Mode</span>
<span class="option-description">Return to standard desktop interface</span>
</div>
</div>
<div class="option-control">
<button class="action-button" id="bp-exit-desktop" data-focusable tabindex="0">
<span class="material-symbols-outlined">logout</span>
<span>Exit</span>
</button>
</div>
</div>
</div>
<!-- Privacy Panel -->
<div class="settings-panel" id="settings-panel-privacy">
<h2 class="settings-panel-title">Privacy & Data</h2>
<div class="settings-option">
<div class="option-info">
<span class="material-symbols-outlined">delete_sweep</span>
<div class="option-text">
<span class="option-label">Clear Browsing Data</span>
<span class="option-description">Delete cookies, cache, and site data</span>
</div>
</div>
<div class="option-control">
<button class="action-button danger" id="bp-clear-data" data-focusable tabindex="0">
<span class="material-symbols-outlined">delete</span>
<span>Clear All</span>
</button>
</div>
</div>
<div class="settings-option">
<div class="option-info">
<span class="material-symbols-outlined">history</span>
<div class="option-text">
<span class="option-label">Clear History</span>
<span class="option-description">Delete browsing history</span>
</div>
</div>
<div class="option-control">
<button class="action-button" id="bp-clear-history" data-focusable tabindex="0">
<span class="material-symbols-outlined">delete</span>
<span>Clear</span>
</button>
</div>
</div>
<div class="settings-option">
<div class="option-info">
<span class="material-symbols-outlined">search_off</span>
<div class="option-text">
<span class="option-label">Clear Search History</span>
<span class="option-description">Delete search query history</span>
</div>
</div>
<div class="option-control">
<button class="action-button" id="bp-clear-search" data-focusable tabindex="0">
<span class="material-symbols-outlined">delete</span>
<span>Clear</span>
</button>
</div>
</div>
</div>
<!-- About Panel -->
<div class="settings-panel" id="settings-panel-about">
<h2 class="settings-panel-title">About Nebula Browser</h2>
<div class="about-info">
<div class="about-logo">
<img src="../assets/images/Logos/Nebula-Icon.svg" alt="Nebula" class="about-logo-img">
<div class="about-title">
<h3>Nebula Browser</h3>
<span id="bp-version">Version loading...</span>
</div>
</div>
<div class="about-details">
<div class="about-row">
<span class="about-label">Electron</span>
<span class="about-value" id="bp-electron-version">--</span>
</div>
<div class="about-row">
<span class="about-label">Chromium</span>
<span class="about-value" id="bp-chromium-version">--</span>
</div>
<div class="about-row">
<span class="about-label">Node.js</span>
<span class="about-value" id="bp-node-version">--</span>
</div>
<div class="about-row">
<span class="about-label">Platform</span>
<span class="about-value" id="bp-platform">--</span>
</div>
</div>
<div class="about-actions">
<button class="action-button" id="bp-github-link" data-focusable tabindex="0">
<span class="ri-github-fill" style="font-size: 20px;"></span>
<span>GitHub</span>
</button>
<button class="action-button" id="bp-copy-diagnostics" data-focusable tabindex="0">
<span class="material-symbols-outlined">content_copy</span>
<span>Copy Info</span>
</button>
</div>
</div>
</div>
</div>
</section>
+502 -3
View File
@@ -1184,6 +1184,7 @@ async function loadData() {
await loadBookmarks();
await loadHistory();
renderQuickAccess();
initSettings();
}
async function loadBookmarks() {
@@ -1724,13 +1725,13 @@ function exitBigPictureMode() {
function handleSettingsAction(action) {
switch (action) {
case 'theme':
showToast('Theme settings coming soon');
switchSettingsTab('themes');
break;
case 'privacy':
showToast('Privacy settings coming soon');
switchSettingsTab('privacy');
break;
case 'display':
showToast('Display settings coming soon');
switchSettingsTab('display');
break;
case 'exit-bigpicture':
exitBigPictureMode();
@@ -1740,6 +1741,504 @@ function handleSettingsAction(action) {
}
}
// =============================================================================
// SETTINGS FUNCTIONALITY
// =============================================================================
const DISPLAY_SCALE_KEY = 'nebula-display-scale';
let currentDisplayScale = 100;
let currentThemeName = 'default';
// Theme definitions (matching customization.js)
const THEMES = {
default: {
name: 'Default',
colors: {
bg: '#121418',
darkPurple: '#1B1035',
primary: '#7B2EFF',
accent: '#00C6FF',
text: '#E0E0E0'
}
},
ocean: {
name: 'Ocean',
colors: {
bg: '#1a365d',
darkPurple: '#2c5282',
primary: '#3182ce',
accent: '#00d9ff',
text: '#e2e8f0'
}
},
forest: {
name: 'Forest',
colors: {
bg: '#1a202c',
darkPurple: '#2d3748',
primary: '#68d391',
accent: '#9ae6b4',
text: '#f7fafc'
}
},
sunset: {
name: 'Sunset',
colors: {
bg: '#744210',
darkPurple: '#c05621',
primary: '#ed8936',
accent: '#fbb040',
text: '#fffaf0'
}
},
cyberpunk: {
name: 'Cyberpunk',
colors: {
bg: '#0a0a0a',
darkPurple: '#2a0a3a',
primary: '#ff0080',
accent: '#00ffff',
text: '#ffffff'
}
},
'midnight-rose': {
name: 'Midnight Rose',
colors: {
bg: '#1c1820',
darkPurple: '#3d3046',
primary: '#d4af37',
accent: '#ffd700',
text: '#f5f5dc'
}
},
'arctic-ice': {
name: 'Arctic Ice',
colors: {
bg: '#f0f8ff',
darkPurple: '#d1e7ff',
primary: '#4169e1',
accent: '#87ceeb',
text: '#2f4f4f'
}
},
'cherry-blossom': {
name: 'Cherry Blossom',
colors: {
bg: '#fff5f8',
darkPurple: '#ffd4db',
primary: '#ff69b4',
accent: '#ffb6c1',
text: '#8b4513'
}
},
'cosmic-purple': {
name: 'Cosmic Purple',
colors: {
bg: '#0f0524',
darkPurple: '#2d1b69',
primary: '#9400d3',
accent: '#da70d6',
text: '#e6e6fa'
}
},
'emerald-dream': {
name: 'Emerald Dream',
colors: {
bg: '#0d2818',
darkPurple: '#2d5a44',
primary: '#50c878',
accent: '#00fa9a',
text: '#f0fff0'
}
},
'mocha-coffee': {
name: 'Mocha Coffee',
colors: {
bg: '#3c2414',
darkPurple: '#5d3a26',
primary: '#d2691e',
accent: '#deb887',
text: '#faf0e6'
}
},
'lavender-fields': {
name: 'Lavender Fields',
colors: {
bg: '#f8f4ff',
darkPurple: '#e6d8ff',
primary: '#9370db',
accent: '#dda0dd',
text: '#4b0082'
}
}
};
function initSettings() {
console.log('[BigPicture] Initializing settings...');
// Load saved settings
loadSavedSettings();
// Initialize settings tabs
initSettingsTabs();
// Initialize theme selection
initThemeSelection();
// Initialize display scale controls
initDisplayScaleControls();
// Initialize privacy controls
initPrivacyControls();
// Initialize about panel
initAboutPanel();
}
function loadSavedSettings() {
// Load display scale
try {
const savedScale = localStorage.getItem(DISPLAY_SCALE_KEY);
if (savedScale) {
currentDisplayScale = parseInt(savedScale, 10);
updateScaleDisplay();
}
} catch (err) {
console.warn('[BigPicture] Failed to load display scale:', err);
}
// Load theme
try {
const savedTheme = localStorage.getItem('nebula-theme-name');
if (savedTheme && THEMES[savedTheme]) {
currentThemeName = savedTheme;
applyTheme(THEMES[savedTheme]);
highlightActiveTheme();
}
} catch (err) {
console.warn('[BigPicture] Failed to load theme:', err);
}
}
function initSettingsTabs() {
document.querySelectorAll('.settings-tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.dataset.settingsTab;
if (tabName) {
switchSettingsTab(tabName);
}
});
});
}
function switchSettingsTab(tabName) {
// Update tab buttons
document.querySelectorAll('.settings-tab').forEach(tab => {
tab.classList.toggle('active', tab.dataset.settingsTab === tabName);
});
// Update panels
document.querySelectorAll('.settings-panel').forEach(panel => {
panel.classList.toggle('active', panel.id === `settings-panel-${tabName}`);
});
// Update focusable elements
setTimeout(() => {
updateFocusableElements();
}, 50);
playNavSound();
}
function initThemeSelection() {
document.querySelectorAll('.theme-card').forEach(card => {
card.addEventListener('click', () => {
const themeName = card.dataset.theme;
if (themeName && THEMES[themeName]) {
selectTheme(themeName);
}
});
});
// Highlight current theme
highlightActiveTheme();
}
function selectTheme(themeName) {
if (!THEMES[themeName]) return;
currentThemeName = themeName;
const theme = THEMES[themeName];
// Apply theme locally
applyTheme(theme);
// Save to localStorage
try {
localStorage.setItem('nebula-theme-name', themeName);
// Also save the full theme data for other pages
const fullThemeData = {
name: theme.name,
colors: {
bg: theme.colors.bg,
darkBlue: theme.colors.darkPurple,
darkPurple: theme.colors.darkPurple,
primary: theme.colors.primary,
accent: theme.colors.accent,
text: theme.colors.text,
urlBarBg: theme.colors.darkPurple,
urlBarText: theme.colors.text,
urlBarBorder: theme.colors.primary,
tabBg: theme.colors.darkPurple,
tabText: theme.colors.text,
tabActive: theme.colors.bg,
tabActiveText: theme.colors.text,
tabBorder: theme.colors.bg
},
gradient: `linear-gradient(145deg, ${theme.colors.bg} 0%, ${theme.colors.darkPurple} 100%)`
};
localStorage.setItem('browserTheme', JSON.stringify(fullThemeData));
} catch (err) {
console.warn('[BigPicture] Failed to save theme:', err);
}
// Notify main process
if (ipcRenderer && ipcRenderer.send) {
ipcRenderer.send('theme-changed', {
name: themeName,
colors: theme.colors
});
}
highlightActiveTheme();
showToast(`Theme changed to ${theme.name}`);
playSelectSound();
}
function highlightActiveTheme() {
document.querySelectorAll('.theme-card').forEach(card => {
card.classList.toggle('active', card.dataset.theme === currentThemeName);
});
}
function initDisplayScaleControls() {
const scaleDown = document.getElementById('bp-scale-down');
const scaleUp = document.getElementById('bp-scale-up');
const exitDesktop = document.getElementById('bp-exit-desktop');
if (scaleDown) {
scaleDown.addEventListener('click', () => {
adjustDisplayScale(-10);
});
}
if (scaleUp) {
scaleUp.addEventListener('click', () => {
adjustDisplayScale(10);
});
}
if (exitDesktop) {
exitDesktop.addEventListener('click', () => {
exitBigPictureMode();
});
}
updateScaleDisplay();
}
function adjustDisplayScale(delta) {
const newScale = Math.min(300, Math.max(50, currentDisplayScale + delta));
if (newScale !== currentDisplayScale) {
currentDisplayScale = newScale;
updateScaleDisplay();
saveDisplayScale();
showToast(`Display scale: ${currentDisplayScale}%`);
playNavSound();
}
}
function updateScaleDisplay() {
const scaleValue = document.getElementById('bp-scale-value');
if (scaleValue) {
scaleValue.textContent = `${currentDisplayScale}%`;
}
}
function saveDisplayScale() {
try {
localStorage.setItem(DISPLAY_SCALE_KEY, currentDisplayScale.toString());
// Notify main process to update zoom level
if (ipcRenderer && ipcRenderer.send) {
ipcRenderer.send('set-display-scale', currentDisplayScale);
}
} catch (err) {
console.warn('[BigPicture] Failed to save display scale:', err);
}
}
function initPrivacyControls() {
const clearDataBtn = document.getElementById('bp-clear-data');
const clearHistoryBtn = document.getElementById('bp-clear-history');
const clearSearchBtn = document.getElementById('bp-clear-search');
if (clearDataBtn) {
clearDataBtn.addEventListener('click', async () => {
if (await confirmAction('Clear all browsing data? This cannot be undone.')) {
await clearAllBrowsingData();
}
});
}
if (clearHistoryBtn) {
clearHistoryBtn.addEventListener('click', async () => {
if (await confirmAction('Clear browsing history?')) {
await clearBrowsingHistory();
}
});
}
if (clearSearchBtn) {
clearSearchBtn.addEventListener('click', async () => {
if (await confirmAction('Clear search history?')) {
await clearSearchHistory();
}
});
}
}
async function confirmAction(message) {
// Simple confirmation using toast - could be enhanced with a modal
showToast(message + ' Press A to confirm.');
return true; // For now, auto-confirm. Could implement modal confirmation.
}
async function clearAllBrowsingData() {
try {
showToast('Clearing all browsing data...');
if (ipcRenderer && ipcRenderer.invoke) {
await ipcRenderer.invoke('clear-browser-data');
}
// Also clear localStorage
localStorage.removeItem('siteHistory');
state.history = [];
renderHistory();
renderRecentSites();
showToast('All browsing data cleared');
playSelectSound();
} catch (err) {
console.error('[BigPicture] Failed to clear browsing data:', err);
showToast('Failed to clear data');
}
}
async function clearBrowsingHistory() {
try {
if (ipcRenderer && ipcRenderer.invoke) {
await ipcRenderer.invoke('clear-site-history');
}
localStorage.removeItem('siteHistory');
state.history = [];
renderHistory();
renderRecentSites();
showToast('Browsing history cleared');
playSelectSound();
} catch (err) {
console.error('[BigPicture] Failed to clear history:', err);
showToast('Failed to clear history');
}
}
async function clearSearchHistory() {
try {
if (ipcRenderer && ipcRenderer.invoke) {
await ipcRenderer.invoke('clear-search-history');
}
showToast('Search history cleared');
playSelectSound();
} catch (err) {
console.error('[BigPicture] Failed to clear search history:', err);
showToast('Failed to clear search history');
}
}
async function initAboutPanel() {
// Load version info
try {
if (ipcRenderer && ipcRenderer.invoke) {
const appInfo = await ipcRenderer.invoke('get-app-info');
if (appInfo) {
const versionEl = document.getElementById('bp-version');
const electronEl = document.getElementById('bp-electron-version');
const chromiumEl = document.getElementById('bp-chromium-version');
const nodeEl = document.getElementById('bp-node-version');
const platformEl = document.getElementById('bp-platform');
if (versionEl) versionEl.textContent = `Version ${appInfo.version || 'Unknown'}`;
if (electronEl) electronEl.textContent = appInfo.electron || '--';
if (chromiumEl) chromiumEl.textContent = appInfo.chrome || '--';
if (nodeEl) nodeEl.textContent = appInfo.node || '--';
if (platformEl) platformEl.textContent = `${appInfo.platform || ''} ${appInfo.arch || ''}`.trim() || '--';
}
}
} catch (err) {
console.warn('[BigPicture] Failed to load app info:', err);
}
// GitHub link
const githubBtn = document.getElementById('bp-github-link');
if (githubBtn) {
githubBtn.addEventListener('click', () => {
navigateTo('https://github.com/Bobbybear007/NebulaBrowser');
});
}
// Copy diagnostics
const copyBtn = document.getElementById('bp-copy-diagnostics');
if (copyBtn) {
copyBtn.addEventListener('click', async () => {
await copyDiagnostics();
});
}
}
async function copyDiagnostics() {
try {
const versionEl = document.getElementById('bp-version');
const electronEl = document.getElementById('bp-electron-version');
const chromiumEl = document.getElementById('bp-chromium-version');
const nodeEl = document.getElementById('bp-node-version');
const platformEl = document.getElementById('bp-platform');
const diagnostics = [
'Nebula Browser Diagnostics',
'========================',
versionEl ? versionEl.textContent : '',
`Electron: ${electronEl ? electronEl.textContent : '--'}`,
`Chromium: ${chromiumEl ? chromiumEl.textContent : '--'}`,
`Node.js: ${nodeEl ? nodeEl.textContent : '--'}`,
`Platform: ${platformEl ? platformEl.textContent : '--'}`,
`Date: ${new Date().toISOString()}`
].join('\n');
await navigator.clipboard.writeText(diagnostics);
showToast('Diagnostics copied to clipboard');
playSelectSound();
} catch (err) {
console.error('[BigPicture] Failed to copy diagnostics:', err);
showToast('Failed to copy diagnostics');
}
}
// =============================================================================
// UTILITIES
// =============================================================================