Refactor site history to use localStorage and improve UI
Site history is now managed in localStorage for faster access and better cross-tab experience, with file sync for persistence. Added a dropdown for quick site history access in the address bar, updated settings page to display and manage site history from localStorage, and improved history recording logic in both main and renderer processes. Also added debug info and test data utilities for development.
This commit is contained in:
@@ -6,13 +6,11 @@ A customizable and privacy-focused web browser built with Electron.
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Custom Theming:** Personalize your browsing experience with custom themes.
|
|
||||||
* **Privacy Control:** Easily clear your browsing data, including history, cookies, and cache.
|
* **Privacy Control:** Easily clear your browsing data, including history, cookies, and cache.
|
||||||
* **Tab Management:** Open new tabs, and manage them efficiently.
|
* **Tab Management:** Open new tabs, and manage them efficiently.
|
||||||
* **History:** Keeps track of your browsing and search history.
|
|
||||||
* **Bookmarks:** Save your favorite sites.
|
* **Bookmarks:** Save your favorite sites.
|
||||||
* **Custom Start Page:** Set your own home page.
|
|
||||||
* **Zoom Controls:** Adjust the zoom level of pages.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|||||||
@@ -82,20 +82,55 @@ function createWindow(startUrl) {
|
|||||||
|
|
||||||
// ensure all embedded <webview> tags also use the same window
|
// ensure all embedded <webview> tags also use the same window
|
||||||
win.webContents.on('did-attach-webview', (event, webContents) => {
|
win.webContents.on('did-attach-webview', (event, webContents) => {
|
||||||
|
// Set up webview with preload script to provide electronAPI
|
||||||
|
webContents.on('dom-ready', () => {
|
||||||
|
webContents.executeJavaScript(`
|
||||||
|
window.electronAPI = {
|
||||||
|
invoke: (channel, ...args) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
ipcRenderer.invoke(channel, ...args).then(resolve).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
// intercept window.open() inside webview
|
// intercept window.open() inside webview
|
||||||
webContents.setWindowOpenHandler(({ url }) => {
|
webContents.setWindowOpenHandler(({ url }) => {
|
||||||
webContents.loadURL(url);
|
webContents.loadURL(url);
|
||||||
|
// record history for webview navigations
|
||||||
|
recordHistory('site-history.json', url);
|
||||||
|
const m = /[?&](?:q|query)=([^&]+)/.exec(url);
|
||||||
|
if (m && m[1]) {
|
||||||
|
const query = decodeURIComponent(m[1].replace(/\+/g, ' '));
|
||||||
|
recordHistory('search-history.json', query);
|
||||||
|
}
|
||||||
return { action: 'deny' };
|
return { action: 'deny' };
|
||||||
});
|
});
|
||||||
// intercept legacy new-window on webview
|
// intercept legacy new-window on webview
|
||||||
webContents.on('new-window', (e, url) => {
|
webContents.on('new-window', (e, url) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
webContents.loadURL(url);
|
webContents.loadURL(url);
|
||||||
|
// record history for webview navigations
|
||||||
|
recordHistory('site-history.json', url);
|
||||||
|
const m = /[?&](?:q|query)=([^&]+)/.exec(url);
|
||||||
|
if (m && m[1]) {
|
||||||
|
const query = decodeURIComponent(m[1].replace(/\+/g, ' '));
|
||||||
|
recordHistory('search-history.json', query);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// intercept navigation on webview (e.g. user clicks link)
|
// intercept navigation on webview (e.g. user clicks link)
|
||||||
webContents.on('will-navigate', (e, url) => {
|
webContents.on('will-navigate', (e, url) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
webContents.loadURL(url);
|
webContents.loadURL(url);
|
||||||
|
// record history for webview navigations
|
||||||
|
recordHistory('site-history.json', url);
|
||||||
|
const m = /[?&](?:q|query)=([^&]+)/.exec(url);
|
||||||
|
if (m && m[1]) {
|
||||||
|
const query = decodeURIComponent(m[1].replace(/\+/g, ' '));
|
||||||
|
recordHistory('search-history.json', query);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -116,13 +151,28 @@ function createWindow(startUrl) {
|
|||||||
|
|
||||||
// record site and search history on every navigation
|
// record site and search history on every navigation
|
||||||
const recordHistory = async (fileName, entry) => {
|
const recordHistory = async (fileName, entry) => {
|
||||||
const filePath = path.join(__dirname, fileName);
|
if (fileName === 'site-history.json') {
|
||||||
let data = [];
|
// Save to both file and send to renderer
|
||||||
try { data = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch {}
|
const filePath = path.join(__dirname, fileName);
|
||||||
if (data[0] !== entry) {
|
let data = [];
|
||||||
data.unshift(entry);
|
try { data = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch {}
|
||||||
if (data.length > 100) data.pop();
|
if (data[0] !== entry) {
|
||||||
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
data.unshift(entry);
|
||||||
|
if (data.length > 100) data.pop();
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||||
|
}
|
||||||
|
// Also send to renderer for localStorage
|
||||||
|
win.webContents.send('record-site-history', entry);
|
||||||
|
} else {
|
||||||
|
// Keep search history in JSON file for now
|
||||||
|
const filePath = path.join(__dirname, fileName);
|
||||||
|
let data = [];
|
||||||
|
try { data = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch {}
|
||||||
|
if (data[0] !== entry) {
|
||||||
|
data.unshift(entry);
|
||||||
|
if (data.length > 100) data.pop();
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -168,7 +218,10 @@ ipcMain.handle('window-close', event => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Add site and search history IPC handlers
|
// Add site and search history IPC handlers
|
||||||
|
// Site history is now handled via localStorage in the renderer
|
||||||
|
// But keep these handlers for compatibility and potential future use
|
||||||
ipcMain.handle('load-site-history', async () => {
|
ipcMain.handle('load-site-history', async () => {
|
||||||
|
// Read from the site history file for settings page
|
||||||
const filePath = path.join(__dirname, 'site-history.json');
|
const filePath = path.join(__dirname, 'site-history.json');
|
||||||
try {
|
try {
|
||||||
const data = fs.readFileSync(filePath, 'utf-8');
|
const data = fs.readFileSync(filePath, 'utf-8');
|
||||||
@@ -179,6 +232,7 @@ ipcMain.handle('load-site-history', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('save-site-history', async (event, history) => {
|
ipcMain.handle('save-site-history', async (event, history) => {
|
||||||
|
// Save to both file and localStorage
|
||||||
const filePath = path.join(__dirname, 'site-history.json');
|
const filePath = path.join(__dirname, 'site-history.json');
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
fs.writeFileSync(filePath, JSON.stringify(history, null, 2));
|
||||||
@@ -188,6 +242,16 @@ ipcMain.handle('save-site-history', async (event, history) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('clear-site-history', async () => {
|
||||||
|
const filePath = path.join(__dirname, 'site-history.json');
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify([], null, 2));
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('load-search-history', async () => {
|
ipcMain.handle('load-search-history', async () => {
|
||||||
const filePath = path.join(__dirname, 'search-history.json');
|
const filePath = path.join(__dirname, 'search-history.json');
|
||||||
try {
|
try {
|
||||||
@@ -269,3 +333,29 @@ ipcMain.handle('zoom-out', event => {
|
|||||||
ipcMain.handle('open-tab-in-new-window', (event, url) => {
|
ipcMain.handle('open-tab-in-new-window', (event, url) => {
|
||||||
createWindow(url);
|
createWindow(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('save-site-history-entry', async (event, url) => {
|
||||||
|
const filePath = path.join(__dirname, 'site-history.json');
|
||||||
|
try {
|
||||||
|
let data = [];
|
||||||
|
try {
|
||||||
|
data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Remove if already exists to avoid duplicates
|
||||||
|
data = data.filter(item => item !== url);
|
||||||
|
// Add to beginning
|
||||||
|
data.unshift(url);
|
||||||
|
// Keep only last 100 entries
|
||||||
|
if (data.length > 100) {
|
||||||
|
data = data.slice(0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
||||||
|
console.log('[MAIN] Saved site history entry:', url);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[MAIN] Error saving site history entry:', err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
+195
-3
@@ -1,5 +1,33 @@
|
|||||||
const ipcRenderer = window.electronAPI;
|
const ipcRenderer = window.electronAPI;
|
||||||
|
|
||||||
|
// Site history management using localStorage
|
||||||
|
function getSiteHistory() {
|
||||||
|
try {
|
||||||
|
const history = localStorage.getItem('siteHistory');
|
||||||
|
return history ? JSON.parse(history) : [];
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error reading site history from localStorage:', err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToSiteHistory(url) {
|
||||||
|
try {
|
||||||
|
let history = getSiteHistory();
|
||||||
|
// Remove if already exists to avoid duplicates
|
||||||
|
history = history.filter(item => item !== url);
|
||||||
|
// Add to beginning
|
||||||
|
history.unshift(url);
|
||||||
|
// Keep only last 100 entries
|
||||||
|
if (history.length > 100) {
|
||||||
|
history = history.slice(0, 100);
|
||||||
|
}
|
||||||
|
localStorage.setItem('siteHistory', JSON.stringify(history));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error saving site history to localStorage:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1) cache hot DOM references
|
// 1) cache hot DOM references
|
||||||
const urlBox = document.getElementById('url');
|
const urlBox = document.getElementById('url');
|
||||||
const tabBarEl = document.getElementById('tab-bar');
|
const tabBarEl = document.getElementById('tab-bar');
|
||||||
@@ -8,13 +36,93 @@ const menuPopup = document.getElementById('menu-popup');
|
|||||||
const contextMenu = document.getElementById('context-menu');
|
const contextMenu = document.getElementById('context-menu');
|
||||||
const menuItems = contextMenu ? contextMenu.querySelectorAll('li') : [];
|
const menuItems = contextMenu ? contextMenu.querySelectorAll('li') : [];
|
||||||
|
|
||||||
|
let siteHistoryDropdown = null;
|
||||||
|
|
||||||
|
function initializeSiteHistoryDropdown() {
|
||||||
|
// Create site history dropdown
|
||||||
|
siteHistoryDropdown = document.createElement('div');
|
||||||
|
siteHistoryDropdown.id = 'site-history-dropdown';
|
||||||
|
siteHistoryDropdown.style.cssText = `
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-top: none;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 1000;
|
||||||
|
display: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (urlBox && urlBox.parentElement) {
|
||||||
|
urlBox.parentElement.style.position = 'relative';
|
||||||
|
urlBox.parentElement.appendChild(siteHistoryDropdown);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSiteHistory() {
|
||||||
|
if (!siteHistoryDropdown) return;
|
||||||
|
|
||||||
|
const history = getSiteHistory();
|
||||||
|
if (history.length === 0) {
|
||||||
|
siteHistoryDropdown.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
siteHistoryDropdown.innerHTML = '';
|
||||||
|
history.slice(0, 10).forEach(url => {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.style.cssText = `
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
`;
|
||||||
|
item.textContent = url;
|
||||||
|
item.addEventListener('mouseenter', () => item.style.backgroundColor = '#f0f0f0');
|
||||||
|
item.addEventListener('mouseleave', () => item.style.backgroundColor = 'white');
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
urlBox.value = url;
|
||||||
|
navigate();
|
||||||
|
hideSiteHistory();
|
||||||
|
});
|
||||||
|
siteHistoryDropdown.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
siteHistoryDropdown.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSiteHistory() {
|
||||||
|
if (siteHistoryDropdown) {
|
||||||
|
siteHistoryDropdown.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Select all text on focus and prevent mouseup from deselecting
|
// Select all text on focus and prevent mouseup from deselecting
|
||||||
urlBox.addEventListener('focus', () => urlBox.select());
|
urlBox.addEventListener('focus', () => {
|
||||||
|
urlBox.select();
|
||||||
|
showSiteHistory();
|
||||||
|
});
|
||||||
urlBox.addEventListener('mouseup', e => e.preventDefault());
|
urlBox.addEventListener('mouseup', e => e.preventDefault());
|
||||||
// Add Enter key navigation
|
// Add Enter key navigation
|
||||||
urlBox.addEventListener('keydown', (e) => {
|
urlBox.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
navigate();
|
navigate();
|
||||||
|
hideSiteHistory();
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
hideSiteHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hide history when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!urlBox.contains(e.target) && (!siteHistoryDropdown || !siteHistoryDropdown.contains(e.target))) {
|
||||||
|
hideSiteHistory();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -24,6 +132,12 @@ let activeTabId = null;
|
|||||||
const allowedInternalPages = ['settings', 'home'];
|
const allowedInternalPages = ['settings', 'home'];
|
||||||
let bookmarks = [];
|
let bookmarks = [];
|
||||||
|
|
||||||
|
// Listen for site history updates from main process
|
||||||
|
ipcRenderer.on('record-site-history', (event, url) => {
|
||||||
|
console.log('[DEBUG] Received site history update:', url);
|
||||||
|
addToSiteHistory(url);
|
||||||
|
});
|
||||||
|
|
||||||
function createTab(inputUrl) {
|
function createTab(inputUrl) {
|
||||||
inputUrl = inputUrl || 'browser://home';
|
inputUrl = inputUrl || 'browser://home';
|
||||||
console.log('[DEBUG] createTab() inputUrl =', inputUrl);
|
console.log('[DEBUG] createTab() inputUrl =', inputUrl);
|
||||||
@@ -45,8 +159,41 @@ function createTab(inputUrl) {
|
|||||||
if (e.favicons.length > 0) updateTabMetadata(id, 'favicon', e.favicons[0]);
|
if (e.favicons.length > 0) updateTabMetadata(id, 'favicon', e.favicons[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
webview.addEventListener('did-navigate', e => handleNavigation(id, e.url)); // was using inputUrl
|
// Consolidated navigation recording - only use did-navigate to avoid duplicates
|
||||||
webview.addEventListener('did-navigate-in-page', e => handleNavigation(id, e.url)); // was using inputUrl
|
webview.addEventListener('did-navigate', e => {
|
||||||
|
handleNavigation(id, e.url);
|
||||||
|
// Record ALL HTTP navigations
|
||||||
|
if (e.url.startsWith('http')) {
|
||||||
|
console.log('[DEBUG] Recording navigation to:', e.url);
|
||||||
|
addToSiteHistory(e.url);
|
||||||
|
// Also save to file for cross-context sharing
|
||||||
|
ipcRenderer.invoke('save-site-history-entry', e.url).catch(err =>
|
||||||
|
console.error('Failed to save to file:', err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
webview.addEventListener('did-navigate-in-page', e => {
|
||||||
|
handleNavigation(id, e.url);
|
||||||
|
// Record in-page navigations too
|
||||||
|
if (e.url.startsWith('http')) {
|
||||||
|
console.log('[DEBUG] Recording in-page navigation to:', e.url);
|
||||||
|
addToSiteHistory(e.url);
|
||||||
|
ipcRenderer.invoke('save-site-history-entry', e.url).catch(err =>
|
||||||
|
console.error('Failed to save to file:', err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also capture when pages finish loading
|
||||||
|
webview.addEventListener('did-finish-load', () => {
|
||||||
|
const currentUrl = webview.getURL();
|
||||||
|
if (currentUrl.startsWith('http') && !currentUrl.includes('browser://')) {
|
||||||
|
console.log('[DEBUG] Webview did-finish-load, recording:', currentUrl);
|
||||||
|
addToSiteHistory(currentUrl);
|
||||||
|
ipcRenderer.invoke('save-site-history-entry', currentUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// catch any target="_blank" or window.open() calls and open them as new tabs
|
// catch any target="_blank" or window.open() calls and open them as new tabs
|
||||||
webview.addEventListener('new-window', e => {
|
webview.addEventListener('new-window', e => {
|
||||||
@@ -131,6 +278,8 @@ function handleNavigation(tabId, newUrl) {
|
|||||||
const tab = tabs.find(t => t.id === tabId);
|
const tab = tabs.find(t => t.id === tabId);
|
||||||
if (!tab) return;
|
if (!tab) return;
|
||||||
|
|
||||||
|
console.log('[DEBUG] handleNavigation called with:', newUrl);
|
||||||
|
|
||||||
// --- record every real navigation into history ---
|
// --- record every real navigation into history ---
|
||||||
if (tab.history[tab.historyIndex] !== newUrl) {
|
if (tab.history[tab.historyIndex] !== newUrl) {
|
||||||
tab.history = tab.history.slice(0, tab.historyIndex + 1);
|
tab.history = tab.history.slice(0, tab.historyIndex + 1);
|
||||||
@@ -138,6 +287,18 @@ function handleNavigation(tabId, newUrl) {
|
|||||||
tab.historyIndex++;
|
tab.historyIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record site history in localStorage (skip internal pages and file:// URLs)
|
||||||
|
if (!newUrl.endsWith('home.html') &&
|
||||||
|
!newUrl.endsWith('settings.html') &&
|
||||||
|
!newUrl.startsWith('file://') &&
|
||||||
|
!newUrl.includes('browser://') &&
|
||||||
|
newUrl.startsWith('http')) {
|
||||||
|
console.log('[DEBUG] Adding to site history:', newUrl);
|
||||||
|
addToSiteHistory(newUrl);
|
||||||
|
// Also send to main process for file storage
|
||||||
|
ipcRenderer.invoke('save-site-history-entry', newUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// translate local files back to our browser:// scheme
|
// translate local files back to our browser:// scheme
|
||||||
const isHome = newUrl.endsWith('home.html');
|
const isHome = newUrl.endsWith('home.html');
|
||||||
const isSettings = newUrl.endsWith('settings.html');
|
const isSettings = newUrl.endsWith('settings.html');
|
||||||
@@ -305,6 +466,20 @@ menuBtn.addEventListener('click', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Initialize site history dropdown after DOM is ready
|
||||||
|
initializeSiteHistoryDropdown();
|
||||||
|
|
||||||
|
// Add some debug info
|
||||||
|
console.log('[DEBUG] Site history initialized, current entries:', getSiteHistory().length);
|
||||||
|
|
||||||
|
// Add test entries if none exist (for debugging)
|
||||||
|
if (getSiteHistory().length === 0) {
|
||||||
|
console.log('[DEBUG] No existing history, adding test entries for debugging');
|
||||||
|
addToSiteHistory('https://www.google.com');
|
||||||
|
addToSiteHistory('https://github.com');
|
||||||
|
console.log('[DEBUG] Test entries added, history now:', getSiteHistory());
|
||||||
|
}
|
||||||
|
|
||||||
createTab();
|
createTab();
|
||||||
// only now bind the reload button (guaranteed to exist)
|
// only now bind the reload button (guaranteed to exist)
|
||||||
const reloadBtn = document.getElementById('reload-btn');
|
const reloadBtn = document.getElementById('reload-btn');
|
||||||
@@ -401,6 +576,23 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Migrate existing site history from JSON file to localStorage (one-time migration)
|
||||||
|
const migrateSiteHistory = async () => {
|
||||||
|
try {
|
||||||
|
// Check if we already have data in localStorage
|
||||||
|
const existingHistory = getSiteHistory();
|
||||||
|
if (existingHistory.length === 0) {
|
||||||
|
// Try to load from the old JSON file system
|
||||||
|
console.log('Attempting to migrate site history from JSON file...');
|
||||||
|
// Since we can't access the file directly, we'll just start fresh
|
||||||
|
// The site-history.json file was the old method, localStorage is the new method
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Site history migration skipped:', err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
migrateSiteHistory();
|
||||||
|
|
||||||
// ipcRenderer.invoke('load-bookmarks').then(bs => {
|
// ipcRenderer.invoke('load-bookmarks').then(bs => {
|
||||||
// bookmarks = bs;
|
// bookmarks = bs;
|
||||||
// console.log('[DEBUG] Loaded bookmarks:', bookmarks);
|
// console.log('[DEBUG] Loaded bookmarks:', bookmarks);
|
||||||
|
|||||||
+188
-31
@@ -11,6 +11,7 @@
|
|||||||
h2 { border-bottom: 1px solid #ccc; padding-bottom: 5px; }
|
h2 { border-bottom: 1px solid #ccc; padding-bottom: 5px; }
|
||||||
ul { list-style: none; padding-left: 0; }
|
ul { list-style: none; padding-left: 0; }
|
||||||
li { padding: 5px 0; border-bottom: 1px solid #eee; }
|
li { padding: 5px 0; border-bottom: 1px solid #eee; }
|
||||||
|
.debug-info { background: #f0f0f0; padding: 10px; margin: 10px 0; font-family: monospace; font-size: 12px; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -23,6 +24,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="note">Settings are stored locally on this device.</p>
|
<p class="note">Settings are stored locally on this device.</p>
|
||||||
|
|
||||||
|
<div class="debug-info" id="debug-info">Loading debug info...</div>
|
||||||
|
|
||||||
|
<!-- add history views -->
|
||||||
|
<section>
|
||||||
|
<h2>Search History</h2>
|
||||||
|
<ul id="search-history-list"></ul>
|
||||||
|
<button id="clear-search-history-btn" style="margin-top: 10px;">Clear Search History</button>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2>Site History</h2>
|
||||||
|
<ul id="site-history-list"></ul>
|
||||||
|
<button id="clear-site-history-btn" style="margin-top: 10px;">Clear Site History</button>
|
||||||
|
<button id="add-test-history-btn" style="margin-top: 10px; margin-left: 10px;">Add Test History</button>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- status overlay moved outside of .container -->
|
<!-- status overlay moved outside of .container -->
|
||||||
@@ -31,44 +47,185 @@
|
|||||||
<span id="status-text"></span>
|
<span id="status-text"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script src="settings.js"></script>
|
<script src="settings.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const { ipcRenderer } = require('electron');
|
// Update debug info
|
||||||
|
function updateDebugInfo() {
|
||||||
|
const debugDiv = document.getElementById('debug-info');
|
||||||
|
const localStorage = window.localStorage;
|
||||||
|
const hasElectronAPI = !!window.electronAPI;
|
||||||
|
const siteHistory = localStorage ? localStorage.getItem('siteHistory') : 'N/A';
|
||||||
|
|
||||||
|
debugDiv.innerHTML = `
|
||||||
|
localStorage available: ${!!localStorage}<br>
|
||||||
|
electronAPI available: ${hasElectronAPI}<br>
|
||||||
|
siteHistory in localStorage: ${siteHistory || 'null'}<br>
|
||||||
|
Current domain: ${window.location.hostname}<br>
|
||||||
|
Current protocol: ${window.location.protocol}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadHistories() {
|
// Get site history from localStorage in this webview context
|
||||||
|
function getSiteHistoryFromLocalStorage() {
|
||||||
try {
|
try {
|
||||||
const searchHistory = await ipcRenderer.invoke('load-search-history');
|
const history = localStorage.getItem('siteHistory');
|
||||||
const siteHistory = await ipcRenderer.invoke('load-site-history');
|
console.log('[SETTINGS DEBUG] localStorage siteHistory:', history);
|
||||||
|
return history ? JSON.parse(history) : [];
|
||||||
const searchList = document.getElementById('search-history-list');
|
} catch (err) {
|
||||||
const siteList = document.getElementById('site-history-list');
|
console.error('[SETTINGS DEBUG] Error reading from localStorage:', err);
|
||||||
|
return [];
|
||||||
// Clear existing content
|
|
||||||
searchList.innerHTML = '';
|
|
||||||
siteList.innerHTML = '';
|
|
||||||
|
|
||||||
// Populate search history
|
|
||||||
searchHistory.forEach(item => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.textContent = item;
|
|
||||||
searchList.appendChild(li);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Populate site history
|
|
||||||
siteHistory.forEach(item => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.textContent = item;
|
|
||||||
siteList.appendChild(li);
|
|
||||||
});
|
|
||||||
} catch(err) {
|
|
||||||
console.error('Error loading histories:', err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load histories on page load.
|
// Sync site history from main browser's localStorage to this webview's localStorage
|
||||||
window.addEventListener('DOMContentLoaded', loadHistories);
|
function syncSiteHistoryFromMain() {
|
||||||
|
try {
|
||||||
|
// Try to get the parent window's localStorage
|
||||||
|
if (window.parent && window.parent !== window) {
|
||||||
|
const parentHistory = window.parent.localStorage.getItem('siteHistory');
|
||||||
|
if (parentHistory) {
|
||||||
|
localStorage.setItem('siteHistory', parentHistory);
|
||||||
|
console.log('[SETTINGS DEBUG] Synced history from parent window');
|
||||||
|
return JSON.parse(parentHistory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
} catch (err) {
|
||||||
|
console.log('[SETTINGS DEBUG] Could not sync from parent:', err.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTestHistory() {
|
||||||
|
const testSites = [
|
||||||
|
'https://www.google.com',
|
||||||
|
'https://github.com',
|
||||||
|
'https://stackoverflow.com',
|
||||||
|
'https://developer.mozilla.org',
|
||||||
|
'https://www.wikipedia.org'
|
||||||
|
];
|
||||||
|
|
||||||
|
testSites.forEach(site => {
|
||||||
|
try {
|
||||||
|
let history = getSiteHistoryFromLocalStorage();
|
||||||
|
history = history.filter(item => item !== site);
|
||||||
|
history.unshift(site);
|
||||||
|
localStorage.setItem('siteHistory', JSON.stringify(history));
|
||||||
|
console.log('[SETTINGS DEBUG] Added test site:', site);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error adding test history:', err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadHistories();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadHistories() {
|
||||||
|
try {
|
||||||
|
console.log('[SETTINGS DEBUG] Loading histories...');
|
||||||
|
updateDebugInfo();
|
||||||
|
|
||||||
|
// First try to sync from parent window
|
||||||
|
let siteHistory = syncSiteHistoryFromMain();
|
||||||
|
|
||||||
|
// If that didn't work, get from local storage
|
||||||
|
if (siteHistory.length === 0) {
|
||||||
|
siteHistory = getSiteHistoryFromLocalStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get search history via message passing to parent or direct file access
|
||||||
|
let searchHistory = [];
|
||||||
|
|
||||||
|
const searchList = document.getElementById('search-history-list');
|
||||||
|
const siteList = document.getElementById('site-history-list');
|
||||||
|
|
||||||
|
// Clear existing content
|
||||||
|
searchList.innerHTML = '';
|
||||||
|
siteList.innerHTML = '';
|
||||||
|
|
||||||
|
// Populate search history
|
||||||
|
const searchLi = document.createElement('li');
|
||||||
|
searchLi.textContent = 'Search history not available in webview context';
|
||||||
|
searchLi.style.fontStyle = 'italic';
|
||||||
|
searchLi.style.color = '#666';
|
||||||
|
searchList.appendChild(searchLi);
|
||||||
|
|
||||||
|
// Populate site history
|
||||||
|
if (siteHistory && siteHistory.length > 0) {
|
||||||
|
console.log('[SETTINGS DEBUG] Displaying', siteHistory.length, 'site history items');
|
||||||
|
siteHistory.forEach(item => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = item;
|
||||||
|
li.style.wordBreak = 'break-all';
|
||||||
|
siteList.appendChild(li);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('[SETTINGS DEBUG] No site history to display');
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = 'No browsing history found. Browse some websites first!';
|
||||||
|
li.style.fontStyle = 'italic';
|
||||||
|
li.style.color = '#666';
|
||||||
|
siteList.appendChild(li);
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.error('[SETTINGS DEBUG] Error loading histories:', err);
|
||||||
|
|
||||||
|
const searchList = document.getElementById('search-history-list');
|
||||||
|
const siteList = document.getElementById('site-history-list');
|
||||||
|
|
||||||
|
const errorLi1 = document.createElement('li');
|
||||||
|
errorLi1.textContent = 'Error loading search history: ' + err.message;
|
||||||
|
errorLi1.style.color = 'red';
|
||||||
|
searchList.appendChild(errorLi1);
|
||||||
|
|
||||||
|
const errorLi2 = document.createElement('li');
|
||||||
|
errorLi2.textContent = 'Error loading site history: ' + err.message;
|
||||||
|
errorLi2.style.color = 'red';
|
||||||
|
siteList.appendChild(errorLi2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearSiteHistory() {
|
||||||
|
try {
|
||||||
|
// Clear from localStorage
|
||||||
|
localStorage.removeItem('siteHistory');
|
||||||
|
console.log('[SETTINGS DEBUG] Cleared site history from localStorage');
|
||||||
|
|
||||||
|
loadHistories(); // Refresh the display
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error clearing site history:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearSearchHistory() {
|
||||||
|
console.log('Search history clearing not available in webview context');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load histories on page load
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
console.log('[SETTINGS DEBUG] DOM loaded, window.electronAPI:', !!window.electronAPI);
|
||||||
|
loadHistories();
|
||||||
|
|
||||||
|
// Refresh history every few seconds to pick up new browsing
|
||||||
|
setInterval(loadHistories, 3000);
|
||||||
|
|
||||||
|
// Add test history button functionality
|
||||||
|
const addTestBtn = document.getElementById('add-test-history-btn');
|
||||||
|
if (addTestBtn) {
|
||||||
|
addTestBtn.addEventListener('click', addTestHistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add clear buttons functionality
|
||||||
|
const clearSiteBtn = document.getElementById('clear-site-history-btn');
|
||||||
|
const clearSearchBtn = document.getElementById('clear-search-history-btn');
|
||||||
|
|
||||||
|
if (clearSiteBtn) {
|
||||||
|
clearSiteBtn.addEventListener('click', clearSiteHistory);
|
||||||
|
}
|
||||||
|
if (clearSearchBtn) {
|
||||||
|
clearSearchBtn.addEventListener('click', clearSearchHistory);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Use require('electron') since webviews have nodeIntegrationInSubFrames: true
|
||||||
const { ipcRenderer } = require('electron');
|
const { ipcRenderer } = require('electron');
|
||||||
|
|
||||||
const clearBtn = document.getElementById('clear-data-btn');
|
const clearBtn = document.getElementById('clear-data-btn');
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
"Awatapu College"
|
||||||
|
]
|
||||||
+10
-1
@@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
|
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
|
||||||
"file:///X:/Projects/Code/SteamOS_Browser/renderer/index.html"
|
"https://www.google.com/search?q=Awatapu%20College",
|
||||||
|
"https://andrewzambazos.com/",
|
||||||
|
"https://www.youtube.com/",
|
||||||
|
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
|
||||||
|
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
|
||||||
|
"https://youtube.com/",
|
||||||
|
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html",
|
||||||
|
"https://www.google.com/",
|
||||||
|
"https://youtube.com/",
|
||||||
|
"file:///X:/Projects/Code/NebulaBrowser/renderer/index.html"
|
||||||
]
|
]
|
||||||
Reference in New Issue
Block a user