Add layout visibility toggles and improve search bar UI
Introduces checkboxes in the edit toolbar to toggle visibility of the greeting, bookmarks, and 'At A Glance' widgets, persisting user preferences in localStorage. Refactors the search bar and search engine selector for a unified, glassy pill appearance and improved responsiveness. The clock now displays hours and minutes only, updating precisely at the start of each minute. Draggable elements are clamped to remain within the viewport, and visibility changes are applied immediately and on save/cancel.
This commit is contained in:
+120
-48
@@ -104,60 +104,133 @@ body, html {
|
|||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Utility: fully hide elements when user toggles them off */
|
||||||
|
.is-hidden { display: none !important; }
|
||||||
|
|
||||||
/* Search bar container */
|
/* Search bar container */
|
||||||
.search-container {
|
.search-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
width: 550px; /* Increased width for the new button */
|
width: 680px;
|
||||||
max-width: 95vw;
|
max-width: min(92vw, 900px);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
z-index: 300; /* ensure dropdown overlays bookmarks/top-sites stacking contexts */
|
||||||
top: var(--home-search-y);
|
top: var(--home-search-y);
|
||||||
|
/* Unified glassy pill */
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(255,255,255,0.14),
|
||||||
|
rgba(255,255,255,0.10)
|
||||||
|
);
|
||||||
|
border: 1px solid rgba(255,255,255,0.20);
|
||||||
|
border-radius: 9999px;
|
||||||
|
box-shadow: 0 18px 50px -22px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.12);
|
||||||
|
backdrop-filter: blur(10px) saturate(140%);
|
||||||
|
-webkit-backdrop-filter: blur(10px) saturate(140%);
|
||||||
|
padding: 6px 8px;
|
||||||
|
transition: box-shadow 180ms ease, border-color 180ms ease, background 180ms ease, transform 120ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container:hover {
|
||||||
|
background: linear-gradient(180deg, rgba(255,255,255,0.18), rgba(255,255,255,0.12));
|
||||||
|
border-color: rgba(255,255,255,0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container:focus-within {
|
||||||
|
box-shadow: 0 22px 60px -24px rgba(0,0,0,0.9), 0 0 0 2px rgba(123,46,255,0.45), inset 0 1px 0 rgba(255,255,255,0.16);
|
||||||
|
border-color: rgba(123,46,255,0.55);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search bar */
|
/* Search bar */
|
||||||
.search-bar {
|
.search-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1; /* Allow search bar to take remaining space */
|
flex: 1; /* Take remaining space inside the pill */
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #ffffff;
|
background: transparent;
|
||||||
border-radius: 0 70px 70px 0; /* Curve right side */
|
border-radius: 9999px;
|
||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
padding: 0 6px 0 2px;
|
||||||
padding: 0.25rem;
|
height: 44px;
|
||||||
overflow: hidden;
|
|
||||||
height: 54px; /* Match button height */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar input.search-input {
|
.search-bar input.search-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 0.75rem 1rem;
|
padding: 0 10px 0 8px;
|
||||||
font-size: 1rem;
|
font-size: 1.05rem;
|
||||||
color: #333;
|
line-height: 1;
|
||||||
|
color: #ffffff;
|
||||||
|
caret-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar input.search-input::placeholder {
|
||||||
|
color: rgba(255,255,255,0.55);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar button.search-btn {
|
.search-bar button.search-btn {
|
||||||
border: none;
|
border: 1px solid rgba(255,255,255,0.14);
|
||||||
background: linear-gradient(90deg, var(--primary), var(--accent));
|
background: rgba(12,14,20,0.45);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0.75rem;
|
width: 40px;
|
||||||
border-radius: 50%;
|
height: 40px;
|
||||||
|
border-radius: 9999px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: transform 0.2s ease;
|
box-shadow: none;
|
||||||
|
transition: transform 120ms ease, background 160ms ease, border-color 160ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-bar button.search-btn:hover {
|
.search-bar button.search-btn:hover { transform: scale(1.02); background: rgba(24,26,34,0.55); border-color: rgba(255,255,255,0.24); }
|
||||||
transform: scale(1.1);
|
.search-bar button.search-btn:active { transform: scale(0.98); }
|
||||||
}
|
|
||||||
|
|
||||||
.search-bar button.search-btn .material-symbols-outlined {
|
.search-bar button.search-btn .material-symbols-outlined {
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Search engine trigger unified look */
|
||||||
|
.search-engine-selector { position: relative; display: flex; align-items: center; }
|
||||||
|
.search-engine-btn {
|
||||||
|
background: rgba(12,14,20,0.45);
|
||||||
|
border: 1px solid rgba(255,255,255,0.14);
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 8px 10px 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 44px;
|
||||||
|
width: 48px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: none;
|
||||||
|
transition: background 160ms ease, border-color 160ms ease, transform 120ms ease;
|
||||||
|
}
|
||||||
|
.search-engine-btn:hover { background: rgba(24,26,34,0.55); border-color: rgba(255,255,255,0.24); }
|
||||||
|
.search-engine-btn:active { transform: scale(0.98); }
|
||||||
|
|
||||||
|
.search-engine-btn img { width: 22px; height: 22px; filter: none; }
|
||||||
|
|
||||||
|
/* Subtle divider after the engine button */
|
||||||
|
.search-engine-selector::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: -4px;
|
||||||
|
top: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
width: 1px;
|
||||||
|
background: linear-gradient(to bottom, rgba(255,255,255,0.06), rgba(255,255,255,0.24), rgba(255,255,255,0.06));
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 520px) {
|
||||||
|
.search-container { width: 94vw; padding: 6px; }
|
||||||
|
.search-bar { height: 42px; }
|
||||||
|
.search-engine-btn { height: 42px; width: 46px; }
|
||||||
|
.search-bar button.search-btn { width: 38px; height: 38px; }
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove default focus outline */
|
/* Remove default focus outline */
|
||||||
.search-bar input.search-input:focus,
|
.search-bar input.search-input:focus,
|
||||||
.search-bar button.search-btn:focus {
|
.search-bar button.search-btn:focus {
|
||||||
@@ -165,44 +238,38 @@ body, html {
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search Engine Selector */
|
/* (legacy Search Engine Selector block removed; unified styles are defined above) */
|
||||||
.search-engine-selector {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-engine-btn {
|
|
||||||
background: #ffffff;
|
|
||||||
border: none;
|
|
||||||
border-radius: 70px 0 0 70px; /* Curve left side */
|
|
||||||
padding: 0.25rem 0.5rem 0.25rem 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
height: 54px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-engine-btn img {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-engine-dropdown {
|
.search-engine-dropdown {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 110%;
|
top: 110%;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: #ffffff;
|
background: linear-gradient(180deg, rgba(18,20,24,0.96), rgba(18,20,24,0.9));
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
border: 1px solid rgba(255,255,255,0.12);
|
||||||
|
box-shadow: 0 18px 50px -22px rgba(0,0,0,0.8), inset 0 1px 0 rgba(255,255,255,0.06);
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
/* Animated open/close */
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
transform-origin: top left;
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
max-height: 320px; /* enough for options; adjust if you add more */
|
||||||
|
transition: opacity 160ms ease, transform 160ms ease, max-height 200ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-engine-dropdown.hidden {
|
.search-engine-dropdown.hidden {
|
||||||
display: none;
|
opacity: 0;
|
||||||
|
transform: translateY(-6px) scale(0.98);
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
max-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-engine-option {
|
.search-engine-option {
|
||||||
@@ -214,15 +281,20 @@ body, html {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-engine-option:hover {
|
.search-engine-option:hover { background-color: rgba(255,255,255,0.08); }
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-engine-option img {
|
.search-engine-option img {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Respect reduced motion preferences */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.search-engine-dropdown {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Bookmark grid */
|
/* Bookmark grid */
|
||||||
.bookmarks {
|
.bookmarks {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
+14
-2
@@ -62,11 +62,11 @@
|
|||||||
<!-- At a glance widget -->
|
<!-- At a glance widget -->
|
||||||
<aside class="glance" aria-live="polite">
|
<aside class="glance" aria-live="polite">
|
||||||
<div class="glance-card">
|
<div class="glance-card">
|
||||||
<div class="glance-title">At a glance</div>
|
<div class="glance-title">At A Glance</div>
|
||||||
<div class="glance-grid">
|
<div class="glance-grid">
|
||||||
<div class="glance-tile">
|
<div class="glance-tile">
|
||||||
<div class="glance-label">Time</div>
|
<div class="glance-label">Time</div>
|
||||||
<div id="clock" class="glance-value">--:--:--</div>
|
<div id="clock" class="glance-value">--:--</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="glance-tile">
|
<div class="glance-tile">
|
||||||
<div class="glance-label">Weather</div>
|
<div class="glance-label">Weather</div>
|
||||||
@@ -78,6 +78,18 @@
|
|||||||
|
|
||||||
<!-- Edit mode toolbar -->
|
<!-- Edit mode toolbar -->
|
||||||
<div id="editToolbar" class="edit-toolbar" hidden>
|
<div id="editToolbar" class="edit-toolbar" hidden>
|
||||||
|
<label style="display:flex;align-items:center;gap:6px;margin-right:8px;">
|
||||||
|
<input type="checkbox" id="toggleShowGreeting" checked>
|
||||||
|
<span>Show greeting</span>
|
||||||
|
</label>
|
||||||
|
<label style="display:flex;align-items:center;gap:6px;margin-right:8px;">
|
||||||
|
<input type="checkbox" id="toggleShowBookmarks" checked>
|
||||||
|
<span>Show bookmarks</span>
|
||||||
|
</label>
|
||||||
|
<label style="display:flex;align-items:center;gap:6px;margin-right:12px;">
|
||||||
|
<input type="checkbox" id="toggleShowGlance" checked>
|
||||||
|
<span>Show At a Glance</span>
|
||||||
|
</label>
|
||||||
<button id="cancelEditBtn" class="btn secondary" aria-label="Cancel layout edits">Cancel</button>
|
<button id="cancelEditBtn" class="btn secondary" aria-label="Cancel layout edits">Cancel</button>
|
||||||
<button id="saveEditBtn" class="btn primary" aria-label="Save layout edits">Save</button>
|
<button id="saveEditBtn" class="btn primary" aria-label="Save layout edits">Save</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+122
-14
@@ -29,6 +29,9 @@ const greetingTitleEl = document.getElementById('greeting');
|
|||||||
const editToolbar = document.getElementById('editToolbar');
|
const editToolbar = document.getElementById('editToolbar');
|
||||||
const saveEditBtn = document.getElementById('saveEditBtn');
|
const saveEditBtn = document.getElementById('saveEditBtn');
|
||||||
const cancelEditBtn = document.getElementById('cancelEditBtn');
|
const cancelEditBtn = document.getElementById('cancelEditBtn');
|
||||||
|
const toggleShowGreeting = document.getElementById('toggleShowGreeting');
|
||||||
|
const toggleShowBookmarks= document.getElementById('toggleShowBookmarks');
|
||||||
|
const toggleShowGlance = document.getElementById('toggleShowGlance');
|
||||||
let selectedIcon = initialIcons[0];
|
let selectedIcon = initialIcons[0];
|
||||||
let availableIcons = initialIcons;
|
let availableIcons = initialIcons;
|
||||||
let currentIconSetKey = 'material';
|
let currentIconSetKey = 'material';
|
||||||
@@ -552,13 +555,21 @@ function computeGreeting(d = new Date()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startClock() {
|
function startClock() {
|
||||||
const tick = () => {
|
const format = { hour: 'numeric', minute: '2-digit', hour12: true };
|
||||||
|
const update = () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (greetingEl) greetingEl.textContent = computeGreeting(now);
|
if (greetingEl) greetingEl.textContent = computeGreeting(now);
|
||||||
if (clockEl) clockEl.textContent = now.toLocaleTimeString([], { hour12: true });
|
if (clockEl) clockEl.textContent = now.toLocaleTimeString([], format);
|
||||||
};
|
};
|
||||||
tick();
|
// Initial draw
|
||||||
setInterval(tick, 1000);
|
update();
|
||||||
|
// Align updates to the start of each minute
|
||||||
|
const now = new Date();
|
||||||
|
const delay = 60000 - (now.getSeconds() * 1000 + now.getMilliseconds());
|
||||||
|
setTimeout(() => {
|
||||||
|
update();
|
||||||
|
setInterval(update, 60000);
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unit helpers
|
// Unit helpers
|
||||||
@@ -762,8 +773,15 @@ function setEditMode(on) {
|
|||||||
greetY: Number(localStorage.getItem('nebula-home-greeting-y') || 12),
|
greetY: Number(localStorage.getItem('nebula-home-greeting-y') || 12),
|
||||||
searchY: Number(localStorage.getItem('nebula-home-search-y') || 22),
|
searchY: Number(localStorage.getItem('nebula-home-search-y') || 22),
|
||||||
bmY: Number(localStorage.getItem('nebula-home-bookmarks-y') || 40),
|
bmY: Number(localStorage.getItem('nebula-home-bookmarks-y') || 40),
|
||||||
corner: localStorage.getItem('nebula-home-glance-corner') || 'br'
|
corner: localStorage.getItem('nebula-home-glance-corner') || 'br',
|
||||||
|
showGreeting: localStorage.getItem('nebula-show-greeting') !== 'false',
|
||||||
|
showBookmarks: localStorage.getItem('nebula-show-bookmarks') !== 'false',
|
||||||
|
showGlance: localStorage.getItem('nebula-show-glance') !== 'false'
|
||||||
};
|
};
|
||||||
|
// Initialize toggles to snapshot values
|
||||||
|
if (toggleShowGreeting) toggleShowGreeting.checked = snapshot.showGreeting;
|
||||||
|
if (toggleShowBookmarks) toggleShowBookmarks.checked = snapshot.showBookmarks;
|
||||||
|
if (toggleShowGlance) toggleShowGlance.checked = snapshot.showGlance;
|
||||||
} else {
|
} else {
|
||||||
snapshot = null;
|
snapshot = null;
|
||||||
}
|
}
|
||||||
@@ -776,24 +794,49 @@ if (editBtn) {
|
|||||||
function vhFromPx(px) { return (px / window.innerHeight) * 100; }
|
function vhFromPx(px) { return (px / window.innerHeight) * 100; }
|
||||||
function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }
|
function clamp(n, min, max) { return Math.max(min, Math.min(max, n)); }
|
||||||
|
|
||||||
|
// Visibility helpers
|
||||||
|
function applyVisibilityFromStorage() {
|
||||||
|
const showGreeting = localStorage.getItem('nebula-show-greeting') !== 'false';
|
||||||
|
const showBookmarks = localStorage.getItem('nebula-show-bookmarks') !== 'false';
|
||||||
|
const showGlance = localStorage.getItem('nebula-show-glance') !== 'false';
|
||||||
|
if (greetingEl) greetingEl.classList.toggle('is-hidden', !showGreeting);
|
||||||
|
if (topSitesEl) topSitesEl.classList.toggle('is-hidden', !showBookmarks);
|
||||||
|
if (glanceEl) glanceEl.classList.toggle('is-hidden', !showGlance);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyVisibilityFromStorage();
|
||||||
|
|
||||||
function makeDragY(el, storageKey, cssVar) {
|
function makeDragY(el, storageKey, cssVar) {
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
let startY = 0; let startTop = 0; let dragging = false;
|
let startY = 0; let startTopVh = 0; let dragging = false;
|
||||||
|
// Cache geometry at drag start for consistent clamping in px
|
||||||
|
let startRectTopPx = 0; let elHeightPx = 0; const MARGIN_PX = 12;
|
||||||
const onDown = (ev) => {
|
const onDown = (ev) => {
|
||||||
if (!editMode) return; dragging = true;
|
if (!editMode) return; dragging = true;
|
||||||
startY = (ev.touches ? ev.touches[0].clientY : ev.clientY);
|
const p = ev.touches ? ev.touches[0] : ev;
|
||||||
// current computed var
|
startY = p.clientY;
|
||||||
|
// current computed var (in vh)
|
||||||
const current = Number((getComputedStyle(document.documentElement).getPropertyValue(cssVar) || '0vh').replace('vh',''));
|
const current = Number((getComputedStyle(document.documentElement).getPropertyValue(cssVar) || '0vh').replace('vh',''));
|
||||||
startTop = isNaN(current) ? 0 : current;
|
startTopVh = isNaN(current) ? 0 : current;
|
||||||
|
// snapshot element geometry
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
startRectTopPx = rect.top;
|
||||||
|
elHeightPx = rect.height;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
};
|
};
|
||||||
const onMove = (ev) => {
|
const onMove = (ev) => {
|
||||||
if (!dragging) return;
|
if (!dragging) return;
|
||||||
const y = (ev.touches ? ev.touches[0].clientY : ev.clientY);
|
const p = ev.touches ? ev.touches[0] : ev;
|
||||||
const deltaPx = y - startY;
|
const deltaPx = p.clientY - startY;
|
||||||
const deltaVh = vhFromPx(deltaPx);
|
// Clamp so the element stays within the viewport with a small margin
|
||||||
const next = clamp(startTop + deltaVh, 0, 90);
|
const minTopPx = MARGIN_PX;
|
||||||
document.documentElement.style.setProperty(cssVar, `${next}vh`);
|
const maxTopPx = Math.max(minTopPx, window.innerHeight - MARGIN_PX - elHeightPx);
|
||||||
|
const desiredTopPx = startRectTopPx + deltaPx;
|
||||||
|
const clampedTopPx = clamp(desiredTopPx, minTopPx, maxTopPx);
|
||||||
|
const clampedDeltaPx = clampedTopPx - startRectTopPx;
|
||||||
|
const deltaVh = vhFromPx(clampedDeltaPx);
|
||||||
|
const nextVh = startTopVh + deltaVh;
|
||||||
|
document.documentElement.style.setProperty(cssVar, `${nextVh}vh`);
|
||||||
};
|
};
|
||||||
const onUp = () => {
|
const onUp = () => {
|
||||||
if (!dragging) return; dragging = false;
|
if (!dragging) return; dragging = false;
|
||||||
@@ -862,6 +905,54 @@ makeDragGlance(glanceEl);
|
|||||||
// Restore greeting to Y-only drag
|
// Restore greeting to Y-only drag
|
||||||
makeDragY(greetingTitleEl, 'nebula-home-greeting-y', '--home-greeting-y');
|
makeDragY(greetingTitleEl, 'nebula-home-greeting-y', '--home-greeting-y');
|
||||||
|
|
||||||
|
// Keep draggable blocks within viewport on resize
|
||||||
|
function keepVisibleWithinViewport() {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const adjust = (el, cssVar) => {
|
||||||
|
if (!el) return;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const MARGIN_PX = 12;
|
||||||
|
const minTopPx = MARGIN_PX;
|
||||||
|
const maxTopPx = Math.max(minTopPx, window.innerHeight - MARGIN_PX - rect.height);
|
||||||
|
let topPx = rect.top;
|
||||||
|
if (topPx < minTopPx || topPx > maxTopPx) {
|
||||||
|
const currentVh = Number((getComputedStyle(root).getPropertyValue(cssVar) || '0vh').replace('vh','')) || 0;
|
||||||
|
// Compute how far to move (px) to bring within range, then convert to vh and adjust var
|
||||||
|
const targetTopPx = clamp(topPx, minTopPx, maxTopPx);
|
||||||
|
const deltaPx = targetTopPx - topPx;
|
||||||
|
const nextVh = currentVh + vhFromPx(deltaPx);
|
||||||
|
root.style.setProperty(cssVar, `${nextVh}vh`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
adjust(greetingTitleEl, '--home-greeting-y');
|
||||||
|
adjust(searchContainerEl, '--home-search-y');
|
||||||
|
adjust(topSitesEl, '--home-bookmarks-y');
|
||||||
|
}
|
||||||
|
|
||||||
|
let resizeTimer;
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(keepVisibleWithinViewport, 80);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle handlers (search cannot be hidden)
|
||||||
|
function bindVisibilityToggles() {
|
||||||
|
if (toggleShowGreeting) toggleShowGreeting.addEventListener('change', () => {
|
||||||
|
const val = toggleShowGreeting.checked;
|
||||||
|
if (greetingEl) greetingEl.classList.toggle('is-hidden', !val);
|
||||||
|
});
|
||||||
|
if (toggleShowBookmarks) toggleShowBookmarks.addEventListener('change', () => {
|
||||||
|
const val = toggleShowBookmarks.checked;
|
||||||
|
if (topSitesEl) topSitesEl.classList.toggle('is-hidden', !val);
|
||||||
|
});
|
||||||
|
if (toggleShowGlance) toggleShowGlance.addEventListener('change', () => {
|
||||||
|
const val = toggleShowGlance.checked;
|
||||||
|
if (glanceEl) glanceEl.classList.toggle('is-hidden', !val);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindVisibilityToggles();
|
||||||
|
|
||||||
// Save/Cancel handlers
|
// Save/Cancel handlers
|
||||||
if (saveEditBtn) saveEditBtn.addEventListener('click', () => {
|
if (saveEditBtn) saveEditBtn.addEventListener('click', () => {
|
||||||
// Persist current CSS variable values and pending corner
|
// Persist current CSS variable values and pending corner
|
||||||
@@ -874,11 +965,17 @@ if (saveEditBtn) saveEditBtn.addEventListener('click', () => {
|
|||||||
localStorage.setItem('nebula-home-greeting-y', String(gy));
|
localStorage.setItem('nebula-home-greeting-y', String(gy));
|
||||||
localStorage.setItem('nebula-home-search-y', String(sy));
|
localStorage.setItem('nebula-home-search-y', String(sy));
|
||||||
localStorage.setItem('nebula-home-bookmarks-y', String(by));
|
localStorage.setItem('nebula-home-bookmarks-y', String(by));
|
||||||
|
// Persist visibility
|
||||||
|
if (toggleShowGreeting) localStorage.setItem('nebula-show-greeting', String(!!toggleShowGreeting.checked));
|
||||||
|
if (toggleShowBookmarks) localStorage.setItem('nebula-show-bookmarks', String(!!toggleShowBookmarks.checked));
|
||||||
|
if (toggleShowGlance) localStorage.setItem('nebula-show-glance', String(!!toggleShowGlance.checked));
|
||||||
} catch {}
|
} catch {}
|
||||||
const corner = glanceEl?.dataset?.pendingCorner || localStorage.getItem(HOME_GLANCE_CORNER_KEY) || 'br';
|
const corner = glanceEl?.dataset?.pendingCorner || localStorage.getItem(HOME_GLANCE_CORNER_KEY) || 'br';
|
||||||
try { localStorage.setItem(HOME_GLANCE_CORNER_KEY, corner); } catch {}
|
try { localStorage.setItem(HOME_GLANCE_CORNER_KEY, corner); } catch {}
|
||||||
if (glanceEl) delete glanceEl.dataset.pendingCorner;
|
if (glanceEl) delete glanceEl.dataset.pendingCorner;
|
||||||
setEditMode(false);
|
setEditMode(false);
|
||||||
|
// Re-apply from saved storage to ensure consistent state after exiting edit mode
|
||||||
|
applyVisibilityFromStorage();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cancelEditBtn) cancelEditBtn.addEventListener('click', () => {
|
if (cancelEditBtn) cancelEditBtn.addEventListener('click', () => {
|
||||||
@@ -896,4 +993,15 @@ if (cancelEditBtn) cancelEditBtn.addEventListener('click', () => {
|
|||||||
applyHomeLayoutPrefs();
|
applyHomeLayoutPrefs();
|
||||||
}
|
}
|
||||||
setEditMode(false);
|
setEditMode(false);
|
||||||
|
// Revert visibility to snapshot
|
||||||
|
if (snapshot) {
|
||||||
|
if (greetingEl) greetingEl.classList.toggle('is-hidden', !snapshot.showGreeting);
|
||||||
|
if (topSitesEl) topSitesEl.classList.toggle('is-hidden', !snapshot.showBookmarks);
|
||||||
|
if (glanceEl) glanceEl.classList.toggle('is-hidden', !snapshot.showGlance);
|
||||||
|
if (toggleShowGreeting) toggleShowGreeting.checked = snapshot.showGreeting;
|
||||||
|
if (toggleShowBookmarks) toggleShowBookmarks.checked = snapshot.showBookmarks;
|
||||||
|
if (toggleShowGlance) toggleShowGlance.checked = snapshot.showGlance;
|
||||||
|
} else {
|
||||||
|
applyVisibilityFromStorage();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user