⛅ Sync 2026-07-08 08:32:44

This commit is contained in:
github-actions[bot] committed 2026-07-08 08:32:44 +08:00
1 parent ff96781576
commit 6dda8a8707
10 files changed
+123 -26

No files matched your search

+61 -11
View File
@@ -193,6 +193,14 @@ header {
.brand {
@apply hover:text-brand text-text inline-block shrink-0 text-xl font-semibold tracking-tight no-underline transition-[color,transform] duration-150 hover:-translate-y-0.5 max-md:flex-1 max-md:text-lg;
[data-nav-type="sidebar"] & {
@apply md:hidden;
}
[data-nav-type="sidebar"].sidebar-collapsed & {
@apply md:inline-block md:transition-opacity md:duration-150 md:starting:opacity-0;
}
}
.nav {
@@ -255,6 +263,14 @@ header {
@apply md:flex;
}
/* D1 底线呼吸:展开态沿用短底线汉堡;折叠后底线伸满(三线齐整 =
侧栏收纳完毕)。dasharray 只改可见长度,线帽形状不受影响。 */
[data-nav-type="sidebar"].sidebar-collapsed
&
.navigation-toggle-line-bottom {
@apply md:[stroke-dasharray:15_15];
}
& .navigation-toggle {
@apply cursor-pointer rounded-none border-0 bg-transparent p-0 shadow-none transition-transform duration-150 hover:scale-105 active:scale-95;
@@ -264,7 +280,15 @@ header {
& .navigation-toggle-line {
transform-box: fill-box;
@apply origin-center transition-[transform,opacity] duration-150 ease-in-out;
@apply origin-center transition-[transform,opacity,stroke-dasharray] duration-150 ease-in-out;
}
/* Tabler menu-2 geometry: all three lines are full-length equals.
The signature short bottom line is a stroke-dasharray crop, not a
shorter segment or a scaleX — scaling distorts the round caps and
makes the "equal" collapsed state read as uneven lengths. */
& .navigation-toggle-line-bottom {
@apply [stroke-dasharray:7.5_15];
}
&.is-expanded {
@@ -278,7 +302,8 @@ header {
}
& .navigation-toggle-line-bottom {
transform: translate(3.75px, -5px) rotate(-45deg) scaleX(2);
transform: translateY(-5px) rotate(-45deg);
@apply [stroke-dasharray:15_15];
}
}
}
@@ -316,8 +341,10 @@ header {
}
body[data-nav-type="sidebar"] {
/* Desktop header + sidebar form one flush, L-shaped application frame: a
full-width header on top, a sidebar pinned to the left edge below it.
/* T4 shell: the sidebar is a full-height first column (brand → menu →
logout in one run), and the header spans only the content column — so
the sidebar's right border runs viewport top to bottom and the header
reads as the content area's toolbar, not a page-wide bar.
A CSS grid (not flex-wrap) keeps the header row at its natural height —
so collapsing the sidebar never stretches it — lets the sidebar column
animate to zero, and lets #maincontent fluidly reclaim the freed space.
@@ -329,11 +356,11 @@ body[data-nav-type="sidebar"] {
}
& > header {
@apply md:border-hairline md:col-span-full md:row-start-1 md:mb-0 md:border-b;
@apply md:border-hairline md:col-start-2 md:row-start-1 md:mb-0 md:border-b;
}
& > .sidebar-panel {
@apply md:col-start-1 md:row-start-2;
@apply md:col-start-1 md:row-span-2 md:row-start-1;
}
& > #maincontent {
@@ -341,7 +368,10 @@ body[data-nav-type="sidebar"] {
}
& .header-crumb {
@apply text-text-muted my-0 mr-auto ml-6 hidden min-w-0 list-none items-center gap-2 overflow-hidden p-0 text-sm md:flex;
/* Expanded: the crumb is the header's leftmost element (brand lives in
the sidebar head), so it sits on the header's own padding. Collapsed:
burger + brand reappear before it and it takes back its old offset. */
@apply text-text-muted my-0 mr-auto ml-0 hidden min-w-0 list-none items-center gap-2 overflow-hidden p-0 text-sm md:flex;
& li {
@apply m-0 list-none whitespace-nowrap;
@@ -356,8 +386,12 @@ body[data-nav-type="sidebar"] {
}
}
&.sidebar-collapsed .header-crumb {
@apply md:ml-6;
}
& .sidebar-panel {
@apply border-hairline sticky top-14 hidden h-[calc(100vh-3.5rem)] w-full overflow-hidden rounded-none border-r transition-[visibility] duration-[250ms] md:block;
@apply border-hairline sticky top-0 hidden h-dvh w-full overflow-hidden rounded-none border-r transition-[visibility] duration-[250ms] md:block;
}
/* Visibility flips at the end of the slide so collapsed controls leave the
@@ -374,6 +408,21 @@ body[data-nav-type="sidebar"] {
@apply -translate-x-full opacity-0;
}
/* Sidebar head: logo + hostname. h-14 matches the header row so its
bottom hairline continues the header's border in one unbroken line
across the viewport. */
& .sidebar-head {
@apply border-hairline flex h-14 shrink-0 items-center gap-2.5 border-b px-4;
& .sidebar-logo {
@apply size-5 shrink-0 rounded-md;
}
& .sidebar-brand {
@apply text-text hover:text-brand min-w-0 flex-1 truncate text-lg font-semibold tracking-tight no-underline transition-colors duration-150;
}
}
& .sidebar-list {
@apply m-0 flex-1 list-none space-y-0.5 overflow-y-auto p-3;
@@ -397,10 +446,11 @@ body[data-nav-type="sidebar"] {
& .sidebar-footer {
@apply border-hairline shrink-0 border-t p-3;
/* Logout: muted at rest; on hover the base .nav-link pill background
plus error-colored text is the intended compound state (matches v1). */
/* Logout: muted at rest, standard row treatment on hover — same
vocabulary as every other sidebar row (the v1 danger-red hover read
as unrelated to the rest of the menu). */
& .nav-link {
@apply text-text-muted hover:text-danger;
@apply text-text-muted hover:text-text;
}
}
}
@@ -8,7 +8,10 @@
*/
.nav-link {
@apply text-text hover:bg-hover-faint block rounded-xl px-3 py-1.5 no-underline transition-all duration-150;
/* hover:no-underline: the global `a:hover { underline }` in _elements.css
outranks a bare class (a:hover is 0,1,1), so nav rows must opt out at
the same specificity tier. */
@apply text-text hover:bg-hover-faint block rounded-xl px-3 py-1.5 no-underline transition-all duration-150 hover:no-underline;
}
.navigation-direct {
@@ -1,8 +1,11 @@
.mobile-menu-overlay {
@apply max-md:bg-mega-menu-bg max-md:invisible max-md:fixed max-md:inset-0 max-md:z-60 max-md:opacity-0 max-md:backdrop-blur-lg max-md:backdrop-saturate-150 max-md:transition-[visibility,opacity] max-md:duration-[250ms] md:hidden;
@apply max-md:bg-mega-menu-bg max-md:invisible max-md:fixed max-md:inset-0 max-md:z-60 max-md:opacity-0 max-md:backdrop-blur-lg max-md:backdrop-saturate-150 md:hidden;
/* Transitions live on the OPEN state only: opening fades in, closing cuts
instantly. Link taps close into a full-page navigation, and any close
fade racing the document swap freezes mid-frame as a ghost. */
&.mobile-menu-open {
@apply max-md:visible max-md:opacity-100;
@apply max-md:visible max-md:opacity-100 max-md:transition-[visibility,opacity] max-md:duration-[250ms];
}
.mobile-nav {
@@ -17,7 +20,7 @@
}
& .mobile-nav-list {
@apply max-md:m-0 max-md:flex max-md:min-h-0 max-md:flex-1 max-md:-translate-y-2 max-md:list-none max-md:flex-col max-md:overflow-y-auto max-md:overscroll-contain max-md:p-0 max-md:pb-5 max-md:opacity-0 max-md:transition-[translate,opacity] max-md:duration-[250ms] max-md:ease-out;
@apply max-md:m-0 max-md:flex max-md:min-h-0 max-md:flex-1 max-md:-translate-y-2 max-md:list-none max-md:flex-col max-md:overflow-y-auto max-md:overscroll-contain max-md:p-0 max-md:pb-5 max-md:opacity-0;
& .mobile-nav-item {
@apply max-md:shrink-0;
@@ -60,7 +63,7 @@
}
&.mobile-menu-open .mobile-nav-list {
@apply max-md:translate-y-0 max-md:opacity-100;
@apply max-md:translate-y-0 max-md:opacity-100 max-md:transition-[translate,opacity] max-md:duration-[250ms] max-md:ease-out;
}
}
@@ -469,7 +469,10 @@ return baseclass.extend({
items.forEach((item) => {
if (item.isActiveGroup || item.isActivePage) {
crumb.push(item.title);
if (item.activePage) crumb.push(item.activePage.title);
// Same-named group/page pairs ("System › System") collapse to one
// level — the duplicate adds no information.
if (item.activePage && item.activePage.title !== item.title)
crumb.push(item.activePage.title);
}
if (item.isLogout) {
@@ -485,7 +488,7 @@ return baseclass.extend({
this.bindNavigationAccordion(list);
crumb.forEach((title, i) => {
if (i) crumbEl?.appendChild(E("li", { class: "crumb-sep" }, ["/"]));
if (i) crumbEl?.appendChild(E("li", { class: "crumb-sep" }, ["›"]));
crumbEl?.appendChild(
E("li", { class: i === crumb.length - 1 ? "current" : "" }, [title]),
);
@@ -729,13 +729,47 @@ test("renders sidebar items, logout, and translated crumbs without duplication",
assert.equal(crumb.children.length, 3);
assert.deepEqual(
crumb.children.map((child) => textContent(child)),
["translated:Network", "/", "translated:Wireless"],
["translated:Network", "›", "translated:Wireless"],
);
assert.equal(crumb.children[2].getAttribute("class"), "current");
assert.equal(list.dataset.accordionBound, "true");
assert.equal(list.listenerCount("click"), 1);
});
test("collapses same-named group/page crumbs to a single level", () => {
const crumb = new FakeElement("ol", {}, [new FakeElement("li")]);
const document = createFakeDocument({
elements: {
"#header-crumb": crumb,
"#sidebar-list": new FakeElement("ul"),
},
navType: "sidebar",
});
const menu = loadMenuModule({
dispatchpath: ["admin", "system", "system"],
document,
translate: (value) => `translated:${value}`,
});
const tree = {
children: {
system: {
title: "System",
children: {
system: { title: "System" },
},
},
},
};
menu.renderSidebar(menu.buildNavigationModel(getMenuChildren(tree), "admin"));
assert.deepEqual(
crumb.children.map((child) => textContent(child)),
["translated:System"],
);
assert.equal(crumb.children[0].getAttribute("class"), "current");
});
test("renders an active group expanded when an open mobile list was initially empty", () => {
const list = new FakeElement("ul");
const overlay = new FakeElement("div", { class: "mobile-menu-open" }, [list]);
+2 -2
View File
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
LUCI_DEPENDS:=+luci-base
PKG_VERSION:=1.0.8
PKG_RELEASE:=44
PKG_VERSION:=1.0.10
PKG_RELEASE:=45
PKG_LICENSE:=Apache-2.0
LUCI_MINIFY_CSS:=
@@ -1,2 +1,2 @@
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--tw-scrollbar-thumb:#0000;--tw-scrollbar-track:#0000;--tw-leading:initial;--tw-tracking:initial;--tw-ease:initial;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-border-spacing-x:0;--tw-border-spacing-y:0}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-xs:20rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-familyLine truncated
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-content:"";--tw-animation-delay:0s;--tw-animation-direction:normal;--tw-animation-duration:initial;--tw-animation-fill-mode:none;--tw-animation-iteration-count:1;--tw-enter-blur:0;--tw-enter-opacity:1;--tw-enter-rotate:0;--tw-enter-scale:1;--tw-enter-translate-x:0;--tw-enter-translate-y:0;--tw-exit-blur:0;--tw-exit-opacity:1;--tw-exit-rotate:0;--tw-exit-scale:1;--tw-exit-translate-x:0;--tw-exit-translate-y:0;--tw-scrollbar-thumb:#0000;--tw-scrollbar-track:#0000;--tw-leading:initial;--tw-tracking:initial;--tw-ease:initial;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-border-spacing-x:0;--tw-border-spacing-y:0}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:var(--spacing);--container-xs:20rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .25);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:calc(var(--radius-base) * 2);--radius-3xl:calc(var(--radius-base) * 3);--radius-4xl:calc(var(--radius-base) * 4);--shadow-sm:var(--app-shadow-sm);--shadow-md:var(--app-shadow-md);--shadow-lg:var(--app-shadow-md);--shadow-xl:var(--app-shadow-lg);--shadow-2xl:var(--app-shadow-lg);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-md:12px;--blur-lg:16px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--container-max-width:var(--container-max-width)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-familyLine truncated
@@ -1 +1 @@
"use strict";"require baseclass";"require ui";return baseclass.extend({__init__(){ui.menu.load().then(tree=>this.render(tree));this.initNavigationControls();this.initUciIndicator()},initUciIndicator(){const original=ui.changes?.setIndicator;if(!original)return;ui.changes.setIndicator=function(n){original.call(this,n);document.querySelector('[data-indicator="uci-changes"]')?.setAttribute("data-count",n||0)}},initNavigationControls(){const overlay=document.querySelector("#mobile-menu-overlay");const navigationToggle=document.querySelector("#navigation-toggle");if(!navigationToggle||!overlay)return;const mobileList=overlay.querySelector("#mobile-nav-list");const desktop=window.matchMedia("(min-width: 768px)");const SIDEBAR_COLLAPSED_KEY="aurora.sidebarCollapsed";const isDesktopSidebar=()=>desktop.matches&&document.body.dataset.navType==="sidebar";const updateToggleState=expanded=>{const desktopSidebar=isDesktopSidebar();navigationToggle.classList.toggle("is-expanded",expanded&&!desktop.matches);navigationToggle.setAttribute("aria-expanded",expanded?"true":"false");navigationToggle.setAttribute("aria-controls",desktopSidebar?"sidebar-panel":"mobile-menu-overlay");const label=expanded&&!desktopSidebar?_("Close"):_("Navigation");navigationToggle.setAttribute("title",label);navigationToggle.setAttribute("aria-label",label)};const closeMobileNavigation=()=>{overlay.classList.remove("mobile-menu-open");document.body.classList.remove("mobile-navigation-open");document.body.style.overflow="";this.resetNavigationGroups(mobileList)};const getNavigationExpanded=()=>{if(isDesktopSidebar()){return!document.body.classList.contains("sidebar-collapsed")}return!desktop.matches&&overlay.classList.contains("mobile-menu-open")};const setNavigationExpanded=expanded=>{if(isDesktopSidebar()){closeMobileNavigation();const collapsed=!expanded;document.body.classList.toggle("sidebar-collapsed",collapsed);localStorage.setItem(SIDEBAR_COLLAPSED_KEY,collapsed);updateToggleState(expanded);return}if(desktop.matches){closeMobileNavigation();updateToggleState(false);return}overlay.classList.toggle("mobile-menu-open",expanded);document.body.classList.toggle("mobile-navigation-open",expanded);document.body.style.overflow=expanded?"hidden":"";if(expanded)this.expandActiveNavigationGroup(mobileList);else this.resetNavigationGroups(mobileList);updateToggleState(expanded)};const toggleNavigation=()=>setNavigationExpanded(!getNavigationExpanded());const syncNavigationState=()=>{closeMobileNavigation();if(isDesktopSidebar()){const collapsed=localStorage.getItem(SIDEBAR_COLLAPSED_KEY)==="true";document.body.classList.toggle("sidebar-collapsed",collapsed);updateToggleState(!collapsed);return}updateToggleState(false)};navigationToggle.addEventListener("click",e=>{e.stopPropagation();toggleNavigation()});overlay.addEventListener("click",e=>{if(e.target===overlay)setNavigationExpanded(false)});document.addEventListener("keydown",e=>{if(e.key==="Escape"&&overlay.classList.contains("mobile-menu-open")){setNavigationExpanded(false)}});desktop.addEventListener("change",syncNavigationState);syncNavigationState();document.addEventListener("click",e=>{const destination=e.target?.closest?.(".navigation-direct, .navigation-sublink, .mobile-nav-logout");if(destination&&overlay.contains(destination)){setNavigationExpanded(false)}})},renderMobileMenu(items){const list=document.querySelector("#mobile-nav-list");const footerAction=document.querySelector("#mobile-nav-footer-action");if(list)list.innerHTML="";if(footerAction)footerAction.innerHTML="";if(!list)return;items.forEach(item=>{if(item.isLogout){if(footerAction){footerAction.appendChild(E("a",{class:"mobile-nav-logout",href:item.href},[item.title]))}return}list.appendChild(this.renderNavigationItem(item,"mobile"))});this.bindNavigationAccordion(list)},render(tree){this.renderModeMenu(tree);if(L.env.dispatchpath.length>=3){let node=tree;let url="";for(let i=0;i<3&&node;i++){const segment=L.env.dispatchpath[i];node=node.children?.[segment];url+=(url?"/":"")+segment}if(node)this.renderTabMenu(node,url)}},renderTabMenu(tree,url,level=0){const container=document.querySelector("#tabmenu");const ul=E("ul",{class:"tabs"});const children=ui.menu.getChildren(tree);let activeNode=null;children.forEach(child=>{const isActive=L.env.dispatchpath[3+level]===child.name;ul.appendChild(E("li",{class:`tabmenu-item-${child.name}${isActive?" active":""}`},[E("a",{href:L.url(url,child.name)},[_(child.title)])]));if(isActive)activeNode=child});if(!ul.children.length)return E([]);container.appendChild(ul);container.style.display="";if(activeNode){this.renderTabMenu(activeNode,`${url}/${activeNode.name}`,level+1)}return ul},renderMainMenu(tree,url,level=0,navigationItems=null){const ul=level?E("ul",{class:"desktop-nav-list"}):document.querySelector("#topmenu");const children=ui.menu.getChildren(tree);if(level>1)return E([]);if(level===0){const navType=document.body?.dataset?.navType||"mega-menu";if(navType==="sidebar"){this.renderSidebLine truncated
"use strict";"require baseclass";"require ui";return baseclass.extend({__init__(){ui.menu.load().then(tree=>this.render(tree));this.initNavigationControls();this.initUciIndicator()},initUciIndicator(){const original=ui.changes?.setIndicator;if(!original)return;ui.changes.setIndicator=function(n){original.call(this,n);document.querySelector('[data-indicator="uci-changes"]')?.setAttribute("data-count",n||0)}},initNavigationControls(){const overlay=document.querySelector("#mobile-menu-overlay");const navigationToggle=document.querySelector("#navigation-toggle");if(!navigationToggle||!overlay)return;const mobileList=overlay.querySelector("#mobile-nav-list");const desktop=window.matchMedia("(min-width: 768px)");const SIDEBAR_COLLAPSED_KEY="aurora.sidebarCollapsed";const isDesktopSidebar=()=>desktop.matches&&document.body.dataset.navType==="sidebar";const updateToggleState=expanded=>{const desktopSidebar=isDesktopSidebar();navigationToggle.classList.toggle("is-expanded",expanded&&!desktop.matches);navigationToggle.setAttribute("aria-expanded",expanded?"true":"false");navigationToggle.setAttribute("aria-controls",desktopSidebar?"sidebar-panel":"mobile-menu-overlay");const label=expanded&&!desktopSidebar?_("Close"):_("Navigation");navigationToggle.setAttribute("title",label);navigationToggle.setAttribute("aria-label",label)};const closeMobileNavigation=()=>{overlay.classList.remove("mobile-menu-open");document.body.classList.remove("mobile-navigation-open");document.body.style.overflow="";this.resetNavigationGroups(mobileList)};const getNavigationExpanded=()=>{if(isDesktopSidebar()){return!document.body.classList.contains("sidebar-collapsed")}return!desktop.matches&&overlay.classList.contains("mobile-menu-open")};const setNavigationExpanded=expanded=>{if(isDesktopSidebar()){closeMobileNavigation();const collapsed=!expanded;document.body.classList.toggle("sidebar-collapsed",collapsed);localStorage.setItem(SIDEBAR_COLLAPSED_KEY,collapsed);updateToggleState(expanded);return}if(desktop.matches){closeMobileNavigation();updateToggleState(false);return}overlay.classList.toggle("mobile-menu-open",expanded);document.body.classList.toggle("mobile-navigation-open",expanded);document.body.style.overflow=expanded?"hidden":"";if(expanded)this.expandActiveNavigationGroup(mobileList);else this.resetNavigationGroups(mobileList);updateToggleState(expanded)};const toggleNavigation=()=>setNavigationExpanded(!getNavigationExpanded());const syncNavigationState=()=>{closeMobileNavigation();if(isDesktopSidebar()){const collapsed=localStorage.getItem(SIDEBAR_COLLAPSED_KEY)==="true";document.body.classList.toggle("sidebar-collapsed",collapsed);updateToggleState(!collapsed);return}updateToggleState(false)};navigationToggle.addEventListener("click",e=>{e.stopPropagation();toggleNavigation()});overlay.addEventListener("click",e=>{if(e.target===overlay)setNavigationExpanded(false)});document.addEventListener("keydown",e=>{if(e.key==="Escape"&&overlay.classList.contains("mobile-menu-open")){setNavigationExpanded(false)}});desktop.addEventListener("change",syncNavigationState);syncNavigationState();document.addEventListener("click",e=>{const destination=e.target?.closest?.(".navigation-direct, .navigation-sublink, .mobile-nav-logout");if(destination&&overlay.contains(destination)){setNavigationExpanded(false)}})},renderMobileMenu(items){const list=document.querySelector("#mobile-nav-list");const footerAction=document.querySelector("#mobile-nav-footer-action");if(list)list.innerHTML="";if(footerAction)footerAction.innerHTML="";if(!list)return;items.forEach(item=>{if(item.isLogout){if(footerAction){footerAction.appendChild(E("a",{class:"mobile-nav-logout",href:item.href},[item.title]))}return}list.appendChild(this.renderNavigationItem(item,"mobile"))});this.bindNavigationAccordion(list)},render(tree){this.renderModeMenu(tree);if(L.env.dispatchpath.length>=3){let node=tree;let url="";for(let i=0;i<3&&node;i++){const segment=L.env.dispatchpath[i];node=node.children?.[segment];url+=(url?"/":"")+segment}if(node)this.renderTabMenu(node,url)}},renderTabMenu(tree,url,level=0){const container=document.querySelector("#tabmenu");const ul=E("ul",{class:"tabs"});const children=ui.menu.getChildren(tree);let activeNode=null;children.forEach(child=>{const isActive=L.env.dispatchpath[3+level]===child.name;ul.appendChild(E("li",{class:`tabmenu-item-${child.name}${isActive?" active":""}`},[E("a",{href:L.url(url,child.name)},[_(child.title)])]));if(isActive)activeNode=child});if(!ul.children.length)return E([]);container.appendChild(ul);container.style.display="";if(activeNode){this.renderTabMenu(activeNode,`${url}/${activeNode.name}`,level+1)}return ul},renderMainMenu(tree,url,level=0,navigationItems=null){const ul=level?E("ul",{class:"desktop-nav-list"}):document.querySelector("#topmenu");const children=ui.menu.getChildren(tree);if(level>1)return E([]);if(level===0){const navType=document.body?.dataset?.navType||"mega-menu";if(navType==="sidebar"){this.renderSidebLine truncated
@@ -208,7 +208,7 @@
<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line class="navigation-toggle-line navigation-toggle-line-top" x1="2.5" y1="5" x2="17.5" y2="5"/>
<line class="navigation-toggle-line navigation-toggle-line-middle" x1="2.5" y1="10" x2="17.5" y2="10"/>
<line class="navigation-toggle-line navigation-toggle-line-bottom" x1="2.5" y1="15" x2="10" y2="15"/>
<line class="navigation-toggle-line navigation-toggle-line-bottom" x1="2.5" y1="15" x2="17.5" y2="15"/>
</svg>
</button>
</div>
@@ -261,6 +261,10 @@
{% if (nav_type == 'sidebar'): %}
<aside class="sidebar-panel" id="sidebar-panel" aria-label="{{ _('Navigation') }}">
<nav class="sidebar-panel-inner">
<div class="sidebar-head">
<img class="sidebar-logo" src="{{ media }}/images/{{ logo_svg }}?v={{ icon_cache_version }}" alt="" aria-hidden="true">
<a class="sidebar-brand" href="/">{{ hostname }}</a>
</div>
<ul class="sidebar-list" id="sidebar-list"></ul>
<div class="sidebar-footer" id="sidebar-footer"></div>
</nav>
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=qBittorrent
PKG_VERSION:=5.2.2
PKG_RELEASE:=3
PKG_VERSION:=5.2.3
PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/qbittorrent/qBittorrent/tar.gz/release-$(PKG_VERSION)?