frontend now looks like firefox and chrome tabs on linux

This commit is contained in:
Joe Lothan 2026-05-25 22:53:11 -04:00
parent 7c4572aafb
commit 3ea88790b5
2 changed files with 123 additions and 67 deletions

View file

@ -57,6 +57,7 @@
image-rendering: auto;
}
/* ── Base tab styles ── */
.tab {
display: inline-flex;
align-items: center;
@ -68,68 +69,71 @@
min-width: 100px;
height: 36px;
transition: background 0.15s;
/* Default: Firefox inactive tab style */
border-radius: 4px;
border: none;
margin: 0 1px;
background: transparent;
}
.tab:hover {
background: var(--tab-hover);
border-radius: 4px;
/* ── Linux Firefox (default) ── */
/* Inactive: transparent, small margin, rounded */
.tab {
border-radius: 6px;
margin: 0 1px;
}
/* Firefox selected tab — solid bg, rounded top, connects to content below */
.browser-firefox .tab-active,
.browser-firefox .tab-active:hover {
/* Hover: pill-shaped background */
.tab:hover {
background: var(--tab-hover);
border-radius: 8px;
}
/* Selected: white bg, rounded top, connects to viewer below */
.tab-active,
.tab-active:hover {
background: var(--tab-bg);
border-radius: 8px 8px 0 0;
box-shadow: 0 1px 0 0 var(--tab-bg);
}
/* Chrome / Chromium — inactive tabs: no bg, thin separators between */
/* ── Linux Chrome ── */
/* Inactive: always inset dimensions, separator via ::before */
.browser-chrome .tab {
border-radius: 0;
margin: 0;
padding: 6px 14px;
border-right: 1px solid var(--tab-border);
border-radius: 8px;
margin: 3px 2px;
padding: 3px 12px;
height: 30px;
position: relative;
}
.browser-chrome .tab::before {
content: "";
position: absolute;
left: -2px;
top: 25%;
height: 50%;
width: 1px;
background: var(--tab-border);
}
/* Hide separator on hover/active tab AND on the tab after hover/active */
.browser-chrome .tab:hover::before,
.browser-chrome .tab-active::before,
.browser-chrome .tab:hover + .tab::before,
.browser-chrome .tab-active + .tab::before {
background: transparent;
}
/* Hover: just add background (no layout change) */
.browser-chrome .tab:hover {
background: var(--tab-hover);
border-radius: 8px 8px 0 0;
border-right: 1px solid transparent;
}
/* Chrome selected tab — raised, rounded top, solid bg */
/* Selected: white bg, subtle shadow */
.browser-chrome .tab-active,
.browser-chrome .tab-active:hover {
background: var(--tab-bg);
border-radius: 10px 10px 0 0;
border-right: 1px solid transparent;
box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.1);
}
/* Safari — flat pill, very minimal */
.browser-safari .tab {
border-radius: 6px;
margin: 0 2px;
height: 32px;
padding: 4px 10px;
}
.browser-safari .tab:hover {
background: var(--tab-hover);
}
/* Safari selected tab — solid pill, slightly elevated */
.browser-safari .tab-active,
.browser-safari .tab-active:hover {
background: var(--tab-bg);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}
.tab-about .tab-title {
font-weight: 600;
}
@ -181,36 +185,69 @@
100% { transform: translateX(0); }
}
/* Inline iframe viewer */
/* ── Inline iframe viewer ── */
.iframe-viewer {
width: 100%;
height: 75vh;
background: var(--viewer-bg);
border-top: 2px solid var(--tab-border);
border-bottom: 2px solid var(--tab-border);
}
.iframe-header {
/* Toolbar area (address bar) */
.iframe-toolbar {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
padding: 8px 12px;
background: var(--tab-bg);
}
.iframe-header .tab-title {
/* URL bar — rounded pill, looks like a browser address bar */
.iframe-urlbar {
flex: 1;
font-size: 13px;
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background: var(--bg);
border-radius: 24px;
min-width: 0;
}
.iframe-header a {
color: var(--text-muted);
font-size: 12px;
text-decoration: none;
/* Firefox: more rectangular address bar */
.browser-firefox .iframe-urlbar {
border-radius: 8px;
}
.iframe-header a:hover {
.iframe-urlbar .tab-icon {
flex-shrink: 0;
}
.iframe-urlbar .url-text {
font-size: 14px;
line-height: 1;
color: var(--text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-decoration: none;
flex-shrink: 0;
}
.iframe-urlbar .url-text:hover {
text-decoration: underline;
}
.iframe-urlbar .url-title {
font-size: 12px;
line-height: 1;
color: var(--text-muted);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: right;
flex: 1;
min-width: 0;
}
.iframe-close {
@ -219,8 +256,9 @@
color: var(--text);
font-size: 16px;
cursor: pointer;
padding: 2px 6px;
border-radius: 4px;
padding: 4px 8px;
border-radius: 50%;
line-height: 1;
}
.iframe-close:hover {
@ -229,7 +267,7 @@
.iframe-viewer iframe {
width: 100%;
height: calc(75vh - 36px);
height: calc(75vh - 56px);
border: none;
background: white;
}

View file

@ -14,16 +14,26 @@ const loadedBundles = new Set();
const container = document.getElementById("tab-container");
const loadingEl = document.getElementById("loading");
// Detect browser and set class on body for tab styling
// Detect browser and OS, set classes on body for tab styling
function detectBrowser() {
const ua = navigator.userAgent;
if (ua.includes("Firefox")) return "firefox";
if (ua.includes("Safari") && !ua.includes("Chrome")) return "safari";
if (ua.includes("Edg/")) return "edge";
if (ua.includes("Safari") && !ua.includes("Chrome") && !ua.includes("CriOS")) return "safari";
return "chrome"; // Chrome, Edge, Opera, Brave, etc.
}
function detectOS() {
const ua = navigator.userAgent;
if (ua.includes("Windows")) return "windows";
if (ua.includes("Macintosh") || ua.includes("Mac OS")) return "mac";
if (ua.includes("iPhone") || ua.includes("iPad")) return "mac";
return "linux"; // Linux, Android, ChromeOS, unknown
}
const browserName = detectBrowser();
document.body.classList.add(`browser-${browserName}`);
const osName = detectOS();
document.body.classList.add(`browser-${browserName}`, `os-${osName}`);
const isFirefox = browserName === "firefox";
console.log(`EveryTab: browser=${browserName}, os=${osName}`);
// How many tabs fit in one row?
function tabsPerRow() {
@ -266,39 +276,47 @@ function openInlineViewer(tabEl, entry, url) {
const viewer = document.createElement("div");
viewer.className = "iframe-viewer";
const header = document.createElement("div");
header.className = "iframe-header";
// Toolbar (address bar area)
const toolbar = document.createElement("div");
toolbar.className = "iframe-toolbar";
// URL bar pill
const urlbar = document.createElement("div");
urlbar.className = "iframe-urlbar";
if (entry.icon) {
const icon = document.createElement("img");
icon.className = "tab-icon";
icon.src = `data:image/png;base64,${entry.icon}`;
header.appendChild(icon);
urlbar.appendChild(icon);
}
const title = document.createElement("span");
title.className = "tab-title";
title.textContent = entry.title || entry.url;
header.appendChild(title);
const link = document.createElement("a");
link.className = "url-text";
link.href = url;
link.target = "_blank";
link.rel = "noopener";
link.textContent = entry.url + " ↗";
header.appendChild(link);
link.textContent = entry.url;
urlbar.appendChild(link);
const urlTitle = document.createElement("span");
urlTitle.className = "url-title";
urlTitle.textContent = entry.title || "";
urlbar.appendChild(urlTitle);
toolbar.appendChild(urlbar);
const close = document.createElement("button");
close.className = "iframe-close";
close.textContent = "✕";
close.addEventListener("click", closeInlineViewer);
header.appendChild(close);
toolbar.appendChild(close);
const iframe = document.createElement("iframe");
iframe.sandbox = "allow-scripts allow-same-origin allow-forms";
iframe.src = url;
viewer.appendChild(header);
viewer.appendChild(toolbar);
viewer.appendChild(iframe);
// Insert after the row