From c514e4faec5d3c3113cac7495b6ba465a29bfdca Mon Sep 17 00:00:00 2001 From: Andrew Zambazos <62979495+Bobbybear007@users.noreply.github.com> Date: Mon, 18 May 2026 18:28:20 +1200 Subject: [PATCH] Menu popup: visibility, zoom sync, and tab fixes Track menu popup visibility and propagate zoom level to the popup. Add menu_popup_visible_ flag, SendMenuPopupZoom(), and call it when creating/showing the popup and when adjusting zoom. Make CreateNewTab accept an optional URL and route popup/new-tab flows to it. Prevent per-tab child closes from triggering app shutdown by tracking closing_tab_browsers_ and adding ForgetClosingTabBrowser(). Treat MenuPopup role specially when enabling frame hit-testing. Platform: remove usage of ApplyRoundedBrowserRegion and switch menu popup sizing to use resized client_size helpers (consolidate calculations across Win/Mac/Linux). UI: update menu-popup CSS/JS (new styling, font, zoom formatting API via NebulaMenuPopup.setZoomLevel), wire settings to use nebulaNative.postMessage for new-tab, and remove the static menu-popup.html. Misc: small chrome CSS/JS tweaks and ensure chrome state includes zoomLevel. --- src/app/nebula_controller.cpp | 94 +++++++++++++++++++---- src/app/nebula_controller.h | 6 +- src/cef/browser_client.cpp | 5 +- src/platform/browser_host.h | 1 - src/platform/linux/browser_host_linux.cpp | 13 +--- src/platform/mac/browser_host_mac.cpp | 13 +--- src/platform/win/browser_host_win.cpp | 28 +------ ui/css/chrome.css | 5 +- ui/css/menu-popup.css | 52 ++++++++----- ui/js/chrome.js | 1 + ui/js/menu-popup.js | 30 +++----- ui/js/settings.js | 8 +- ui/pages/menu-popup.html | 24 ------ ui/pages/settings.html | 6 +- 14 files changed, 156 insertions(+), 130 deletions(-) delete mode 100644 ui/pages/menu-popup.html diff --git a/src/app/nebula_controller.cpp b/src/app/nebula_controller.cpp index 021e52f..81f0f57 100644 --- a/src/app/nebula_controller.cpp +++ b/src/app/nebula_controller.cpp @@ -155,6 +155,12 @@ void NebulaController::OnWindowResized(const nebula::window::BrowserLayout& layo } void NebulaController::OnWindowCloseRequested() { + if (!closing_ && !closing_tab_browsers_.empty()) { + // CEF Alloy can bubble a child browser close as WM_CLOSE on the host + // window. Per-tab closes should not turn into full app shutdown. + return; + } + if (closing_) { // CEF re-sends WM_CLOSE to the top-level window after each Alloy // child browser finishes its JS unload + DoClose phase. Destroy the @@ -193,7 +199,7 @@ void NebulaController::OnActiveTabChanged(const nebula::browser::NebulaTab& tab) } void NebulaController::OnBrowserCreated(nebula::cef::BrowserRole role, CefRefPtr browser) { - if (window_ && browser) { + if (window_ && browser && role != nebula::cef::BrowserRole::MenuPopup) { window_->EnableFrameHitTest(browser->GetHost()->GetWindowHandle()); } @@ -205,7 +211,9 @@ void NebulaController::OnBrowserCreated(nebula::cef::BrowserRole role, CefRefPtr } } else if (role == nebula::cef::BrowserRole::MenuPopup) { menu_popup_browser_ = browser; + menu_popup_visible_ = true; PositionMenuPopup(); + SendMenuPopupZoom(); } else { tabs_.SetActiveBrowser(browser); } @@ -220,7 +228,9 @@ void NebulaController::OnBrowserClosing(nebula::cef::BrowserRole role, CefRefPtr } else if (role == nebula::cef::BrowserRole::MenuPopup) { menu_popup_browser_ = nullptr; menu_popup_client_ = nullptr; + menu_popup_visible_ = false; } else { + ForgetClosingTabBrowser(browser); if (content_fullscreen_) { const auto* active_tab = tabs_.ActiveTab(); if (active_tab && active_tab->browser && active_tab->browser->IsSame(browser)) { @@ -242,7 +252,7 @@ void NebulaController::OnChromeCommand(const std::string& command, const std::st tabs_.LoadURL(target); } } else if (command == "new-tab") { - CreateNewTab(); + CreateNewTab(payload); } else if (command == "activate-tab") { ActivateTab(ParseTabId(payload)); } else if (command == "close-tab") { @@ -354,9 +364,9 @@ void NebulaController::OnPopupRequested(CefRefPtr browser, const std return; } - tabs_.LoadURL(nebula::ui::IsEmptyOrChromiumNewTabUrl(target_url) - ? nebula::ui::GetHomeUrl() - : target_url); + CreateNewTab(nebula::ui::IsEmptyOrChromiumNewTabUrl(target_url) + ? nebula::ui::GetHomeUrl() + : target_url); } bool NebulaController::ShouldBypassInsecureWarning(CefRefPtr browser, const std::string& target_url) { @@ -373,12 +383,14 @@ bool NebulaController::ShouldBypassInsecureWarning(CefRefPtr browser return true; } -void NebulaController::CreateNewTab() { +void NebulaController::CreateNewTab(std::string url) { if (auto* tab = tabs_.ActiveTab()) { SetBrowserVisible(tab->browser, false); } - tabs_.CreateTab(nebula::ui::GetHomeUrl()); + const std::string target = + url.empty() ? nebula::ui::GetHomeUrl() : nebula::browser::NormalizeNavigationInput(url); + tabs_.CreateTab(target.empty() ? nebula::ui::GetHomeUrl() : target); PersistSession(); CreateContentBrowser(); } @@ -415,6 +427,7 @@ void NebulaController::CloseTab(int tab_id) { CefRefPtr closing_browser = tabs_.CloseTab(tab_id); PersistSession(); if (closing_browser) { + closing_tab_browsers_.push_back(closing_browser); closing_browser->GetHost()->CloseBrowser(false); } @@ -464,22 +477,31 @@ void NebulaController::CreateContentBrowser() { } void NebulaController::ToggleMenuPopup() { - if (menu_popup_browser_) { + if (menu_popup_browser_ && menu_popup_visible_) { CloseMenuPopup(); return; } + if (menu_popup_browser_) { + menu_popup_visible_ = true; + PositionMenuPopup(); + SetBrowserVisible(menu_popup_browser_, true); + SendMenuPopupZoom(); + return; + } + CreateMenuPopupBrowser(); } void NebulaController::CloseMenuPopup() { if (menu_popup_browser_) { - menu_popup_browser_->GetHost()->CloseBrowser(false); + menu_popup_visible_ = false; + SetBrowserVisible(menu_popup_browser_, false); } } void NebulaController::CreateMenuPopupBrowser() { - if (!window_ || !window_->native_handle()) { + if (!window_ || !window_->native_handle() || content_fullscreen_) { return; } @@ -494,20 +516,34 @@ void NebulaController::CreateMenuPopupBrowser() { } void NebulaController::PositionMenuPopup() { - if (content_fullscreen_ || !window_ || !window_->native_handle() || !menu_popup_browser_) { + if (content_fullscreen_ || !window_ || !window_->native_handle() || !menu_popup_browser_ || + !menu_popup_visible_) { return; } const auto rect = nebula::platform::MenuPopupRect(window_->native_handle(), window_->CurrentLayout()); const auto browser_window = menu_popup_browser_->GetHost()->GetWindowHandle(); - window_->ResizeChild(browser_window, rect); - nebula::platform::ApplyRoundedBrowserRegion( - browser_window, - nebula::platform::ScaleForParentWindow(window_->native_handle(), 28)); + nebula::platform::ResizeBrowserWindow(browser_window, rect); nebula::platform::RaiseBrowserWindow(browser_window); } +void NebulaController::SendMenuPopupZoom() { + if (!menu_popup_browser_) { + return; + } + + double zoom_level = 0.0; + if (const auto* tab = tabs_.ActiveTab(); tab && tab->browser) { + zoom_level = tab->browser->GetHost()->GetZoomLevel(); + } + + const std::string script = + "window.NebulaMenuPopup && window.NebulaMenuPopup.setZoomLevel(" + + std::to_string(zoom_level) + ");"; + menu_popup_browser_->GetMainFrame()->ExecuteJavaScript(script, nebula::ui::GetMenuPopupUrl(), 0); +} + void NebulaController::ToggleDevTools() { auto* tab = tabs_.ActiveTab(); if (!tab || !tab->browser || !window_ || !window_->native_handle()) { @@ -534,6 +570,10 @@ void NebulaController::AdjustZoom(double delta) { CefRefPtr host = tab->browser->GetHost(); host->SetZoomLevel(host->GetZoomLevel() + delta); + SendMenuPopupZoom(); + if (chrome_ready_) { + SendChromeState(*tab); + } } void NebulaController::FreshReload() { @@ -589,6 +629,10 @@ void NebulaController::SendChromeState(const nebula::browser::NebulaTab& tab) { } const std::string display_url = GetChromeDisplayUrl(tab.url); + double zoom_level = 0.0; + if (tab.browser) { + zoom_level = tab.browser->GetHost()->GetZoomLevel(); + } std::string tabs_json = "["; const auto& tabs = tabs_.Tabs(); for (size_t i = 0; i < tabs.size(); ++i) { @@ -615,6 +659,7 @@ void NebulaController::SendChromeState(const nebula::browser::NebulaTab& tab) { ",\"canGoBack\":" + std::string(tab.CanGoBack() ? "true" : "false") + ",\"canGoForward\":" + std::string(tab.CanGoForward() ? "true" : "false") + ",\"favicon\":\"" + nebula::browser::JsonEscape(tab.favicon_url) + "\"" + + ",\"zoomLevel\":" + std::to_string(zoom_level) + ",\"tabs\":" + tabs_json + "});"; @@ -667,4 +712,23 @@ void NebulaController::MaybeFinishShutdown() { CefQuitMessageLoop(); } +bool NebulaController::ForgetClosingTabBrowser(CefRefPtr browser) { + if (!browser) { + return false; + } + + const auto it = std::find_if( + closing_tab_browsers_.begin(), + closing_tab_browsers_.end(), + [browser](const CefRefPtr& closing_browser) { + return closing_browser && closing_browser->IsSame(browser); + }); + if (it == closing_tab_browsers_.end()) { + return false; + } + + closing_tab_browsers_.erase(it); + return true; +} + } // namespace nebula::app diff --git a/src/app/nebula_controller.h b/src/app/nebula_controller.h index 66ba36f..c9f141b 100644 --- a/src/app/nebula_controller.h +++ b/src/app/nebula_controller.h @@ -41,7 +41,7 @@ public: bool ShouldBypassInsecureWarning(CefRefPtr browser, const std::string& target_url) override; private: - void CreateNewTab(); + void CreateNewTab(std::string url = {}); void ActivateTab(int tab_id); void CloseTab(int tab_id); void CreateChromeBrowser(); @@ -50,6 +50,7 @@ private: void CloseMenuPopup(); void CreateMenuPopupBrowser(); void PositionMenuPopup(); + void SendMenuPopupZoom(); void ToggleDevTools(); void AdjustZoom(double delta); void FreshReload(); @@ -60,12 +61,14 @@ private: void InjectSettingsHistory(CefRefPtr browser); void PersistSession() const; void MaybeFinishShutdown(); + bool ForgetClosingTabBrowser(CefRefPtr browser); nebula::platform::AppStartup startup_; std::string initial_url_; bool closing_ = false; bool chrome_ready_ = false; bool content_fullscreen_ = false; + bool menu_popup_visible_ = false; std::unique_ptr window_; nebula::browser::TabManager tabs_; @@ -74,6 +77,7 @@ private: CefRefPtr chrome_client_; CefRefPtr content_client_; CefRefPtr menu_popup_client_; + std::vector> closing_tab_browsers_; std::unordered_set insecure_warning_bypasses_; std::vector site_history_; }; diff --git a/src/cef/browser_client.cpp b/src/cef/browser_client.cpp index fdf1c74..f1228e2 100644 --- a/src/cef/browser_client.cpp +++ b/src/cef/browser_client.cpp @@ -51,8 +51,8 @@ bool NebulaBrowserClient::OnProcessMessageReceived(CefRefPtr browser return false; } - if (role_ != BrowserRole::Chrome && role_ != BrowserRole::MenuPopup && - role_ != BrowserRole::Content) { + if (role_ != BrowserRole::Chrome && role_ != BrowserRole::Content && + role_ != BrowserRole::MenuPopup) { return false; } @@ -64,6 +64,7 @@ bool NebulaBrowserClient::OnProcessMessageReceived(CefRefPtr browser command == "navigate-insecure" && IsInsecureInterstitialFrame(frame); const bool allowed_settings_command = IsSettingsFrame(frame) && (command == "navigate" || + command == "new-tab" || command == "clear-site-history" || command == "clear-search-history"); if (!allowed_insecure_command && !allowed_settings_command) { diff --git a/src/platform/browser_host.h b/src/platform/browser_host.h index 7900df7..8f34e99 100644 --- a/src/platform/browser_host.h +++ b/src/platform/browser_host.h @@ -14,7 +14,6 @@ void ResizeBrowserWindow(NativeWindow browser_window, const Rect& rect); void SetBrowserVisible(NativeWindow browser_window, bool visible); void RaiseBrowserWindow(NativeWindow browser_window); Rect MenuPopupRect(NativeWindow parent, const BrowserLayout& layout); -void ApplyRoundedBrowserRegion(NativeWindow browser_window, int corner_radius); std::string CacheBusterToken(); void DestroyTopLevelWindow(NativeWindow window); int ScaleForParentWindow(NativeWindow parent, int value); diff --git a/src/platform/linux/browser_host_linux.cpp b/src/platform/linux/browser_host_linux.cpp index 9be05ee..32bef69 100644 --- a/src/platform/linux/browser_host_linux.cpp +++ b/src/platform/linux/browser_host_linux.cpp @@ -45,26 +45,21 @@ std::pair ParentClientSize(NativeWindow parent) { } Rect MenuPopupRect(NativeWindow parent, const BrowserLayout& layout) { - const auto [client_right, client_bottom] = ParentClientSize(parent); + const auto client_size = ParentClientSize(parent); const int width = 260; const int height = 258; const int margin = 12; const int overlap = 2; - const int x = std::max(0, client_right - width - margin); + const int x = std::max(0, client_size.first - width - margin); const int y = std::max(0, layout.chrome.y + layout.chrome.height - overlap); return { x, y, - std::min(client_right, x + width) - x, - std::min(client_bottom, y + height) - y, + std::min(client_size.first, x + width) - x, + std::min(client_size.second, y + height) - y, }; } -void ApplyRoundedBrowserRegion(NativeWindow browser_window, int corner_radius) { - UNREFERENCED_PARAMETER(browser_window); - UNREFERENCED_PARAMETER(corner_radius); -} - std::string CacheBusterToken() { return "0"; } diff --git a/src/platform/mac/browser_host_mac.cpp b/src/platform/mac/browser_host_mac.cpp index 2d0eb99..da845a6 100644 --- a/src/platform/mac/browser_host_mac.cpp +++ b/src/platform/mac/browser_host_mac.cpp @@ -43,26 +43,21 @@ std::pair ParentClientSize(NativeWindow parent) { } Rect MenuPopupRect(NativeWindow parent, const BrowserLayout& layout) { - const auto [client_right, client_bottom] = ParentClientSize(parent); + const auto client_size = ParentClientSize(parent); const int width = 260; const int height = 258; const int margin = 12; const int overlap = 2; - const int x = std::max(0, client_right - width - margin); + const int x = std::max(0, client_size.first - width - margin); const int y = std::max(0, layout.chrome.y + layout.chrome.height - overlap); return { x, y, - std::min(client_right, x + width) - x, - std::min(client_bottom, y + height) - y, + std::min(client_size.first, x + width) - x, + std::min(client_size.second, y + height) - y, }; } -void ApplyRoundedBrowserRegion(NativeWindow browser_window, int corner_radius) { - UNREFERENCED_PARAMETER(browser_window); - UNREFERENCED_PARAMETER(corner_radius); -} - std::string CacheBusterToken() { return "0"; } diff --git a/src/platform/win/browser_host_win.cpp b/src/platform/win/browser_host_win.cpp index a12c5ed..eed998d 100644 --- a/src/platform/win/browser_host_win.cpp +++ b/src/platform/win/browser_host_win.cpp @@ -95,42 +95,22 @@ std::pair ParentClientSize(NativeWindow parent) { } Rect MenuPopupRect(NativeWindow parent, const BrowserLayout& layout) { - const auto [client_right, client_bottom] = ParentClientSize(parent); - + const auto client_size = ParentClientSize(parent); const int width = ScaleForParentWindow(parent, 260); const int height = ScaleForParentWindow(parent, 258); const int margin = ScaleForParentWindow(parent, 12); const int overlap = ScaleForParentWindow(parent, 2); - const int x = std::max(0, client_right - width - margin); + const int x = std::max(0, client_size.first - width - margin); const int y = std::max(0, layout.chrome.y + layout.chrome.height - overlap); return { x, y, - std::min(client_right, x + width) - x, - std::min(client_bottom, y + height) - y, + std::min(client_size.first, x + width) - x, + std::min(client_size.second, y + height) - y, }; } -void ApplyRoundedBrowserRegion(NativeWindow browser_window, int corner_radius) { - const HWND hwnd = AsHwnd(browser_window); - if (!hwnd) { - return; - } - - RECT rect = {}; - if (!GetClientRect(hwnd, &rect)) { - return; - } - - const int width = std::max(1, rect.right - rect.left); - const int height = std::max(1, rect.bottom - rect.top); - HRGN region = CreateRoundRectRgn(0, 0, width + 1, height + 1, corner_radius, corner_radius); - if (region && !SetWindowRgn(hwnd, region, TRUE)) { - DeleteObject(region); - } -} - std::string CacheBusterToken() { return std::to_string(GetTickCount64()); } diff --git a/ui/css/chrome.css b/ui/css/chrome.css index 6fbe84e..c8cb692 100644 --- a/ui/css/chrome.css +++ b/ui/css/chrome.css @@ -59,9 +59,10 @@ button:disabled { .nebula-chrome { display: grid; - grid-template-rows: 42px 52px; + grid-template-rows: 42px 52px 1fr; height: 100%; border-bottom: 1px solid var(--outline); + overflow: visible; } /* ── Title row ──────────────────────────────────────────────── */ @@ -253,6 +254,7 @@ button:disabled { padding: 0 12px; background: var(--surface-raised); border-top: 1px solid var(--outline); + overflow: visible; } /* ── Lucide icon sizing ─────────────────────────────────────── */ @@ -361,3 +363,4 @@ button:disabled { transform: rotate(360deg); } } + diff --git a/ui/css/menu-popup.css b/ui/css/menu-popup.css index ffc9cef..74b7b4f 100644 --- a/ui/css/menu-popup.css +++ b/ui/css/menu-popup.css @@ -1,34 +1,40 @@ :root { - --bg: #0b0d10; - --primary: #7b2eff; - --accent: #00c6ff; - --text: #e0e0e0; - --url-bar-bg: #1c2030; - --url-bar-border: #3e4652; - --shadow-1: 0 12px 30px rgba(0, 0, 0, 0.35); - --blur: 12px; + --surface-raised: #141824; + --surface-hover: rgba(255, 255, 255, 0.06); + --text: #e8e8f0; + --muted: #7a7e90; + --outline: #1f2533; + color-scheme: dark; +} + +@font-face { + font-family: "InterVariable"; + src: url("../assets/fonts/InterVariable.ttf") format("truetype"); + font-weight: 100 900; + font-display: swap; } * { box-sizing: border-box; } body { margin: 0; - background: transparent; + background: var(--surface-raised); color: var(--text); - font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + font-family: "InterVariable", "Segoe UI", system-ui, sans-serif; + overflow: hidden; + user-select: none; } #menu-popup { - background: color-mix(in srgb, var(--url-bar-bg) 92%, var(--text) 8%); - border: 1px solid color-mix(in srgb, var(--primary) 25%, color-mix(in srgb, var(--accent) 18%, transparent)); - border-radius: 14px; - padding: 8px; + width: 100%; + height: 100%; + background: var(--surface-raised); + border: 1px solid var(--outline); + border-radius: 12px; + padding: 6px; display: flex; flex-direction: column; - min-width: 220px; - box-shadow: var(--shadow-1); - -webkit-backdrop-filter: blur(var(--blur)); - backdrop-filter: blur(var(--blur)); + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); } #menu-popup button { @@ -37,13 +43,15 @@ body { color: var(--text); text-align: left; padding: 8px 10px; - border-radius: 10px; + border-radius: 8px; cursor: pointer; - transition: background 120ms ease, filter 120ms ease; + font: inherit; + font-size: 0.84rem; + transition: background 120ms ease; } #menu-popup button:hover { - background: color-mix(in srgb, var(--text) 8%, transparent); + background: var(--surface-hover); } .zoom-controls { @@ -63,4 +71,6 @@ body { #zoom-percent { min-width: 54px; text-align: center; + color: var(--muted); + font-size: 0.82rem; } diff --git a/ui/js/chrome.js b/ui/js/chrome.js index 56d19da..44eda0a 100644 --- a/ui/js/chrome.js +++ b/ui/js/chrome.js @@ -123,6 +123,7 @@ function applyState(nextState) { progressBar.style.width = `${Math.max(0, Math.min(1, state.progress || 0)) * 100}%`; progressBar.style.opacity = state.isLoading ? '1' : '0'; + } function wireCommands() { diff --git a/ui/js/menu-popup.js b/ui/js/menu-popup.js index c97a05f..db2db9d 100644 --- a/ui/js/menu-popup.js +++ b/ui/js/menu-popup.js @@ -18,37 +18,29 @@ function applyTheme(theme) { } function sendMenuCommand(cmd) { - if (window.electronAPI?.send) { - window.electronAPI.send('menu-popup-command', { cmd }); - return; - } - if (window.nebulaNative?.postMessage) { window.nebulaNative.postMessage(cmd); } } -async function refreshZoom() { - if (!window.electronAPI?.invoke || !zoomPercentEl) return; - try { - const z = await window.electronAPI.invoke('get-zoom-factor'); - zoomPercentEl.textContent = `${Math.round(z * 100)}%`; - } catch {} +function formatZoomPercent(zoomLevel) { + const level = Number.isFinite(zoomLevel) ? zoomLevel : 0; + return `${Math.round(Math.pow(1.2, level) * 100)}%`; } -window.electronAPI?.on?.('menu-popup-init', (payload) => { - applyTheme(payload?.theme); - refreshZoom(); -}); +function setZoomLevel(zoomLevel) { + if (zoomPercentEl) { + zoomPercentEl.textContent = formatZoomPercent(zoomLevel); + } +} + +window.NebulaMenuPopup = { applyTheme, setZoomLevel }; window.addEventListener('click', (e) => { const btn = e.target.closest('button[data-cmd]'); if (!btn) return; const cmd = btn.getAttribute('data-cmd'); sendMenuCommand(cmd); - if (cmd === 'zoom-in' || cmd === 'zoom-out') { - setTimeout(refreshZoom, 50); - } }); window.addEventListener('keydown', (e) => { @@ -57,4 +49,4 @@ window.addEventListener('keydown', (e) => { } }); -refreshZoom(); +setZoomLevel(0); diff --git a/ui/js/settings.js b/ui/js/settings.js index dd388e9..d6a2837 100644 --- a/ui/js/settings.js +++ b/ui/js/settings.js @@ -773,7 +773,9 @@ window.addEventListener('DOMContentLoaded', () => { try { e.preventDefault(); const url = gh.getAttribute('href'); - if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { + if (window.nebulaNative && typeof window.nebulaNative.postMessage === 'function') { + window.nebulaNative.postMessage('new-tab', url); + } else if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { window.electronAPI.sendToHost('navigate', url, { newTab: true }); } else if (window.parent && window.parent !== window) { window.parent.postMessage({ type: 'navigate', url, newTab: true }, '*'); @@ -792,7 +794,9 @@ window.addEventListener('DOMContentLoaded', () => { try { e.preventDefault(); const url = help.getAttribute('href'); - if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { + if (window.nebulaNative && typeof window.nebulaNative.postMessage === 'function') { + window.nebulaNative.postMessage('new-tab', url); + } else if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { window.electronAPI.sendToHost('navigate', url, { newTab: true }); } else if (window.parent && window.parent !== window) { window.parent.postMessage({ type: 'navigate', url, newTab: true }, '*'); diff --git a/ui/pages/menu-popup.html b/ui/pages/menu-popup.html deleted file mode 100644 index a55ee53..0000000 --- a/ui/pages/menu-popup.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Menu - - - - - - - diff --git a/ui/pages/settings.html b/ui/pages/settings.html index af92fef..5dc4795 100644 --- a/ui/pages/settings.html +++ b/ui/pages/settings.html @@ -489,8 +489,10 @@ a.addEventListener('click', (e) => { e.preventDefault(); try { - if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { - // Ask the host to open this URL in a new tab to keep Settings open + if (window.nebulaNative && typeof window.nebulaNative.postMessage === 'function') { + // Ask the CEF host to open this URL in a new tab to keep Settings open. + window.nebulaNative.postMessage('new-tab', item); + } else if (window.electronAPI && typeof window.electronAPI.sendToHost === 'function') { window.electronAPI.sendToHost('navigate', item, { newTab: true }); } else if (window.parent && window.parent !== window) { // Fallback: postMessage to parent if available