a8786b4c1c
Introduce core application structure and browser management: add NebulaController and run entry (src/app/*) to centralize window, tab and CEF lifecycle logic; implement TabManager and NebulaTab (src/browser/*) for tab creation, navigation and state tracking; add URL utilities (NormalizeNavigationInput, JsonEscape) and CEF browser client glue (src/cef/browser_client.cpp/.h) to forward chrome commands and content events. Update app/main.cpp to delegate startup to nebula::app::RunNebula. Add UI assets (chrome.html, chrome.css, chrome.js, lucide, menu-popup updates) and remove obsolete nebot.html. Update CMakeLists to include new sources, add ${CMAKE_SOURCE_DIR}/src to includes and link dwmapi on Windows. Overall this refactors startup and splits responsibilities for cleaner tab and browser lifecycle handling.
384 lines
7.7 KiB
CSS
384 lines
7.7 KiB
CSS
:root {
|
|
--bg: #080a0f;
|
|
--surface: #0e1119;
|
|
--surface-raised: #141824;
|
|
--surface-hover: rgba(255, 255, 255, 0.06);
|
|
--text: #e8e8f0;
|
|
--muted: #7a7e90;
|
|
--accent: #7b2eff;
|
|
--accent-2: #00c6ff;
|
|
--outline: #1f2533;
|
|
--outline-soft: rgba(255, 255, 255, 0.06);
|
|
--danger: #e0445c;
|
|
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;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: "InterVariable", "Segoe UI", system-ui, sans-serif;
|
|
user-select: none;
|
|
}
|
|
|
|
button,
|
|
input {
|
|
font: inherit;
|
|
}
|
|
|
|
button {
|
|
border: 0;
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
button:disabled {
|
|
cursor: default;
|
|
opacity: 0.3;
|
|
}
|
|
|
|
/* ── Chrome shell ───────────────────────────────────────────── */
|
|
|
|
.nebula-chrome {
|
|
display: grid;
|
|
grid-template-rows: 42px 52px;
|
|
height: 100%;
|
|
border-bottom: 1px solid var(--outline);
|
|
}
|
|
|
|
/* ── Title row ──────────────────────────────────────────────── */
|
|
|
|
.title-row,
|
|
.toolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.title-row {
|
|
gap: 10px;
|
|
padding: 0 0 0 12px;
|
|
background: var(--bg);
|
|
}
|
|
|
|
/* ── Brand ──────────────────────────────────────────────────── */
|
|
|
|
.brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
min-width: 104px;
|
|
color: var(--accent-2);
|
|
font-size: 0.73rem;
|
|
font-weight: 800;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.brand-icon {
|
|
width: 17px;
|
|
height: 17px;
|
|
}
|
|
|
|
/* ── Tabs ───────────────────────────────────────────────────── */
|
|
|
|
.tabs {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 3px;
|
|
min-width: 0;
|
|
flex: 1;
|
|
height: 100%;
|
|
}
|
|
|
|
.tab {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
min-width: 0;
|
|
width: min(260px, 38vw);
|
|
height: 33px;
|
|
padding: 0 14px;
|
|
border-radius: 10px 10px 0 0;
|
|
border: 1px solid transparent;
|
|
border-bottom: none;
|
|
background: transparent;
|
|
color: var(--muted);
|
|
cursor: pointer;
|
|
font-size: 0.82rem;
|
|
transition: background 120ms, color 120ms;
|
|
}
|
|
|
|
.tab:hover:not(.active) {
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
}
|
|
|
|
.tab.active {
|
|
background: var(--surface-raised);
|
|
border-color: var(--outline);
|
|
color: var(--text);
|
|
}
|
|
|
|
.tab-title {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.tab-favicon,
|
|
.tab-loading {
|
|
width: 16px;
|
|
height: 16px;
|
|
flex: 0 0 auto;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.tab-favicon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--accent);
|
|
opacity: 0.85;
|
|
border-radius: 999px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tab-favicon.has-favicon {
|
|
background: transparent;
|
|
border-radius: 3px;
|
|
opacity: 1;
|
|
}
|
|
|
|
.tab-favicon.empty {
|
|
width: 13px;
|
|
height: 13px;
|
|
}
|
|
|
|
.tab-favicon img {
|
|
display: block;
|
|
width: 16px;
|
|
height: 16px;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.tab-loading {
|
|
width: 13px;
|
|
height: 13px;
|
|
border: 2px solid rgba(0, 198, 255, 0.2);
|
|
border-top-color: var(--accent-2);
|
|
border-radius: 999px;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
.tab-close {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 20px;
|
|
height: 20px;
|
|
padding: 0;
|
|
border-radius: 6px;
|
|
background: transparent;
|
|
color: var(--muted);
|
|
opacity: 0;
|
|
transition: background 120ms, color 120ms, opacity 120ms;
|
|
}
|
|
|
|
.tab:hover .tab-close,
|
|
.tab.active .tab-close,
|
|
.tab-close:focus-visible {
|
|
opacity: 1;
|
|
}
|
|
|
|
.tab-close:hover {
|
|
background: var(--surface-hover);
|
|
color: var(--text);
|
|
}
|
|
|
|
.tab-add {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
margin-bottom: 2px;
|
|
border-radius: 8px;
|
|
border: 1px solid transparent;
|
|
background: transparent;
|
|
color: var(--muted);
|
|
transition: background 120ms, color 120ms, border-color 120ms;
|
|
}
|
|
|
|
.tab-add:hover {
|
|
background: var(--surface-hover);
|
|
border-color: var(--outline);
|
|
color: var(--text);
|
|
}
|
|
|
|
/* ── Window controls ────────────────────────────────────────── */
|
|
|
|
.window-controls {
|
|
display: flex;
|
|
align-self: stretch;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
border-top-right-radius: 10px;
|
|
}
|
|
|
|
.window-controls button {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 46px;
|
|
background: transparent;
|
|
color: var(--muted);
|
|
transition: background 100ms, color 100ms;
|
|
}
|
|
|
|
.window-controls button:hover {
|
|
background: var(--surface-hover);
|
|
color: var(--text);
|
|
}
|
|
|
|
.window-controls .close:hover {
|
|
background: var(--danger);
|
|
color: white;
|
|
}
|
|
|
|
/* ── Toolbar ────────────────────────────────────────────────── */
|
|
|
|
.toolbar {
|
|
gap: 4px;
|
|
padding: 0 12px;
|
|
background: var(--surface-raised);
|
|
border-top: 1px solid var(--outline);
|
|
}
|
|
|
|
/* ── Lucide icon sizing ─────────────────────────────────────── */
|
|
|
|
/* Lucide replaces <i data-lucide> with <svg>; enforce consistent size */
|
|
.icon-button svg,
|
|
.tab-close svg,
|
|
.tab-add svg,
|
|
.window-controls svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
display: block;
|
|
stroke-width: 1.75;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ── Icon buttons ───────────────────────────────────────────── */
|
|
|
|
.icon-button {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 34px;
|
|
height: 34px;
|
|
padding: 0;
|
|
border-radius: 9px;
|
|
background: transparent;
|
|
border: 1px solid transparent;
|
|
color: var(--muted);
|
|
flex-shrink: 0;
|
|
transition: background 120ms, color 120ms, border-color 120ms;
|
|
}
|
|
|
|
.icon-button:hover:not(:disabled) {
|
|
background: var(--surface-hover);
|
|
border-color: var(--outline);
|
|
color: var(--text);
|
|
}
|
|
|
|
/* ── Address bar ────────────────────────────────────────────── */
|
|
|
|
.address-shell {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
min-width: 160px;
|
|
height: 36px;
|
|
flex: 1;
|
|
margin: 0 4px;
|
|
overflow: hidden;
|
|
border: 1px solid var(--outline);
|
|
border-radius: 10px;
|
|
background: var(--surface);
|
|
transition: border-color 140ms, box-shadow 140ms;
|
|
}
|
|
|
|
.address-shell:focus-within {
|
|
border-color: rgba(123, 46, 255, 0.55);
|
|
box-shadow: 0 0 0 3px rgba(123, 46, 255, 0.12);
|
|
}
|
|
|
|
.address-shell input {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
outline: 0;
|
|
padding: 0 16px;
|
|
background: transparent;
|
|
color: var(--text);
|
|
font-size: 0.84rem;
|
|
}
|
|
|
|
.address-shell input::placeholder {
|
|
color: var(--muted);
|
|
}
|
|
|
|
.progress-bar {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
height: 2px;
|
|
width: 0%;
|
|
pointer-events: none;
|
|
background: var(--accent);
|
|
border-radius: 0 2px 2px 0;
|
|
opacity: 0.7;
|
|
transition: width 120ms ease, opacity 160ms ease;
|
|
}
|
|
|
|
/* ── Utilities ──────────────────────────────────────────────── */
|
|
|
|
.sr-only {
|
|
position: absolute;
|
|
width: 1px;
|
|
height: 1px;
|
|
padding: 0;
|
|
margin: -1px;
|
|
overflow: hidden;
|
|
clip: rect(0, 0, 0, 0);
|
|
white-space: nowrap;
|
|
border: 0;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|