Test custom.css

This commit is contained in:
2026-03-21 19:28:12 +01:00
parent 24a782f3da
commit 99471fbc52
6 changed files with 12778 additions and 472 deletions
+325 -293
View File
@@ -1,42 +1,83 @@
/* ============================================================================ /* ============================================================================
HOMEPAGE CUSTOM CSS - OPTIMIZED KalliLab — FINAL CUSTOM CSS
Header CSS removed completely / everything else kept
============================================================================ */ ============================================================================ */
:root { :root {
/* Theme colors */ --kl-bg: #071015;
--dominant-color: #1a3a3a; --kl-panel: rgba(8, 14, 19, 0.78);
--accent-color: #00c8a0; --kl-panel-2: rgba(11, 19, 24, 0.86);
--accent-color-dim: #00c8a050;
--bg-color: #18191a;
--bg-color-light: #1e2021;
--text-color: #ffffff;
/* Derived properties using dynamic colors */ --kl-card: rgba(11, 19, 25, 0.72);
--card-border: 3.5px solid var(--dominant-color); --kl-card-hover: rgba(14, 24, 31, 0.78);
--card-border-hover: var(--accent-color); --kl-tab: rgba(9, 16, 22, 0.78);
--card-border-focus: var(--accent-color); --kl-tab-hover: rgba(12, 22, 28, 0.82);
--card-bg: var(--bg-color); --kl-tab-active: rgba(10, 22, 26, 0.84);
--card-bg-hover: var(--accent-color-dim);
--text-shadow: 0 0 10px var(--bg-color); --kl-border: rgba(118, 181, 187, 0.12);
--transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease; --kl-border-strong: rgba(0, 200, 160, 0.34);
--focus-ring: 0 0 8px var(--accent-color-dim); --kl-border-active: rgba(74, 219, 196, 0.62);
--font-family-base: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
--base-font-size: 14px; --kl-text: #e8fbfb;
--card-padding: 0; --kl-text-soft: rgba(220, 243, 243, 0.74);
--card-margin: 0; --kl-text-dim: rgba(182, 215, 215, 0.54);
--border-radius: 1.5rem;
--kl-shadow: 0 12px 30px rgba(0, 0, 0, 0.22);
--kl-glow: 0 0 18px rgba(0, 200, 160, 0.14);
--kl-glow-strong: 0 0 24px rgba(0, 200, 160, 0.22);
--kl-radius: 18px;
--kl-radius-sm: 14px;
--kl-font-ui: "Inter", "Segoe UI", system-ui, sans-serif;
--kl-font-tech: "JetBrains Mono", "Consolas", monospace;
--kl-transition: 180ms ease;
} }
/* ============================================================================ /* ============================================================================
NAVIGATION TABS BASE
============================================================================ */ ============================================================================ */
html,
body {
background: #050c10;
color: var(--kl-text);
font-family: var(--kl-font-ui);
}
body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background:
radial-gradient(circle at top center, rgba(0, 200, 160, 0.06), transparent 32%),
linear-gradient(180deg, rgba(4, 10, 14, 0.18), rgba(4, 10, 14, 0.48));
z-index: 0;
}
.container,
main,
#page_container {
position: relative;
z-index: 1;
}
.container {
max-width: 94%;
}
/* ============================================================================
TABS
============================================================================ */
#myTab { #myTab {
display: flex; display: flex;
gap: 60px; gap: 18px;
margin: 0 150px 30px; margin: 0 24px 22px;
padding: 0; padding: 0;
font: 18px/2 var(--font-family-base); background: transparent;
background: none;
backdrop-filter: none; backdrop-filter: none;
} }
@@ -48,231 +89,183 @@
padding: 0; padding: 0;
} }
[id$="-tab"] {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 50px;
min-width: 180px;
padding: 0 22px;
border: 1px solid var(--kl-border);
border-radius: 16px;
background: linear-gradient(180deg, rgba(10, 18, 24, 0.72), rgba(7, 13, 18, 0.82));
color: var(--kl-text-soft);
font-family: var(--kl-font-ui);
font-size: 0.95rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
cursor: pointer;
user-select: none;
transition:
border-color var(--kl-transition),
background var(--kl-transition),
box-shadow var(--kl-transition),
color var(--kl-transition);
}
[id$="-tab"]:hover {
color: var(--kl-text);
border-color: rgba(0, 200, 160, 0.28);
background: linear-gradient(180deg, var(--kl-tab-hover), rgba(8, 15, 20, 0.88));
box-shadow: var(--kl-glow);
transform: none;
}
[id$="-tab"].tab-focused,
[id$="-tab"].active,
[id$="-tab"][aria-selected="true"] {
color: var(--kl-text);
border-color: var(--kl-border-active);
background: linear-gradient(180deg, var(--kl-tab-active), rgba(7, 14, 18, 0.9));
box-shadow: var(--kl-glow-strong);
transform: none;
}
.tabcontent { .tabcontent {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
inset: 0;
display: none; display: none;
overflow-y: auto; overflow-y: auto;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease; transition: opacity 220ms ease, visibility 220ms ease;
} }
.tabcontent.active { .tabcontent.active {
display: block; display: block;
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
margin: 12px 0 15px;
}
[id$="-tab"] {
position: relative;
display: flex;
align-items: center;
justify-content: center;
min-height: 60px;
margin: 0; margin: 0;
border: var(--card-border); }
border-radius: var(--border-radius);
background-color: #18191a; /* ============================================================================
text-align: center; GROUP TITLES
text-shadow: var(--text-shadow); ============================================================================ */
text-transform: uppercase;
.service-group,
.services-group,
.bookmarks-group,
.widget-group {
margin-bottom: 18px;
}
h2,
.group-title,
.section-title {
color: var(--kl-text-soft);
font-family: var(--kl-font-ui);
font-size: 0.88rem;
font-weight: 700; font-weight: 700;
white-space: nowrap; letter-spacing: 0.14em;
cursor: pointer; text-transform: uppercase;
user-select: none;
transition: var(--transition);
-webkit-tap-highlight-color: transparent;
contain: layout style paint;
will-change: background-color, border-color;
backface-visibility: hidden;
transform: translateZ(0);
}
[id$="-tab"]::before {
content: "";
position: absolute;
inset: -5px;
background-color: var(--card-bg);
border-radius: var(--border-radius);
z-index: -1;
}
[id$="-tab"]:hover {
background-color: #1e2021;
border-color: var(--card-border-hover);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
[id$="-tab"].tab-focused,
[id$="-tab"].active {
background-color: #1e2021;
border-color: var(--card-border-focus);
outline: none;
box-shadow: var(--focus-ring);
} }
/* ============================================================================ /* ============================================================================
SERVICE CARDS SERVICE CARDS
============================================================================ */ ============================================================================ */
.service-card { .service-card {
position: relative; position: relative;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border: var(--card-border); border: 1px solid var(--kl-border);
border-radius: var(--border-radius); border-radius: var(--kl-radius);
background-color: #18191a; background: linear-gradient(180deg, rgba(12, 20, 26, 0.72), rgba(8, 14, 18, 0.72));
text-shadow: var(--text-shadow); box-shadow:
font-weight: 400; inset 0 1px 0 rgba(255, 255, 255, 0.03),
white-space: nowrap; var(--kl-shadow);
transition: var(--transition); color: var(--kl-text);
contain: layout style paint; text-shadow: none;
will-change: background-color, border-color; font-weight: 500;
content-visibility: auto; white-space: normal;
contain-intrinsic-size: auto 200px; overflow: hidden;
backface-visibility: hidden; transition:
transform: translateZ(0); border-color var(--kl-transition),
background var(--kl-transition),
box-shadow var(--kl-transition);
} }
.service-card::before { .service-card::before {
content: ""; content: "";
position: absolute; position: absolute;
inset: -5px; inset: 0;
background-color: var(--card-bg); background: linear-gradient(135deg, rgba(74, 219, 196, 0.03), transparent 36%);
border-radius: var(--border-radius); pointer-events: none;
z-index: -1;
} }
.service-card:hover { .service-card:hover {
background-color: #1e2021; border-color: var(--kl-border-strong);
border-color: var(--card-border-hover); background: linear-gradient(180deg, rgba(14, 24, 31, 0.78), rgba(9, 16, 21, 0.78));
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.04),
0 14px 34px rgba(0, 0, 0, 0.26),
0 0 0 1px rgba(0, 200, 160, 0.05),
0 0 18px rgba(0, 200, 160, 0.1);
transform: none;
} }
.service-card > :is(div, span, h1, h2, h3, h4, h5, h6, p):first-child:not(.service-container), .service-card a {
.bookmark, color: inherit;
.widget-container { text-decoration: none;
font-weight: 700;
text-transform: uppercase;
} }
.service-card > :first-child:not(.service-container), .service-header,
.service-card > .service-header, .service-title,
.service-card > .service-title { .card-title {
margin-bottom: 1px; color: var(--kl-text) !important;
font-family: var(--kl-font-ui);
font-weight: 700 !important;
letter-spacing: 0.03em;
} }
.service-card > :nth-child(2):not(.service-header):not(.service-title), .service-description,
.service-card > .service-body, .card-description,
.service-card > .service-content { .service-card p {
margin-top: 1px; color: var(--kl-text-dim) !important;
}
.service-card:has(iframe) .service-container {
margin-top: 0;
padding-top: 0;
}
.service-card a:-moz-any-link {
background-color: transparent;
}
.service-card :is(a:focus, a:hover, div[onclick]:hover) {
background-color: transparent;
outline: none;
} }
.service-icon img { .service-icon img {
width: 12px; width: 18px;
height: 12px; height: 18px;
vertical-align: middle; border-radius: 5px;
}
.service-container {
color: var(--kl-text-soft);
} }
.service-block { .service-block {
position: relative; position: relative;
border: 2px solid darkslategrey;
border-radius: var(--border-radius);
}
.service-block:has(iframe) {
border: none; border: none;
border-radius: var(--kl-radius);
background: transparent;
} }
.service-block:has(.epl), .service-card .status,
.service-block:has(.uptime), .service-status,
.service-block:has(.todo) { .card-status {
border-color: #191c1d; font-family: var(--kl-font-tech);
font-size: 0.8rem;
color: var(--kl-text-soft);
} }
/* ============================================================================ /* ============================================================================
INFORMATION WIDGETS IFRAMES / SPECIALS
============================================================================ */ ============================================================================ */
#information-widgets {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 12px;
}
#information-widgets-right {
margin-right: 10px;
}
.information-widget-greeting {
flex-grow: 1.5;
}
.information-widget-resources {
position: relative;
}
.information-widget-resource {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
margin: 0 auto;
text-align: center;
text-transform: uppercase;
font-weight: 700;
}
#information-widgets > * {
position: relative;
border: none;
border-radius: var(--border-radius);
backdrop-filter: none;
text-shadow: var(--text-shadow);
text-transform: uppercase;
font-weight: 700;
white-space: nowrap;
transition: var(--transition);
contain: layout;
will-change: transform;
content-visibility: auto;
contain-intrinsic-size: auto 200px;
backface-visibility: hidden;
transform: translateZ(0);
}
#information-widgets > *::before {
display: none;
}
#information-widgets > *:is(:hover, :focus) {
background-color: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
backdrop-filter: none !important;
}
/* ============================================================================
IFRAME CONTAINERS
============================================================================ */
iframe { iframe {
width: 100%; width: 100%;
border: 0; border: 0;
@@ -311,8 +304,9 @@ iframe {
} }
/* ============================================================================ /* ============================================================================
CUSTOM LISTS - OPTIMIZED SELECTORS CUSTOM LISTS
============================================================================ */ ============================================================================ */
#upcominggames .flex.flex-row.text-right, #upcominggames .flex.flex-row.text-right,
#tautulli_recent_movies .flex.flex-row.text-right, #tautulli_recent_movies .flex.flex-row.text-right,
#tautulli_recent_shows .flex.flex-row.text-right { #tautulli_recent_shows .flex.flex-row.text-right {
@@ -386,62 +380,14 @@ iframe {
margin: 3px 3px 1px; margin: 3px 3px 1px;
border-radius: 0.4rem; border-radius: 0.4rem;
text-align: left; text-align: left;
text-shadow: var(--text-shadow); text-shadow: none;
font-size: 12px; font-size: 12px;
font-weight: 400; font-weight: 400;
} }
#headlessui-disclosure-panel-\:r6\: > ul:first-child > li:first-child > div:first-child > div:first-child {
margin: -100px;
}
/* ============================================================================ /* ============================================================================
ANIMATIONS UTILITIES
============================================================================ */ ============================================================================ */
@keyframes updatePulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
@keyframes loadingBar {
0% {
width: 0;
opacity: 0.5;
}
50% {
opacity: 1;
}
100% {
width: 100%;
opacity: 0.5;
}
}
.service-card.updating {
animation: updatePulse 0.5s ease-in-out;
}
.service-card.loading::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: var(--card-border-hover);
animation: loadingBar 2s linear infinite;
}
/* ============================================================================
UTILITY CLASSES
============================================================================ */
.container {
max-width: 90%;
}
.flex { .flex {
display: flex; display: flex;
@@ -477,54 +423,73 @@ iframe {
} }
/* ============================================================================ /* ============================================================================
RESPONSIVE DESIGN ANIMATIONS
============================================================================ */ ============================================================================ */
@media (max-width: 768px) {
:root {
--card-padding: 10px;
--card-margin: 3px;
}
@keyframes updatePulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.74;
}
}
@keyframes loadingBar {
0% {
width: 0;
opacity: 0.45;
}
50% {
opacity: 1;
}
100% {
width: 100%;
opacity: 0.45;
}
}
.service-card.updating {
animation: updatePulse 0.5s ease-in-out;
}
.service-card.loading::after {
content: "";
position: absolute;
top: 0;
left: 0;
height: 2px;
background: linear-gradient(90deg, rgba(0, 200, 160, 0.2), rgba(74, 219, 196, 0.9));
animation: loadingBar 2s linear infinite;
}
/* ============================================================================
RESPONSIVE
============================================================================ */
@media (max-width: 768px) {
#myTab { #myTab {
flex-direction: column; flex-direction: column;
gap: 5px; gap: 8px;
margin: 0; margin: 0 12px 18px;
padding: 0;
} }
[id$="-tab"] { [id$="-tab"] {
width: 100%; width: 100%;
padding: 15px; min-width: 0;
text-align: center;
justify-content: center;
align-items: center;
} }
.tabcontent { .container {
margin: 0; max-width: 100%;
padding: 0;
} }
.service-card, .service-card,
[id$="-tab"] { [id$="-tab"] {
font-size: 14px; font-size: 14px;
} }
.hide-on-mobile {
display: none !important;
height: 0 !important;
min-height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
} }
@media (max-width: 480px) { @media (max-width: 480px) {
#myTab {
gap: 3px;
}
.service-card, .service-card,
[id$="-tab"] { [id$="-tab"] {
font-size: 12px; font-size: 12px;
@@ -546,23 +511,90 @@ iframe {
transition-duration: 0.01ms !important; transition-duration: 0.01ms !important;
} }
} }
/* ============================================================================ /* ============================================================================
BOOKMARKS — gleicher Look wie Service Cards BOOKMARKS — FINAL MATCH TO MAIN CARDS
Homepage rendert Bookmarks als <a> Tags mit Tailwind-Klassen
============================================================================ */ ============================================================================ */
.service-block a[href],
#bookmarks a[href] { /* Haupt-Balken */
border: var(--card-border); #bookmarks a[href],
border-radius: var(--border-radius); .service-block a[href] {
background-color: var(--bg-color-light); position: relative;
text-shadow: var(--text-shadow);
transition: var(--transition); border: 1px solid var(--kl-border) !important;
border-radius: 14px;
background: linear-gradient(
180deg,
rgba(12, 20, 26, 0.72),
rgba(8, 14, 18, 0.72)
) !important;
backdrop-filter: blur(6px);
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.03),
var(--kl-shadow);
transition:
border-color var(--kl-transition),
background var(--kl-transition),
box-shadow var(--kl-transition);
overflow: hidden;
} }
.service-block a[href]:hover, /* leichter Glow Layer wie bei Cards */
#bookmarks a[href]:hover { #bookmarks a[href]::before,
background-color: var(--bg-color); .service-block a[href]::before {
border-color: var(--card-border-hover); content: "";
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); position: absolute;
inset: 0;
background: linear-gradient(
135deg,
rgba(74, 219, 196, 0.04),
transparent 40%
);
pointer-events: none;
}
/* Hover exakt wie Main Cards */
#bookmarks a[href]:hover,
.service-block a[href]:hover {
border-color: var(--kl-border-strong) !important;
background: linear-gradient(
180deg,
rgba(14, 24, 31, 0.78),
rgba(9, 16, 21, 0.78)
) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.04),
0 14px 34px rgba(0, 0, 0, 0.26),
0 0 0 1px rgba(0, 200, 160, 0.05),
0 0 18px rgba(0, 200, 160, 0.1);
}
/* Text sauber wie oben */
#bookmarks a[href] * {
color: var(--kl-text-soft) !important;
font-weight: 500;
}
#bookmarks a[href]:hover * {
color: var(--kl-text) !important;
}
/* Icons etwas cleaner */
#bookmarks img {
border-radius: 6px;
opacity: 0.9;
}
/* leichte Separation zwischen Reihen */
#bookmarks .flex,
#bookmarks .grid {
gap: 10px;
} }
+133 -105
View File
@@ -1,22 +1,23 @@
/* ============================================================================ /* ============================================================================
KalliLab — CUSTOM JAVASCRIPT KalliLab — CUSTOM JAVASCRIPT
Basiert auf LionCityGaming/homepage custom.js (MIT License) Home / Media Tabs
Anpassungen: KalliLab Tab-Namen, keine HA/EPL/Glance iFrames Reload-Button entfernen
Aktiven Tab merken
============================================================================ */ ============================================================================ */
const CONFIG = { const CONFIG = {
STORAGE: { STORAGE: {
KEY: "lastFocusedTabId", KEY: "kallilab-last-focused-tab-id",
}, },
TIMING: { TIMING: {
RETRY_DELAY: 500, RETRY_DELAY: 500,
STANDARD_REFRESH: 1800000, // 30 Minuten STANDARD_REFRESH: 1800000,
QUICK_REFRESH: 60000, // 1 Minute QUICK_REFRESH: 60000,
RETRY_ON_ERROR: 30000, RETRY_ON_ERROR: 30000,
BATCH_DELAY: 100, BATCH_DELAY: 100,
}, },
SERVICES: { SERVICES: {
QUICK_REFRESH: [], // keine kritischen Quick-Refresh Widgets QUICK_REFRESH: [],
}, },
}; };
@@ -28,17 +29,10 @@ const RELOAD_BUTTON_SELECTORS = [
'[role="button"][aria-label="Reload"]', '[role="button"][aria-label="Reload"]',
]; ];
// Keine iFrame-Widgets in KalliLab const TAB_HASH_MAP = {
const IFRAME_CONFIG = []; "#home": "Home-tab",
"#media": "Media-tab",
// Tab-Mapping für KalliLab Tabs (5 Tabs) "": "Home-tab",
const TAB_MAPPING = {
"#ueberblick": ["#Überblick-tab", "#Überblick"],
"#system": ["#System-tab", "#System"],
"#sicherheit": ["#Sicherheit-tab", "#Sicherheit"],
"#dienste": ["#Dienste-tab", "#Dienste"],
"#backends": ["#Backends-tab", "#Backends"],
"": ["#Überblick-tab", "#Überblick"],
}; };
const state = { const state = {
@@ -46,7 +40,6 @@ const state = {
currentFocusedTab: null, currentFocusedTab: null,
observers: { observers: {
reloadButton: null, reloadButton: null,
resize: null,
}, },
}; };
@@ -105,7 +98,9 @@ function throttle(func, limit) {
if (!inThrottle) { if (!inThrottle) {
func.apply(this, args); func.apply(this, args);
inThrottle = true; inThrottle = true;
setTimeout(() => (inThrottle = false), limit); setTimeout(() => {
inThrottle = false;
}, limit);
} }
}; };
} }
@@ -141,6 +136,81 @@ function setupReloadButtonObserver() {
return observer; return observer;
} }
function setTabFocus(tab) {
requestAnimationFrame(() => {
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.remove("tab-focused");
}
state.currentFocusedTab = tab;
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.add("tab-focused");
}
});
}
function showTabContent(contentElement) {
if (!contentElement || !domCache.tabContents) return;
domCache.tabContents.forEach((content) => {
content.classList.remove("active");
content.style.display = "none";
});
contentElement.classList.add("active");
contentElement.style.display = "block";
domCache.updateActiveTab();
}
function resolveTabContentElement(tab) {
if (!tab) return null;
const ariaControls = tab.getAttribute("aria-controls");
if (ariaControls) {
return document.getElementById(ariaControls);
}
const tabText = (tab.textContent || "").trim().toLowerCase();
const directMatch = Array.from(document.querySelectorAll(".tabcontent")).find((content) => {
return content.id.toLowerCase() === tabText;
});
return directMatch || document.querySelector(".tabcontent.active");
}
function activateTab(tab, updateHash = true) {
if (!tab) return;
setTabFocus(tab);
storage.save(tab.id);
const contentToShow = resolveTabContentElement(tab);
showTabContent(contentToShow);
if (updateHash) {
const normalized = (tab.textContent || "").trim().toLowerCase();
if (normalized === "home") {
history.replaceState(null, "", "#home");
} else if (normalized === "media") {
history.replaceState(null, "", "#media");
}
}
}
function handleTabFocusFromURL() {
const hash = window.location.hash.toLowerCase();
const tabId = TAB_HASH_MAP[hash] || TAB_HASH_MAP[""];
const tabToFocus = document.getElementById(tabId);
if (tabToFocus) {
activateTab(tabToFocus, false);
return true;
}
return false;
}
function updateServiceCard(card, data) { function updateServiceCard(card, data) {
requestAnimationFrame(() => { requestAnimationFrame(() => {
const titleElement = card.querySelector(".card-title"); const titleElement = card.querySelector(".card-title");
@@ -154,6 +224,7 @@ function updateServiceCard(card, data) {
statusElement.textContent = Array.isArray(data) statusElement.textContent = Array.isArray(data)
? `${data.length} items` ? `${data.length} items`
: (data.status ?? (typeof data === "object" ? "Data received" : "")); : (data.status ?? (typeof data === "object" ? "Data received" : ""));
statusElement.style.color = "";
} }
}); });
} }
@@ -165,7 +236,7 @@ function updateServiceCardError(card, error) {
statusElement.textContent = error.message.includes("404") statusElement.textContent = error.message.includes("404")
? "Service unavailable" ? "Service unavailable"
: "Error loading data"; : "Error loading data";
statusElement.style.color = "red"; statusElement.style.color = "#ff7b7b";
} }
}); });
} }
@@ -221,53 +292,14 @@ async function batchUpdateServiceCards(cards) {
} }
} }
function handleTabFocusFromURL() {
const hash = window.location.hash.toLowerCase();
const mapping = TAB_MAPPING[hash] || TAB_MAPPING[""];
const [tabSelector, contentSelector] = mapping;
const tabToFocus = document.querySelector(tabSelector);
const contentToShow = document.querySelector(contentSelector);
if (tabToFocus) {
setTabFocus(tabToFocus);
storage.save(tabToFocus.id);
domCache.tabContents.forEach((content) => {
content.classList.remove("active");
content.style.display = "none";
});
if (contentToShow) {
showTabContent(contentToShow);
}
}
}
function setTabFocus(tab) {
requestAnimationFrame(() => {
if (state.currentFocusedTab) {
state.currentFocusedTab.classList.remove("tab-focused");
}
state.currentFocusedTab = tab;
state.currentFocusedTab.classList.add("tab-focused");
});
}
function showTabContent(contentElement) {
if (!contentElement) return;
contentElement.classList.add("active");
contentElement.style.display = "block";
domCache.updateActiveTab();
}
async function preloadAllTabs() { async function preloadAllTabs() {
const tabContents = document.querySelectorAll(".tab-pane"); const tabContents = document.querySelectorAll(".tabcontent, .tab-pane");
const serviceCards = new Set(); const serviceCards = new Set();
tabContents.forEach((tab) => { tabContents.forEach((tab) => {
tab.querySelectorAll(".service-card").forEach((card) => serviceCards.add(card)); tab.querySelectorAll(".service-card[data-api-endpoint]").forEach((card) => {
serviceCards.add(card);
});
}); });
await batchUpdateServiceCards(Array.from(serviceCards)); await batchUpdateServiceCards(Array.from(serviceCards));
@@ -295,49 +327,45 @@ function setupPeriodicRefresh() {
}); });
domCache.myTab?.addEventListener("click", (event) => { domCache.myTab?.addEventListener("click", (event) => {
if (event.target.matches('[id$="-tab"]')) { const tab = event.target.closest('[id$="-tab"]');
if (tab) {
activateTab(tab);
debouncedRefresh(); debouncedRefresh();
} }
}); });
} }
function initializeTabFocus() { function initializeTabFocus() {
// KalliLab Tab-Selektoren const tabs = document.querySelectorAll('#myTab [id$="-tab"]');
const tabs = document.querySelectorAll( if (!tabs.length) return;
"#Überblick-tab, #System-tab, #Sicherheit-tab, #Dienste-tab, #Backends-tab",
);
handleTabFocusFromURL(); const handledByHash = handleTabFocusFromURL();
if (!window.location.hash) { if (!handledByHash) {
const savedTabId = storage.get(); const savedTabId = storage.get();
const savedTab = savedTabId && document.getElementById(savedTabId); const savedTab = savedTabId && document.getElementById(savedTabId);
if (savedTab) { if (savedTab) {
setTabFocus(savedTab); activateTab(savedTab, false);
} else { } else {
const activeTab = document.querySelector(".tabcontent.active"); const activeTab = document.querySelector('#myTab [aria-selected="true"]');
const correspondingTab = if (activeTab) {
activeTab && document.querySelector(`[aria-controls="${activeTab.id}"]`); activateTab(activeTab, false);
if (correspondingTab) { } else {
setTabFocus(correspondingTab); activateTab(tabs[0], false);
} }
} }
} }
tabs.forEach((tab) => { tabs.forEach((tab) => {
const handleTabAction = function () { tab.addEventListener("click", () => {
setTabFocus(this); activateTab(tab);
storage.save(this.id); });
};
tab.addEventListener("click", handleTabAction); tab.addEventListener("keydown", (e) => {
tab.addEventListener("keydown", function (e) {
if (e.key === "Enter" || e.key === " ") { if (e.key === "Enter" || e.key === " ") {
e.preventDefault(); e.preventDefault();
this.click(); activateTab(tab);
handleTabAction.call(this);
} }
}); });
}); });
@@ -347,6 +375,10 @@ function initializeTabFocus() {
storage.save(state.currentFocusedTab.id); storage.save(state.currentFocusedTab.id);
} }
}); });
window.addEventListener("hashchange", () => {
handleTabFocusFromURL();
});
} }
function initializeEverything() { function initializeEverything() {
@@ -368,14 +400,21 @@ function initializeEverything() {
} else { } else {
setTimeout(initializeEverything, CONFIG.TIMING.RETRY_DELAY); setTimeout(initializeEverything, CONFIG.TIMING.RETRY_DELAY);
} }
}
window.addEventListener("orientationchange", () => { function cleanup() {
setTimeout(removeReloadButton, 100); if (state.observers.reloadButton) {
}); state.observers.reloadButton.disconnect();
state.observers.reloadButton = null;
}
domCache.clear();
state.currentFocusedTab = null;
} }
document.addEventListener("DOMContentLoaded", removeReloadButton); document.addEventListener("DOMContentLoaded", removeReloadButton);
window.addEventListener("load", initializeEverything); window.addEventListener("load", initializeEverything);
window.addEventListener("unload", cleanup);
if (typeof window.htmlLoaded === "function") { if (typeof window.htmlLoaded === "function") {
const originalHtmlLoaded = window.htmlLoaded; const originalHtmlLoaded = window.htmlLoaded;
@@ -386,22 +425,11 @@ if (typeof window.htmlLoaded === "function") {
} }
if ("ontouchstart" in window) { if ("ontouchstart" in window) {
window.addEventListener("touchend", () => { window.addEventListener(
"touchend",
() => {
setTimeout(removeReloadButton, 100); setTimeout(removeReloadButton, 100);
}, { passive: true }); },
{ passive: true },
);
} }
function cleanup() {
if (state.observers.reloadButton) {
state.observers.reloadButton.disconnect();
state.observers.reloadButton = null;
}
if (state.observers.resize) {
state.observers.resize.disconnect();
state.observers.resize = null;
}
domCache.clear();
state.currentFocusedTab = null;
}
window.addEventListener("unload", cleanup);
+12277
View File
File diff suppressed because it is too large Load Diff
+9 -15
View File
@@ -176,7 +176,6 @@
description: Fotos & Videos description: Fotos & Videos
server: my-docker server: my-docker
container: immich_server container: immich_server
siteMonitor: http://192.168.178.58:2283
widget: widget:
type: immich type: immich
url: http://192.168.178.58:2283 url: http://192.168.178.58:2283
@@ -246,7 +245,6 @@
description: Startseite description: Startseite
server: my-docker server: my-docker
container: homepage container: homepage
siteMonitor: https://home.kaleschke.info
- Dozzle: - Dozzle:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/dozzle.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/dozzle.webp
@@ -254,7 +252,6 @@
description: Container-Logs description: Container-Logs
server: my-docker server: my-docker
container: Dozzle container: Dozzle
siteMonitor: http://192.168.178.58:9888
- Tailscale: - Tailscale:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tailscale.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tailscale.webp
@@ -269,7 +266,6 @@
description: UI-Themes description: UI-Themes
server: my-docker server: my-docker
container: theme-park container: theme-park
siteMonitor: http://192.168.178.58:8009
- DDNS Updater: - DDNS Updater:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ddns-updater.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ddns-updater.webp
@@ -289,15 +285,13 @@
description: Server-Dashboard description: Server-Dashboard
server: my-docker server: my-docker
container: dashdot container: dashdot
siteMonitor: http://192.168.178.58:3002
- ntopng: - ntopng:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ntopng.webp icon: mdi-chart-line
href: http://kallilabcore.local:3000 href: http://kallilabcore.local:3000
description: Netzwerktraffic description: Netzwerktraffic
server: my-docker server: my-docker
container: ntopng container: ntopng
siteMonitor: http://192.168.178.58:3000
- NetAlertX: - NetAlertX:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/netalertx.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/netalertx.webp
@@ -307,12 +301,11 @@
container: netalertx container: netalertx
- Code Server: - Code Server:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/code-server.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/visual-studio-code.webp
href: https://code.kaleschke.info href: https://code.kaleschke.info
description: Browser-IDE description: Browser-IDE
server: my-docker server: my-docker
container: code-server container: code-server
siteMonitor: https://code.kaleschke.info
- Paperless-AI: - Paperless-AI:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp
@@ -320,7 +313,6 @@
description: KI-Dokumente description: KI-Dokumente
server: my-docker server: my-docker
container: Paperless-AI container: Paperless-AI
siteMonitor: http://192.168.178.58:3236
- Vaultwarden: - Vaultwarden:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/vaultwarden.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/vaultwarden.webp
@@ -328,7 +320,6 @@
description: Passwortmanager description: Passwortmanager
server: my-docker server: my-docker
container: vaultwarden container: vaultwarden
siteMonitor: https://kallilabcore.taild9fcf2.ts.net:4743
- Unbound: - Unbound:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/unbound.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/unbound.webp
@@ -342,7 +333,6 @@
description: Mail-Archiv description: Mail-Archiv
server: my-docker server: my-docker
container: mail-archiver container: mail-archiver
siteMonitor: http://192.168.178.58:5000
- Scanopy: - Scanopy:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp
@@ -350,15 +340,13 @@
description: Scan-Workflow description: Scan-Workflow
server: my-docker server: my-docker
container: scanopy-server container: scanopy-server
siteMonitor: http://192.168.178.58:60072
- luckyBackup: - luckyBackup:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/luckybackup.webp icon: mdi-backup-restore
href: http://kallilabcore.local:7675/vnc.html?autoconnect=true href: http://kallilabcore.local:7675/vnc.html?autoconnect=true
description: Backup-GUI description: Backup-GUI
server: my-docker server: my-docker
container: luckyBackup container: luckyBackup
siteMonitor: http://192.168.178.58:7675
- Scanopy Daemon: - Scanopy Daemon:
icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp icon: https://cdn.jsdelivr.net/gh/selfhst/icons/webp/paperless-ngx.webp
@@ -366,6 +354,12 @@
server: my-docker server: my-docker
container: scanopy-daemon container: scanopy-daemon
- Filebrowser:
icon: mdi-folder-open
href: http://192.168.178.58:8085
description: Filesystem GUI
container: filebrowser
# ============================================================================ # ============================================================================
# TAB: Home — BACKENDS MINI # TAB: Home — BACKENDS MINI
# ============================================================================ # ============================================================================
+6 -6
View File
@@ -5,14 +5,14 @@ disableCollapse: false
background: background:
image: /images/back10.jpg image: /images/back10.jpg
saturate: 1 saturate: 0.95
brightness: 100 brightness: 78
opacity: 100 opacity: 100
cardBlur: 4xl cardBlur: xl
theme: dark theme: dark
color: emerald color: emerald
headerStyle: underlined headerStyle: clean
iconStyle: theme iconStyle: theme
statusStyle: dot statusStyle: dot
@@ -23,7 +23,7 @@ hideVersion: true
hideErrors: false hideErrors: false
fiveColumns: false fiveColumns: false
useEqualHeights: true useEqualHeights: true
preloadAllWidgets: true preloadAllWidgets: false
target: _blank target: _blank
language: de language: de
@@ -52,7 +52,7 @@ layout:
header: false header: false
tab: Home tab: Home
style: row style: row
columns: 6 columns: 5
BACKENDS MINI: BACKENDS MINI:
header: false header: false
+11 -36
View File
@@ -1,49 +1,24 @@
- logo:
icon: /images/icons/logo.png
href: https://home.kaleschke.info
target: _blank
- greeting: - greeting:
text_size: 3xl text_size: 2xl
text: KalliLab text: KALLILAB CORE
href: https://home.kaleschke.info href: https://home.kaleschke.info
target: _blank target: _blank
- datetime:
locale: de-DE
text_size: 3xl
format:
dateStyle: full
hour12: false
timeStyle: short
# Jede Metrik = eigener Block = alle nebeneinander
- resources:
cpu: true
- resources:
memory: true
- resources:
cputemp: true
units: metric
- resources:
uptime: true
- resources:
label: KalliLab
disk: /
href: http://kallilabcore.local:61208
target: _blank
- search: - search:
provider: google provider: google
focus: false focus: false
target: _blank target: _blank
- datetime:
locale: de-DE
text_size: xl
format:
dateStyle: full
hour12: false
timeStyle: short
- openmeteo: - openmeteo:
label: KalliLab label: Horstmar
latitude: 52.1497 latitude: 52.1497
longitude: 7.5205 longitude: 7.5205
units: metric units: metric