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:
2025-07-26 14:15:13 +12:00
parent ba9a2aa891
commit 0b61f86dd4
7 changed files with 496 additions and 46 deletions
+97 -7
View File
@@ -82,20 +82,55 @@ function createWindow(startUrl) {
// ensure all embedded <webview> tags also use the same window
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
webContents.setWindowOpenHandler(({ 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' };
});
// intercept legacy new-window on webview
webContents.on('new-window', (e, url) => {
e.preventDefault();
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)
webContents.on('will-navigate', (e, url) => {
e.preventDefault();
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
const recordHistory = async (fileName, entry) => {
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));
if (fileName === 'site-history.json') {
// Save to both file and send to renderer
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));
}
// 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
// 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 () => {
// Read from the site history file for settings page
const filePath = path.join(__dirname, 'site-history.json');
try {
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) => {
// Save to both file and localStorage
const filePath = path.join(__dirname, 'site-history.json');
try {
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 () => {
const filePath = path.join(__dirname, 'search-history.json');
try {
@@ -269,3 +333,29 @@ ipcMain.handle('zoom-out', event => {
ipcMain.handle('open-tab-in-new-window', (event, 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;
}
});