prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit e5f78550b431ffd6e06b6e8fdb2d4996aacd231d
parent 108cbea0ec6188c08e093d02bd9d48e17bbe990a
Author: Damian Myrda <damian@prime8.dev>
Date:   Thu, 22 Oct 2026 01:31:00 +0000

web: links that leave the app open in a new tab

Diffstat:
Minternal/web/scripts/modules/header.js | 30+++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/internal/web/scripts/modules/header.js b/internal/web/scripts/modules/header.js @@ -13,6 +13,21 @@ const APP_HOSTS = new Set(["prime8.dev", "www.prime8.dev"]); // static assets) is a real load. const APP_PATH = /^\/(?:(?:projects|experience)(?:\/[^/]+)?)?\/?$/; +// opensOut reports whether an anchor should open in a new tab. +function opensOut(a) { + if (a.hasAttribute("download") || a.hasAttribute("hx-get")) return false; + let url; + try { + url = new URL(a.href, window.location.href); + } catch { + return false; + } + if (!/^https?:$/.test(url.protocol)) return false; + const here = new URL(window.location.href); + if (url.origin === here.origin && url.pathname === here.pathname && url.hash) return false; + return true; +} + // appPath returns the in-app path an anchor points at, or "" when clicking // it should be left to the browser. function appPath(a) { @@ -79,9 +94,18 @@ export function initHeader() { const a = e.target.closest?.("a[href]"); if (!a || !window.htmx) return; const path = appPath(a); - if (!path) return; - e.preventDefault(); - navigate(path); + if (path) { + e.preventDefault(); + navigate(path); + return; + } + // Everything else (other sites, raw files, links in project READMEs) + // opens in a new tab so the app tab stays put. In-page #anchors and + // non-http links (mailto:) are left to the browser. + if (opensOut(a)) { + e.preventDefault(); + window.open(a.href, "_blank", "noopener,noreferrer"); + } }; document.addEventListener("click", headerHandlers.linkClick);