Improve tab history navigation and internal page handling
Refactors tab history management to prevent duplicate entries and ensure correct navigation for internal nebula:// pages. Updates back/forward navigation to use custom history tracking, properly converting between home and webview tabs, and disables navigation buttons based on history state instead of webview methods.
This commit is contained in:
+127
-18
@@ -254,6 +254,7 @@ urlBox.addEventListener('keydown', (e) => {
|
|||||||
|
|
||||||
let tabs = [];
|
let tabs = [];
|
||||||
let activeTabId = null;
|
let activeTabId = null;
|
||||||
|
let isHistoryNavigation = false; // Flag to prevent duplicate history entries during back/forward
|
||||||
const allowedInternalPages = ['settings', 'home', 'downloads', 'nebot', 'insecure'];
|
const allowedInternalPages = ['settings', 'home', 'downloads', 'nebot', 'insecure'];
|
||||||
// Session-scoped allowlist of HTTP hosts the user explicitly chose to proceed with.
|
// Session-scoped allowlist of HTTP hosts the user explicitly chose to proceed with.
|
||||||
const insecureBypassedHosts = new Set();
|
const insecureBypassedHosts = new Set();
|
||||||
@@ -843,8 +844,8 @@ function convertHomeTabToWebview(tabId, inputUrl, resolvedUrl) {
|
|||||||
tab.isHome = false;
|
tab.isHome = false;
|
||||||
tab.webview = webview;
|
tab.webview = webview;
|
||||||
tab.url = inputUrl;
|
tab.url = inputUrl;
|
||||||
tab.history = [inputUrl];
|
// Keep existing history (including home) - the new URL will be added by handleNavigation when webview loads
|
||||||
tab.historyIndex = 0;
|
// Don't modify historyIndex here - handleNavigation will handle it
|
||||||
|
|
||||||
// Hide home container and show webview
|
// Hide home container and show webview
|
||||||
const homeContainer = document.getElementById('home-container');
|
const homeContainer = document.getElementById('home-container');
|
||||||
@@ -864,11 +865,20 @@ function handleNavigation(tabId, newUrl) {
|
|||||||
debug('[DEBUG] handleNavigation called with:', newUrl);
|
debug('[DEBUG] handleNavigation called with:', newUrl);
|
||||||
|
|
||||||
// --- record every real navigation into history ---
|
// --- record every real navigation into history ---
|
||||||
if (tab.history[tab.historyIndex] !== newUrl) {
|
// Skip adding to history if this is a programmatic back/forward navigation
|
||||||
|
if (!isHistoryNavigation) {
|
||||||
|
// Check both current position AND last recorded URL to prevent duplicates from
|
||||||
|
// multiple event firings (did-navigate + did-navigate-in-page)
|
||||||
|
const lastRecordedUrl = tab.history[tab.history.length - 1];
|
||||||
|
if (tab.history[tab.historyIndex] !== newUrl && lastRecordedUrl !== newUrl) {
|
||||||
tab.history = tab.history.slice(0, tab.historyIndex + 1);
|
tab.history = tab.history.slice(0, tab.historyIndex + 1);
|
||||||
tab.history.push(newUrl);
|
tab.history.push(newUrl);
|
||||||
tab.historyIndex++;
|
tab.historyIndex++;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Reset flag after handling the navigation
|
||||||
|
isHistoryNavigation = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Record site history in localStorage (skip internal pages and file:// URLs)
|
// Record site history in localStorage (skip internal pages and file:// URLs)
|
||||||
if (!newUrl.endsWith('home.html') &&
|
if (!newUrl.endsWith('home.html') &&
|
||||||
@@ -905,6 +915,25 @@ function handleNavigation(tabId, newUrl) {
|
|||||||
|
|
||||||
tab.url = displayUrl;
|
tab.url = displayUrl;
|
||||||
|
|
||||||
|
// Clear favicon and reset title for internal nebula:// pages
|
||||||
|
if (displayUrl.startsWith('nebula://')) {
|
||||||
|
tab.favicon = null;
|
||||||
|
// Set appropriate title for each internal page
|
||||||
|
if (isHome) {
|
||||||
|
tab.title = 'New Tab';
|
||||||
|
} else if (isSettings) {
|
||||||
|
tab.title = 'Settings';
|
||||||
|
} else if (isDownloads) {
|
||||||
|
tab.title = 'Downloads';
|
||||||
|
} else if (isNebot) {
|
||||||
|
tab.title = 'Nebot';
|
||||||
|
} else if (isInsecure) {
|
||||||
|
tab.title = 'Insecure Connection';
|
||||||
|
} else if (is404) {
|
||||||
|
tab.title = 'Page Not Found';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tabId === activeTabId) {
|
if (tabId === activeTabId) {
|
||||||
urlBox.value = displayUrl === 'nebula://home' ? '' : displayUrl;
|
urlBox.value = displayUrl === 'nebula://home' ? '' : displayUrl;
|
||||||
}
|
}
|
||||||
@@ -1202,27 +1231,112 @@ ipcRenderer.on('open-url', (url) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function goBack() {
|
function goBack() {
|
||||||
|
const tab = tabs.find(t => t.id === activeTabId);
|
||||||
|
if (!tab) return;
|
||||||
|
|
||||||
|
// Use custom history tracking to properly handle internal pages like home
|
||||||
|
if (tab.historyIndex > 0) {
|
||||||
|
tab.historyIndex--;
|
||||||
|
const targetUrl = tab.history[tab.historyIndex];
|
||||||
|
|
||||||
|
// Special handling for nebula://home - convert webview tab back to home tab
|
||||||
|
if (targetUrl === 'nebula://home') {
|
||||||
|
const homeContainer = document.getElementById('home-container');
|
||||||
|
const webviewsEl = document.getElementById('webviews');
|
||||||
const webview = document.getElementById(`tab-${activeTabId}`);
|
const webview = document.getElementById(`tab-${activeTabId}`);
|
||||||
if (webview && webview.canGoBack()) {
|
|
||||||
webview.goBack();
|
// Remove the webview if it exists
|
||||||
|
if (webview) {
|
||||||
|
webview.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tab back to home tab
|
||||||
|
tab.isHome = true;
|
||||||
|
tab.url = targetUrl;
|
||||||
|
delete tab.webview;
|
||||||
|
|
||||||
|
// Show home container
|
||||||
|
if (homeContainer) homeContainer.classList.add('active');
|
||||||
|
if (webviewsEl) webviewsEl.classList.add('hidden');
|
||||||
|
|
||||||
|
urlBox.value = '';
|
||||||
|
scheduleRenderTabs();
|
||||||
|
scheduleUpdateNavButtons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const webview = document.getElementById(`tab-${activeTabId}`);
|
||||||
|
if (webview) {
|
||||||
|
isHistoryNavigation = true; // Prevent adding to history during programmatic navigation
|
||||||
|
// Resolve internal URLs (nebula://) to actual file paths
|
||||||
|
const resolvedUrl = resolveInternalUrl(targetUrl);
|
||||||
|
webview.loadURL(resolvedUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function goForward() {
|
function goForward() {
|
||||||
|
const tab = tabs.find(t => t.id === activeTabId);
|
||||||
|
if (!tab) return;
|
||||||
|
|
||||||
|
// Use custom history tracking to properly handle internal pages like home
|
||||||
|
if (tab.historyIndex < tab.history.length - 1) {
|
||||||
|
tab.historyIndex++;
|
||||||
|
const targetUrl = tab.history[tab.historyIndex];
|
||||||
|
|
||||||
|
// Special handling for nebula://home - it doesn't use a webview
|
||||||
|
if (targetUrl === 'nebula://home') {
|
||||||
|
const homeContainer = document.getElementById('home-container');
|
||||||
|
const webviewsEl = document.getElementById('webviews');
|
||||||
const webview = document.getElementById(`tab-${activeTabId}`);
|
const webview = document.getElementById(`tab-${activeTabId}`);
|
||||||
if (webview && webview.canGoForward()) {
|
|
||||||
webview.goForward();
|
// Remove the webview if it exists
|
||||||
|
if (webview) {
|
||||||
|
webview.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tab back to home tab
|
||||||
|
tab.isHome = true;
|
||||||
|
tab.url = targetUrl;
|
||||||
|
delete tab.webview;
|
||||||
|
|
||||||
|
// Show home container
|
||||||
|
if (homeContainer) homeContainer.classList.add('active');
|
||||||
|
if (webviewsEl) webviewsEl.classList.add('hidden');
|
||||||
|
|
||||||
|
urlBox.value = '';
|
||||||
|
scheduleRenderTabs();
|
||||||
|
scheduleUpdateNavButtons();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're currently on home and need to create a webview
|
||||||
|
if (tab.isHome && targetUrl !== 'nebula://home') {
|
||||||
|
// We're going forward from home to a webview page
|
||||||
|
const resolvedUrl = resolveInternalUrl(targetUrl);
|
||||||
|
convertHomeTabToWebview(activeTabId, targetUrl, resolvedUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const webview = document.getElementById(`tab-${activeTabId}`);
|
||||||
|
if (webview) {
|
||||||
|
isHistoryNavigation = true; // Prevent adding to history during programmatic navigation
|
||||||
|
// Resolve internal URLs (nebula://) to actual file paths
|
||||||
|
const resolvedUrl = resolveInternalUrl(targetUrl);
|
||||||
|
webview.loadURL(resolvedUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNavButtons() {
|
function updateNavButtons() {
|
||||||
const webview = document.getElementById(`tab-${activeTabId}`);
|
const tab = tabs.find(t => t.id === activeTabId);
|
||||||
if (!backBtnCached || !fwdBtnCached) {
|
if (!backBtnCached || !fwdBtnCached) {
|
||||||
backBtnCached = document.querySelector('.nav-left button:nth-child(1)');
|
backBtnCached = document.querySelector('.nav-left button:nth-child(1)');
|
||||||
fwdBtnCached = document.querySelector('.nav-left button:nth-child(2)');
|
fwdBtnCached = document.querySelector('.nav-left button:nth-child(2)');
|
||||||
}
|
}
|
||||||
if (backBtnCached) backBtnCached.disabled = !webview || !webview.canGoBack();
|
// Use custom history tracking for button state
|
||||||
if (fwdBtnCached) fwdBtnCached.disabled = !webview || !webview.canGoForward();
|
if (backBtnCached) backBtnCached.disabled = !tab || tab.historyIndex <= 0;
|
||||||
|
if (fwdBtnCached) fwdBtnCached.disabled = !tab || tab.historyIndex >= tab.history.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
@@ -1451,14 +1565,9 @@ window.addEventListener('DOMContentLoaded', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// wire up back/forward buttons
|
// Cache back/forward buttons for faster updates (no need to add listeners - already in HTML)
|
||||||
const backBtn = document.querySelector('.nav-left button:nth-child(1)');
|
backBtnCached = document.querySelector('.nav-left button:nth-child(1)');
|
||||||
const forwardBtn = document.querySelector('.nav-left button:nth-child(2)');
|
fwdBtnCached = document.querySelector('.nav-left button:nth-child(2)');
|
||||||
backBtn.addEventListener('click', goBack);
|
|
||||||
forwardBtn.addEventListener('click', goForward);
|
|
||||||
// cache for faster updates
|
|
||||||
backBtnCached = backBtn;
|
|
||||||
fwdBtnCached = forwardBtn;
|
|
||||||
|
|
||||||
// settings button
|
// settings button
|
||||||
const settingsBtn = document.getElementById('open-settings-btn');
|
const settingsBtn = document.getElementById('open-settings-btn');
|
||||||
|
|||||||
Reference in New Issue
Block a user