Improve theme and navigation IPC between pages

Enhanced inter-process communication for theme updates and navigation between home, settings, and main browser pages. Theme changes in settings now propagate to home via Electron IPC and postMessage fallback. Navigation requests from home and other webviews are handled more robustly, supporting both Electron IPC and postMessage. Also refactored tab creation and improved event handling for better reliability.
This commit is contained in:
2025-07-31 22:40:37 +12:00
parent aba6145bea
commit e8dc253f03
6 changed files with 109 additions and 47 deletions
+6 -3
View File
@@ -478,10 +478,13 @@ class BrowserCustomizer {
applyThemeToPages() {
// This will be called to apply theme to home.html and other pages
// We'll store the theme and let other pages load it
this.saveTheme();
// If we have access to other windows/frames, apply there too
// Send theme update to host (for settings webview)
if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') {
window.electronAPI.sendToHost('theme-update', this.currentTheme);
}
// Fallback: send via postMessage (for iframe embedding)
try {
if (window.parent && window.parent !== window) {
window.parent.postMessage({
+23 -20
View File
@@ -76,36 +76,39 @@
<!-- Theme loader script -->
<script src="customization.js"></script>
<script>
// Apply saved theme on page load
// Apply saved theme on page load and listen for updates
document.addEventListener('DOMContentLoaded', () => {
BrowserCustomizer.applyThemeToPage();
// Listen for theme updates from settings
// Function to update logo and title based on theme
function updateLogoAndTitle(theme) {
const logoText = document.querySelector('.logo-text');
const logoImg = document.querySelector('.logo-img');
if (logoText) {
logoText.textContent = theme.customTitle || 'Nebula Browser';
}
if (logoImg) {
logoImg.style.display = theme.showLogo ? 'block' : 'none';
}
}
// Listen for theme updates via postMessage fallback
window.addEventListener('message', (event) => {
if (event.data.type === 'theme-update') {
const theme = event.data.theme;
localStorage.setItem('currentTheme', JSON.stringify(theme));
BrowserCustomizer.applyThemeToPage();
// Update logo and title if needed
updateLogoAndTitle(theme);
}
});
// Update logo and title based on theme
function updateLogoAndTitle(theme) {
const logoText = document.querySelector('.logo-text');
const logoImg = document.querySelector('.logo-img');
if (logoText) {
logoText.textContent = theme.customTitle || 'Nebula Browser';
}
if (!theme.showLogo && logoImg) {
logoImg.style.display = 'none';
} else if (logoImg) {
logoImg.style.display = 'block';
}
// Listen for theme updates via Electron IPC
if (window.electronAPI && typeof window.electronAPI.on === 'function') {
window.electronAPI.on('theme-update', (theme) => {
localStorage.setItem('currentTheme', JSON.stringify(theme));
BrowserCustomizer.applyThemeToPage();
updateLogoAndTitle(theme);
});
}
});
</script>
+16 -2
View File
@@ -87,11 +87,16 @@ function renderBookmarks() {
// Navigate via IPC to host page
box.onclick = () => {
const url = b.url;
if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') {
window.electronAPI.sendToHost('navigate', b.url);
window.electronAPI.sendToHost('navigate', url);
} else {
console.error('Unable to send navigation IPC to host');
}
// Fallback: post message to embedding page
if (window.parent && typeof window.parent.postMessage === 'function') {
window.parent.postMessage({ type: 'navigate', url }, '*');
}
};
box.appendChild(label);
@@ -205,7 +210,16 @@ searchBtn.addEventListener('click', () => {
const searchEngineUrl = searchEngines[selectedSearchEngine];
target = `${searchEngineUrl}${encodeURIComponent(input)}`;
}
window.location.href = target;
// Always send navigation request to host
if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') {
window.electronAPI.sendToHost('navigate', target);
return;
}
// Fallback: post message to embedding page
if (window.parent && typeof window.parent.postMessage === 'function') {
window.parent.postMessage({ type: 'navigate', url: target }, '*');
return;
}
});
searchInput.addEventListener('keydown', e => {
+50 -19
View File
@@ -75,7 +75,7 @@ async function saveBookmarks(newBookmarks) {
// Load bookmarks when the script starts
loadBookmarks();
// Home tab will be created on DOMContentLoaded event
// Initial home tab will be created on DOMContentLoaded
// Remove iframe-based navigation listener (using webview IPC now)
@@ -108,6 +108,8 @@ function createTab(inputUrl) {
};
tabs.push(tab);
setActiveTab(id);
// Render the tab bar so the new home tab appears
renderTabs();
return id;
}
@@ -116,16 +118,6 @@ function createTab(inputUrl) {
console.log('[DEBUG] createTab() resolvedUrl =', resolvedUrl);
const webview = document.createElement('webview');
webview.id = `tab-${id}`;
webview.src = resolvedUrl;
webview.setAttribute('allowpopups', '');
webview.setAttribute('partition', 'persist:default');
webview.setAttribute('preload', '../preload.js');
webview.classList.add('active');
webview.addEventListener('did-fail-load', handleLoadFail(id));
webview.addEventListener('page-title-updated', e => updateTabMetadata(id, 'title', e.title));
webview.addEventListener('page-favicon-updated', e => {
if (e.favicons.length > 0) updateTabMetadata(id, 'favicon', e.favicons[0]);
});
@@ -186,6 +178,14 @@ function createTab(inputUrl) {
createTab(e.url);
});
// After creating dynamic webview:
webview.addEventListener('ipc-message', e => {
if (e.channel === 'theme-update') {
const home = document.getElementById('home-webview');
if (home) home.send('theme-update', ...e.args);
}
});
webviewsEl.appendChild(webview);
tabs.push({
@@ -324,6 +324,14 @@ function convertHomeTabToWebview(tabId, inputUrl, resolvedUrl) {
createTab(e.url);
});
// After creating dynamic webview:
webview.addEventListener('ipc-message', e => {
if (e.channel === 'theme-update') {
const home = document.getElementById('home-webview');
if (home) home.send('theme-update', ...e.args);
}
});
// Add webview to DOM
webviewsEl.appendChild(webview);
@@ -486,7 +494,9 @@ function renderTabs() {
});
// add the “+” at the end
const plus = document.createElement('div');
plus.className = 'tab'; plus.textContent = '+'; plus.onclick = () => createTab();
plus.className = 'tab';
plus.textContent = '+';
plus.onclick = () => createTab();
frag.appendChild(plus);
tabBarEl.innerHTML = ''; // clear once
@@ -533,10 +543,9 @@ function reload() {
}
}
// Function to open the Settings page
function openSettings() {
urlBox.value = 'browser://settings';
navigate();
createTab('browser://settings');
}
// Toggle menu dropdown
@@ -562,16 +571,38 @@ window.addEventListener('DOMContentLoaded', () => {
}
createTab();
// Listen for navigation IPC messages from home webview
const homeWebview = document.getElementById('home-webview');
if (homeWebview) {
homeWebview.addEventListener('ipc-message', e => {
// Handle IPC messages from the static home webview (bookmarks navigation)
const staticHome = document.getElementById('home-webview');
if (staticHome) {
staticHome.addEventListener('ipc-message', (e) => {
if (e.channel === 'navigate' && e.args[0]) {
urlBox.value = e.args[0];
navigate();
}
});
}
// Listen for IPC messages from other webviews (e.g., settings)
webviewsEl.addEventListener('ipc-message', (e) => {
// Navigation messages from home or other pages
if (e.channel === 'navigate' && e.args[0]) {
urlBox.value = e.args[0];
navigate();
}
// Theme update from settings webview
if (e.channel === 'theme-update' && e.args[0]) {
const homeWebview = document.getElementById('home-webview');
if (homeWebview) {
homeWebview.send('theme-update', e.args[0]);
}
}
});
// Fallback: listen for postMessage navigations from home webview
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'navigate' && event.data.url) {
urlBox.value = event.data.url;
navigate();
}
});
// only now bind the reload button (guaranteed to exist)
const reloadBtn = document.getElementById('reload-btn');
reloadBtn.addEventListener('click', reload);
+9 -3
View File
@@ -1,4 +1,5 @@
// Use require('electron') since webviews have nodeIntegrationInSubFrames: true
// In settings webview we use the same preload API as main windows
const { ipcRenderer } = require('electron');
const clearBtn = document.getElementById('clear-data-btn');
@@ -14,11 +15,10 @@ function showStatus(message) {
}
clearBtn.onclick = async () => {
statusDiv.classList.remove('hidden'); // Show spinner immediately
statusText.textContent = 'Clearing all browser data...'; // Update text while clearing
statusDiv.classList.remove('hidden');
statusText.textContent = 'Clearing all browser data...';
try {
// Invoke the main process to clear cookies, local storage, and cache
const ok = await ipcRenderer.invoke('clear-browser-data');
showStatus(ok
? 'All browser data and bookmarks cleared!'
@@ -26,5 +26,11 @@ clearBtn.onclick = async () => {
} catch (error) {
console.error('Error clearing browser data:', error);
showStatus('An error occurred while clearing data.');
} finally {
// Send theme update to host after clearing
const currentTheme = window.browserCustomizer ? window.browserCustomizer.currentTheme : null;
if (currentTheme && window.electronAPI && typeof window.electronAPI.sendToHost === 'function') {
window.electronAPI.sendToHost('theme-update', currentTheme);
}
}
};