mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-31 10:25:13 +08:00
🐶 Sync 2026-09-17 00:01:35
This commit is contained in:
1 parent
d0c748add2
commit
6c6ea07309
106 files changed
+7551
-5770
No files matched your search
@@ -14,4 +14,4 @@ Restored HTML loses inline JS handlers (`innerHTML` serialization), so anything
|
||||
|
||||
**Bump `v` whenever the sidebar markup changes shape.**
|
||||
|
||||
Cross-document `@view-transition` rules live in `components/_view-transitions.css` (only `#sidebar` gets its own snapshot group — the topbar must not, or its top-layer snapshot escapes `.content-card`'s rounded-corner clipping during transitions); they assume the cache keeps the sidebar's first frame populated.
|
||||
The theme captures no view transitions (`components/_view-transitions.css` sets `:root { view-transition-name: none }`); swapped and freshly loaded content fades in instead. On desktop, collapsing eases the rail's width (`_layout.css`) and `#main-wrapper`, a flex-1 sibling, follows it frame by frame: the content is pushed, never snapped and slid back. `sidebar-shadcn.js` sets `html[data-sidebar-anim]` on the first toggle so a page load never plays the transition.
|
||||
@@ -27,12 +27,12 @@
|
||||
"@types/node": "^25.9.1",
|
||||
"colorjs.io": "^0.6.1",
|
||||
"husky": "^9.1.7",
|
||||
"lightningcss": "^1.32.0",
|
||||
"lint-staged": "^16.4.0",
|
||||
"prettier": "^3.8.3",
|
||||
"prettier-plugin-tailwindcss": "^0.8.0",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"terser": "^5.47.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vite": "^8.0.13"
|
||||
},
|
||||
"packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c"
|
||||
|
||||
Generated
+3
-8
@@ -187,6 +187,9 @@ importers:
|
||||
husky:
|
||||
specifier: ^9.1.7
|
||||
version: 9.1.7
|
||||
lightningcss:
|
||||
specifier: ^1.32.0
|
||||
version: 1.32.0
|
||||
lint-staged:
|
||||
specifier: ^16.4.0
|
||||
version: 16.4.0
|
||||
@@ -202,9 +205,6 @@ importers:
|
||||
terser:
|
||||
specifier: ^5.47.1
|
||||
version: 5.47.1
|
||||
tw-animate-css:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
vite:
|
||||
specifier: ^8.0.13
|
||||
version: 8.0.13(@types/node@25.9.1)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0)
|
||||
@@ -801,9 +801,6 @@ packages:
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
tw-animate-css@1.4.0:
|
||||
resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
|
||||
|
||||
undici-types@7.24.6:
|
||||
resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==}
|
||||
|
||||
@@ -1300,8 +1297,6 @@ snapshots:
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
tw-animate-css@1.4.0: {}
|
||||
|
||||
undici-types@7.24.6: {}
|
||||
|
||||
vite@8.0.13(@types/node@25.9.1)(jiti@2.7.0)(terser@5.47.1)(yaml@2.9.0):
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* ── Sidebar ──────────────────────────────────────────────────────── */
|
||||
|
||||
#sidebar {
|
||||
/* Width only: `transition-all` would also ease background-color, making the
|
||||
sidebar lag a beat behind the untransitioned content area on theme flips. */
|
||||
@apply bg-sidebar-bg flex h-full w-[220px] shrink-0 flex-col overflow-hidden transition-[width] duration-200;
|
||||
@apply max-md:fixed max-md:top-0 max-md:left-0 max-md:z-40 max-md:w-[240px] max-md:-translate-x-full max-md:shadow-xl max-md:transition-transform;
|
||||
/* Mobile drawer: translate only; `transition-all` would also ease
|
||||
background-color and lag the sidebar behind the content on theme flips. */
|
||||
@apply bg-sidebar-bg flex h-full w-[220px] shrink-0 flex-col overflow-hidden;
|
||||
@apply max-md:fixed max-md:top-0 max-md:left-0 max-md:z-40 max-md:w-[240px] max-md:-translate-x-full max-md:shadow-xl max-md:[transition:translate_.2s];
|
||||
/* Drawer height tracks the dynamic viewport so its bottom edge meets the
|
||||
Safari toolbar smoothly instead of jumping when the toolbar shows/hides. */
|
||||
@apply max-md:h-dvh;
|
||||
@@ -54,7 +54,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
@apply text-sidebar-muted flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 transition-colors;
|
||||
@apply text-sidebar-muted flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-sidebar-accent text-sidebar-accent-fg;
|
||||
@@ -82,7 +82,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-nav-parent {
|
||||
@apply text-sidebar-muted flex w-full cursor-pointer appearance-none items-center justify-start gap-2.5 rounded-md border-0 bg-transparent px-2 py-1.5 text-left text-sm font-medium transition-colors duration-150;
|
||||
@apply text-sidebar-muted flex w-full cursor-pointer appearance-none items-center justify-start gap-2.5 rounded-md border-0 bg-transparent px-2 py-1.5 text-left text-sm font-medium [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-sidebar-accent text-sidebar-accent-fg;
|
||||
@@ -101,7 +101,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-chevron {
|
||||
@apply ml-auto shrink-0 transition-transform duration-200;
|
||||
@apply ml-auto shrink-0 [transition:rotate_.2s];
|
||||
svg,
|
||||
img {
|
||||
@apply h-3.5 w-3.5;
|
||||
@@ -132,7 +132,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
opening bounces every row below them. visibility transitions too, so the
|
||||
collapsed links leave the tab order only once the close has finished. */
|
||||
.sidebar-accordion-sub {
|
||||
@apply invisible grid grid-rows-[0fr] overflow-hidden opacity-0 transition-[grid-template-rows,opacity,visibility] duration-200 ease-out;
|
||||
@apply invisible grid grid-rows-[0fr] overflow-hidden opacity-0 [transition:grid-template-rows_.2s_var(--ease-out),opacity_.2s_var(--ease-out),visibility_.2s_var(--ease-out)];
|
||||
|
||||
> * {
|
||||
@apply min-h-0 self-start;
|
||||
@@ -156,7 +156,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-sub-link {
|
||||
@apply text-sidebar-muted block rounded-md px-2 py-1.5 text-sm no-underline transition-colors;
|
||||
@apply text-sidebar-muted block rounded-md px-2 py-1.5 text-sm no-underline [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-sidebar-accent text-sidebar-accent-fg;
|
||||
@@ -178,7 +178,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-collapsed-popover-link {
|
||||
@apply text-sidebar-muted mx-1 block rounded-sm px-2 py-1.5 text-sm no-underline transition-colors;
|
||||
@apply text-sidebar-muted mx-1 block rounded-sm px-2 py-1.5 text-sm no-underline [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-sidebar-accent text-sidebar-accent-fg;
|
||||
@@ -197,7 +197,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.sidebar-logout {
|
||||
@apply text-sidebar-muted flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm no-underline transition-colors;
|
||||
@apply text-sidebar-muted flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-sm no-underline [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-sidebar-accent text-sidebar-accent-fg;
|
||||
@@ -237,6 +237,12 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
@apply border-panel-border bg-panel-bg flex flex-1 flex-col overflow-hidden rounded-xl border shadow-sm max-md:rounded-none max-md:border-none max-md:shadow-none;
|
||||
}
|
||||
|
||||
/* The rail width eases and #main-wrapper (flex-1) follows it frame by frame.
|
||||
data-sidebar-anim is set on the first toggle, so a page load never plays it. */
|
||||
html[data-sidebar-anim] #sidebar {
|
||||
@apply md:[transition:width_200ms_var(--ease-out)];
|
||||
}
|
||||
|
||||
/* ── Card header ─────────────────────────────────────────────────── */
|
||||
|
||||
.card-header {
|
||||
@@ -251,7 +257,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
.topbar-hamburger {
|
||||
@apply text-muted-foreground hidden h-8 w-8 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 transition-colors max-md:flex;
|
||||
@apply text-muted-foreground hidden h-8 w-8 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 [transition:color_.15s,background-color_.15s,border-color_.15s] max-md:flex;
|
||||
|
||||
&:hover {
|
||||
@apply bg-muted;
|
||||
@@ -283,7 +289,7 @@ html[data-sidebar-collapsed="true"] #sidebar {
|
||||
}
|
||||
|
||||
button.topbar-icon-btn {
|
||||
@apply text-muted-foreground inline-flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 transition-colors;
|
||||
@apply text-muted-foreground inline-flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-0 [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
|
||||
&:hover {
|
||||
@apply bg-muted text-foreground;
|
||||
@@ -295,16 +301,20 @@ button.topbar-icon-btn {
|
||||
}
|
||||
|
||||
.topbar-indicators {
|
||||
@apply flex items-center gap-2;
|
||||
@apply flex items-center;
|
||||
|
||||
/* LuCI-owned spans: icon via ::before mask only (no JS / no img) */
|
||||
span[data-indicator] {
|
||||
> img {
|
||||
@apply hidden;
|
||||
}
|
||||
@apply text-muted-foreground relative inline-flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-[0px] transition-colors;
|
||||
@apply text-muted-foreground relative inline-flex h-8 w-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-[0px] [transition:color_.15s,background-color_.15s,border-color_.15s,width_.15s_ease-out,opacity_.15s_ease-out,margin-left_.15s_ease-out] starting:w-0 starting:opacity-0;
|
||||
@apply before:relative before:block before:size-4 before:shrink-0 before:bg-current before:[mask-size:cover] before:[mask-position:center] before:[mask-repeat:no-repeat];
|
||||
|
||||
& + span[data-indicator] {
|
||||
@apply ml-2 starting:ml-0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply bg-muted text-foreground;
|
||||
}
|
||||
@@ -335,7 +345,7 @@ button.topbar-icon-btn {
|
||||
/* ── Maincontent ──────────────────────────────────────────────────── */
|
||||
|
||||
#maincontent {
|
||||
@apply flex-1 overflow-y-auto p-6 max-md:p-4;
|
||||
@apply flex-1 [scrollbar-gutter:stable] overflow-y-auto p-6 max-md:p-4;
|
||||
|
||||
h1 {
|
||||
@apply mb-4 border-0 pl-0 text-2xl font-semibold tracking-tight;
|
||||
@@ -409,7 +419,7 @@ button.topbar-icon-btn {
|
||||
#sidebar-overlay {
|
||||
/* Absolute, not fixed: iOS Safari 26 tints its bars from edge-touching
|
||||
fixed elements' background-color, opacity notwithstanding. */
|
||||
@apply bg-overlay-base-a50 pointer-events-none absolute inset-0 z-30 opacity-0 transition-opacity duration-200 md:hidden;
|
||||
@apply bg-overlay-base-a50 pointer-events-none absolute inset-0 z-30 opacity-0 [transition:opacity_.2s] md:hidden;
|
||||
|
||||
&.active {
|
||||
@apply pointer-events-auto opacity-100;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Everything still transitions, for 1ms: luci-base acts on transitionend
|
||||
(dropdown focus, notification removal), which transition: none never fires. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
@apply [transition-delay:0s]! [transition-duration:1ms]! [animation-delay:0s]! [animation-duration:1ms]!;
|
||||
}
|
||||
|
||||
/* A loading spinner is state, not decoration. */
|
||||
.spinning::before {
|
||||
@apply [animation-duration:1s]!;
|
||||
}
|
||||
}
|
||||
@@ -48,12 +48,24 @@
|
||||
@apply whitespace-nowrap;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
@apply animate-in fade-in-0 fill-mode-backwards duration-400;
|
||||
@keyframes shadcn-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Also content that lands after first paint: the view's top-level blocks
|
||||
(full load and router swap; poll never re-inserts them) and a switched-in
|
||||
form tab pane (the dashboard's polled tabs sit under .Dashboard). */
|
||||
.fade-in,
|
||||
#view > :not(.spinning),
|
||||
#view > .cbi-map > .cbi-map-tabbed > [data-tab-active="true"],
|
||||
.cbi-section-node-tabbed > [data-tab-active="true"] {
|
||||
@apply animate-[shadcn-fade-in_250ms_ease_backwards];
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
@apply pointer-events-none opacity-0 transition-opacity duration-400;
|
||||
@apply pointer-events-none opacity-0 [transition:opacity_.4s];
|
||||
}
|
||||
|
||||
.spinning {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
|
||||
.zonebadge {
|
||||
@apply border-secondary bg-secondary text-secondary-foreground inline-flex items-center gap-1.5 overflow-visible rounded-md border px-2 py-1 align-middle text-sm leading-normal font-semibold transition-colors duration-200;
|
||||
@apply border-secondary bg-secondary text-secondary-foreground inline-flex items-center gap-1.5 overflow-visible rounded-md border px-2 py-1 align-middle text-sm leading-normal font-semibold [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
&[style*="--zone-color-rgb"] {
|
||||
@apply border-transparent bg-[rgb(var(--zone-color-rgb),.6)]!;
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
|
||||
.ifacebadge {
|
||||
@apply border-border bg-label-surface text-foreground inline-flex cursor-default flex-nowrap items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-sm font-normal transition-colors duration-200;
|
||||
@apply border-border bg-label-surface text-foreground inline-flex cursor-default flex-nowrap items-center gap-1.5 rounded-lg border px-2.5 py-1.5 text-sm font-normal [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
.zonebadge > & {
|
||||
@apply rounded-md px-1.5 py-0;
|
||||
|
||||
@@ -4,7 +4,7 @@ input[type="submit"],
|
||||
input[type="reset"],
|
||||
.btn,
|
||||
.cbi-button {
|
||||
@apply inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors duration-200;
|
||||
@apply inline-flex cursor-pointer items-center justify-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
&[disabled] {
|
||||
@apply cursor-not-allowed opacity-40;
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
|
||||
.ifacebox-head {
|
||||
@apply border-border bg-label-surface text-foreground w-full rounded-t-lg border-b px-3 py-2 text-center text-sm font-semibold transition-colors duration-200;
|
||||
@apply border-border bg-label-surface text-foreground w-full rounded-t-lg border-b px-3 py-2 text-center text-sm font-semibold [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
&[style*="--zone-color-rgb"] {
|
||||
@apply bg-[rgb(var(--zone-color-rgb),.65)]!;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.cbi-dropdown {
|
||||
@apply relative inline-flex h-fit max-w-full min-w-fit shrink-0 items-center p-0 whitespace-nowrap transition-colors duration-200;
|
||||
@apply relative inline-flex h-fit max-w-full min-w-fit shrink-0 items-center p-0 whitespace-nowrap [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
&[disabled] {
|
||||
@apply pointer-events-none opacity-40;
|
||||
@@ -13,8 +13,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* luci-base moves focus into the list only on the ul's transitionend. */
|
||||
> ul {
|
||||
@apply m-0 flex flex-1 list-none flex-nowrap items-center gap-0.5 overflow-hidden;
|
||||
@apply m-0 flex flex-1 list-none flex-nowrap items-center gap-0.5 overflow-hidden opacity-99 [transition:opacity_120ms_ease-out];
|
||||
|
||||
> li {
|
||||
@apply m-0 hidden list-none items-center gap-1 overflow-hidden px-2 py-1.5;
|
||||
@@ -48,7 +49,7 @@
|
||||
}
|
||||
|
||||
&.dropdown {
|
||||
@apply border-border bg-panel-bg absolute left-0 z-50 w-fit min-w-full overflow-y-auto rounded-lg border shadow-lg backdrop-blur-sm;
|
||||
@apply border-border bg-panel-bg absolute left-0 z-50 w-fit min-w-full overflow-y-auto rounded-lg border opacity-100 shadow-lg;
|
||||
|
||||
> li {
|
||||
@apply text-muted-foreground hover:bg-muted min-h-9 w-full cursor-pointer px-3 py-1.5 text-sm font-medium;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
@apply inline-flex w-max max-w-96 flex-col items-start gap-3 max-md:max-w-full;
|
||||
|
||||
.item {
|
||||
@apply border-border bg-panel-bg pointer-events-auto relative inline-flex cursor-move flex-col items-start gap-2 self-stretch overflow-hidden rounded-lg border py-3 pr-9 pl-4 break-all shadow-sm transition-all duration-200 select-text;
|
||||
@apply border-border bg-panel-bg pointer-events-auto relative inline-flex cursor-move flex-col items-start gap-2 self-stretch overflow-hidden rounded-lg border py-3 pr-9 pl-4 break-all shadow-sm select-text [transition:scale_.2s,opacity_.2s,box-shadow_.2s,border-color_.2s,background-color_.2s];
|
||||
|
||||
&::after {
|
||||
content: "×";
|
||||
@@ -69,7 +69,7 @@
|
||||
}
|
||||
|
||||
.add-item {
|
||||
@apply border-border hover:border-border-a80 hover:bg-muted-a30 inline-flex items-center gap-1.5 self-stretch rounded-lg border border-dashed bg-transparent px-1 py-1 transition-colors duration-200;
|
||||
@apply border-border hover:border-border-a80 hover:bg-muted-a30 inline-flex items-center gap-1.5 self-stretch rounded-lg border border-dashed bg-transparent px-1 py-1 [transition:color_.2s,background-color_.2s,border-color_.2s];
|
||||
|
||||
> input {
|
||||
@apply min-w-56 flex-1;
|
||||
|
||||
@@ -8,7 +8,7 @@ input[type="number"],
|
||||
input[type="email"],
|
||||
.cbi-input-text,
|
||||
.cbi-input {
|
||||
@apply border-input bg-background text-foreground placeholder:text-muted-foreground-a70 focus:border-primary focus:ring-ring relative w-52.5 max-w-full rounded-lg border px-3 py-1.5 text-sm shadow-none transition-colors duration-200 focus:ring-2 focus:outline-none;
|
||||
@apply border-input bg-background text-foreground placeholder:text-muted-foreground-a70 focus:border-primary focus:ring-ring relative w-52.5 max-w-full rounded-lg border px-3 py-1.5 text-sm shadow-none [transition:color_.2s,background-color_.2s,border-color_.2s] focus:ring-2 focus:outline-none;
|
||||
|
||||
.table.cbi-section-table & {
|
||||
@apply w-full;
|
||||
@@ -24,7 +24,7 @@ input[type="email"],
|
||||
}
|
||||
|
||||
textarea {
|
||||
@apply border-input bg-background text-foreground placeholder:text-muted-foreground-a70 focus:border-primary focus:ring-ring min-h-24 w-full resize-y rounded-lg border px-3 py-2 text-sm transition-colors duration-200 focus:ring-2 focus:outline-none;
|
||||
@apply border-input bg-background text-foreground placeholder:text-muted-foreground-a70 focus:border-primary focus:ring-ring min-h-24 w-full resize-y rounded-lg border px-3 py-2 text-sm [transition:color_.2s,background-color_.2s,border-color_.2s] focus:ring-2 focus:outline-none;
|
||||
|
||||
&[disabled] {
|
||||
@apply cursor-not-allowed opacity-40;
|
||||
@@ -34,8 +34,8 @@ textarea {
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
@apply relative mr-3 inline-block h-4 w-4 cursor-pointer appearance-none align-middle disabled:cursor-not-allowed;
|
||||
@apply before:border-input before:bg-background before:absolute before:inset-0 before:rounded before:border before:transition-colors before:duration-200;
|
||||
@apply after:bg-primary-foreground after:absolute after:inset-0 after:[mask-size:cover] after:[mask-position:center] after:[mask-repeat:no-repeat] after:opacity-0 after:transition-opacity after:duration-200;
|
||||
@apply before:border-input before:bg-background before:absolute before:inset-0 before:rounded before:border before:[transition:background-color_.2s,border-color_.2s];
|
||||
@apply after:bg-primary-foreground after:absolute after:inset-0 after:[mask-size:cover] after:[mask-position:center] after:[mask-repeat:no-repeat] after:opacity-0 after:[transition:opacity_.2s];
|
||||
@apply checked:before:border-primary checked:before:bg-primary checked:after:opacity-100;
|
||||
@apply focus:before:ring-ring focus:before:ring-2 focus:before:outline-none;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
> .modal {
|
||||
@apply border-border bg-panel-bg flex w-full max-w-2xl flex-col gap-4 rounded-xl border p-6 break-words whitespace-normal shadow-2xl backdrop-blur-lg;
|
||||
@apply border-border bg-panel-bg flex w-full max-w-2xl flex-col gap-4 rounded-xl border p-6 break-words whitespace-normal shadow-2xl;
|
||||
|
||||
h4 {
|
||||
@apply mb-0 text-center;
|
||||
|
||||
@@ -46,7 +46,7 @@ button.cmdk-trigger {
|
||||
separates title from label — 460 read cramped once rows became
|
||||
two-anchored. */
|
||||
@apply border-panel-border bg-panel-bg mt-[15vh] flex w-[min(560px,calc(100vw-32px))] flex-col overflow-hidden rounded-xl border shadow-2xl;
|
||||
@apply transition-[opacity,translate] duration-150 ease-out starting:-translate-y-1.5 starting:opacity-0;
|
||||
@apply [transition:opacity_.15s_var(--ease-out),translate_.15s_var(--ease-out)] starting:-translate-y-1.5 starting:opacity-0;
|
||||
@apply max-md:mt-0 max-md:h-dvh max-md:w-full max-md:rounded-none max-md:border-0 max-md:pt-[env(safe-area-inset-top)] max-md:shadow-none max-md:transition-none;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
@apply before:text-foreground before:absolute before:inset-x-0 before:top-1/2 before:-translate-y-1/2 before:text-center before:text-[10px] before:leading-none before:whitespace-nowrap before:content-[attr(title)];
|
||||
|
||||
> div {
|
||||
@apply from-progress-bar-start to-progress-bar-end h-full bg-gradient-to-r transition-all duration-300;
|
||||
@apply from-progress-bar-start to-progress-bar-end h-full bg-gradient-to-r [transition:width_.3s];
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
DOM. Only width and opacity move. Reduced motion drops the transitions,
|
||||
not the bar. */
|
||||
#luci-nav-progress {
|
||||
@apply from-progress-bar-start to-progress-bar-end pointer-events-none fixed top-0 left-0 z-80 h-0.5 w-0 bg-linear-to-r transition-[width] duration-300 ease-out;
|
||||
@apply from-progress-bar-start to-progress-bar-end pointer-events-none fixed top-0 left-0 z-80 h-0.5 w-0 bg-linear-to-r [transition:width_.3s_var(--ease-out)];
|
||||
|
||||
&[data-state="done"] {
|
||||
@apply opacity-0 [transition:width_.3s_ease-out,opacity_.15s_ease-in_.15s];
|
||||
@@ -32,6 +32,6 @@
|
||||
|
||||
/* Outgoing page while the router renders the next one. */
|
||||
#view.view-leaving {
|
||||
@apply cursor-progress opacity-60 transition-opacity duration-150;
|
||||
@apply cursor-progress opacity-60 [transition:opacity_.15s];
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
select {
|
||||
@apply border-input bg-background text-foreground focus:border-primary focus:ring-ring max-w-full min-w-52.5 appearance-none rounded-lg border px-3 py-1.5 pr-9 text-sm transition-colors duration-200 focus:ring-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40 md:max-w-100;
|
||||
@apply border-input bg-background text-foreground focus:border-primary focus:ring-ring max-w-full min-w-52.5 appearance-none rounded-lg border px-3 py-1.5 pr-9 text-sm [transition:color_.2s,background-color_.2s,border-color_.2s] focus:ring-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-40 md:max-w-100;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%238888a0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 0.6rem center;
|
||||
|
||||
@@ -34,7 +34,7 @@ table.table,
|
||||
|
||||
tr,
|
||||
.tr {
|
||||
@apply hover:bg-label-surface max-md:border-border transition-colors duration-200 max-md:flex max-md:flex-wrap max-md:border-b max-md:px-2 max-md:py-2;
|
||||
@apply hover:bg-label-surface max-md:border-border [transition:background-color_.2s] max-md:flex max-md:flex-wrap max-md:border-b max-md:px-2 max-md:py-2;
|
||||
|
||||
&:first-child {
|
||||
&:not([data-title]) {
|
||||
@@ -74,7 +74,7 @@ table.table,
|
||||
|
||||
th,
|
||||
.th {
|
||||
@apply border-border bg-label-surface border-b px-3 py-2.5 text-left text-sm font-medium backdrop-blur-sm max-md:hidden;
|
||||
@apply border-border bg-label-surface border-b px-3 py-2.5 text-left text-sm font-medium max-md:hidden;
|
||||
|
||||
&[width="100%"] {
|
||||
@apply w-full;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
> a {
|
||||
@apply text-muted-foreground hover:text-foreground focus-visible:ring-ring-a50 inline-flex items-center justify-center rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap no-underline transition-all focus-visible:ring-[3px] focus-visible:outline-none;
|
||||
@apply text-muted-foreground hover:text-foreground focus-visible:ring-ring-a50 inline-flex items-center justify-center rounded-md border border-transparent px-3 py-1 text-sm font-medium whitespace-nowrap no-underline [transition:color_.15s,background-color_.15s,border-color_.15s,box-shadow_.15s] focus-visible:ring-[3px] focus-visible:outline-none;
|
||||
}
|
||||
|
||||
&.cbi-tab > a {
|
||||
@@ -39,7 +39,7 @@
|
||||
@apply border-border relative flex items-center gap-0 overflow-x-auto border-b;
|
||||
|
||||
a {
|
||||
@apply text-muted-foreground hover:text-foreground block px-6 py-3 text-center text-sm font-medium whitespace-nowrap transition-colors duration-200 hover:no-underline;
|
||||
@apply text-muted-foreground hover:text-foreground block px-6 py-3 text-center text-sm font-medium whitespace-nowrap [transition:color_.2s,background-color_.2s,border-color_.2s] hover:no-underline;
|
||||
}
|
||||
|
||||
> li.active {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
`transition-all` would animate the inset and make the bubble fly in from
|
||||
off-screen. `w-max` sizes the bubble by its content instead of the narrow
|
||||
anchor's containing block (which squeezed it to one word per line). */
|
||||
@apply border-border-a60 bg-panel-bg text-foreground absolute inset-s-[-10000px] z-50 w-max max-w-xs scale-95 rounded-lg border px-3 py-2 text-xs break-normal whitespace-normal opacity-0 shadow-lg backdrop-blur-sm transition-[visibility,opacity,scale] duration-200 ease-out;
|
||||
@apply border-border-a60 bg-panel-bg text-foreground absolute inset-s-[-10000px] z-50 w-max max-w-xs scale-95 rounded-lg border px-3 py-2 text-xs break-normal whitespace-normal opacity-0 shadow-lg [transition:visibility_.2s_var(--ease-out),opacity_.2s_var(--ease-out),scale_.2s_var(--ease-out)];
|
||||
|
||||
&.error {
|
||||
@apply border-destructive-a40 bg-destructive text-destructive-fg;
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
.cbi-tooltip-container,
|
||||
[data-tooltip] {
|
||||
@apply relative inline-block cursor-help whitespace-nowrap underline decoration-current decoration-dotted underline-offset-2 transition-all duration-200;
|
||||
@apply relative inline-block cursor-help whitespace-nowrap underline decoration-current decoration-dotted underline-offset-2;
|
||||
|
||||
.cbi-tooltip {
|
||||
@apply invisible scale-95 opacity-0 transition-[visibility,opacity,scale] duration-200 ease-out;
|
||||
@apply invisible scale-95 opacity-0 [transition:visibility_.2s_var(--ease-out),opacity_.2s_var(--ease-out),scale_.2s_var(--ease-out)];
|
||||
}
|
||||
|
||||
&:hover .cbi-tooltip {
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
/* Cross-document view transitions: same-origin full-page navigations
|
||||
cross-fade instead of hard-flashing. Progressive enhancement — browsers
|
||||
without support (e.g. Firefox) keep plain instant navigation. */
|
||||
|
||||
@view-transition {
|
||||
navigation: auto;
|
||||
}
|
||||
|
||||
/* The sidebar persists across pages, so it gets its own snapshot group and
|
||||
stays visually static while only the rest of the page transitions. The
|
||||
topbar deliberately has no group: its snapshot would render in the top
|
||||
layer, unclipped by .content-card's rounded corners (squaring them for
|
||||
the whole transition), and its pixels are identical across pages anyway
|
||||
so the root cross-fade already keeps it visually static. */
|
||||
#sidebar {
|
||||
view-transition-name: shadcn-sidebar;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
@view-transition {
|
||||
navigation: none;
|
||||
}
|
||||
/* No view transitions: swapped and freshly loaded content fades in instead
|
||||
(_utilities.css). A captured page would crossfade two layouts on top of
|
||||
that fade, and a cross-document one would only fade to the spinner. */
|
||||
:root {
|
||||
view-transition-name: none;
|
||||
}
|
||||
@@ -4,18 +4,44 @@
|
||||
* Licensed to the public under the Apache License 2.0.
|
||||
*/
|
||||
|
||||
@import "tailwindcss";
|
||||
/* Theme variables and @apply'd utilities only: no Preflight, no scanned
|
||||
classes. The reset below covers what sysauth.ut renders. */
|
||||
@layer theme, base, components, utilities;
|
||||
@import "tailwindcss/theme.css" layer(theme);
|
||||
@import "tailwindcss/utilities.css" layer(utilities) source(none);
|
||||
|
||||
@import "./inter.css";
|
||||
@import "./_tokens.css";
|
||||
@import "./_shared.css";
|
||||
|
||||
/* See main.css: keep dev-only tooling out of Tailwind's source detection. */
|
||||
@source not "../../vite.config.ts";
|
||||
@source not "../../scripts/*.client.js";
|
||||
|
||||
@custom-variant dark (&:where([data-darkmode=true], [data-darkmode=true] *));
|
||||
|
||||
@layer base {
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
@apply box-border;
|
||||
}
|
||||
|
||||
body,
|
||||
p {
|
||||
@apply m-0;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
@apply m-0 border-0 bg-transparent p-0 [color:inherit] [font:inherit];
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
@apply hidden!;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
@apply bg-background text-foreground font-sans text-sm;
|
||||
}
|
||||
@@ -128,7 +154,7 @@ html {
|
||||
|
||||
/* Glass input wrapper — matches sign-in.tsx GlassInputWrapper */
|
||||
.login-input-glass {
|
||||
@apply border-border bg-foreground-a5 relative rounded-2xl border transition-colors;
|
||||
@apply border-border bg-foreground-a5 relative rounded-2xl border [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
backdrop-filter: blur(4px);
|
||||
|
||||
&:focus-within {
|
||||
@@ -152,7 +178,7 @@ html {
|
||||
@apply absolute inset-y-0 right-0 flex cursor-pointer items-center border-0 bg-transparent px-3;
|
||||
|
||||
img {
|
||||
@apply h-4 w-4 opacity-40 transition-opacity;
|
||||
@apply h-4 w-4 opacity-40 [transition:opacity_.15s];
|
||||
}
|
||||
|
||||
&:hover img {
|
||||
@@ -161,7 +187,7 @@ html {
|
||||
}
|
||||
|
||||
.login-submit {
|
||||
@apply bg-primary text-primary-foreground hover:bg-primary-hover mt-4 w-full cursor-pointer rounded-2xl border-0 py-3.5 text-sm font-medium transition-colors;
|
||||
@apply bg-primary text-primary-foreground hover:bg-primary-hover mt-4 w-full cursor-pointer rounded-2xl border-0 py-3.5 text-sm font-medium [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
}
|
||||
|
||||
/* ── Footer ── */
|
||||
@@ -170,7 +196,7 @@ html {
|
||||
@apply text-muted-foreground absolute right-0 bottom-5 left-0 px-6 text-center text-xs;
|
||||
|
||||
a {
|
||||
@apply text-muted-foreground hover:text-foreground underline underline-offset-2 transition-colors;
|
||||
@apply text-muted-foreground hover:text-foreground underline underline-offset-2 [transition:color_.15s,background-color_.15s,border-color_.15s];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
@import "tailwindcss";
|
||||
@import "./inter.css";
|
||||
@import "./_tokens.css";
|
||||
@import "tw-animate-css";
|
||||
|
||||
/* Dev-only tooling holds prose and plain CSS whose bare words (static,
|
||||
transition, backdrop-filter, …) Tailwind's automatic source detection would
|
||||
@@ -41,3 +40,4 @@
|
||||
@import "./components/_router.css";
|
||||
@import "./_utilities.css";
|
||||
@import "./_shared.css";
|
||||
@import "./_reduced-motion.css";
|
||||
@@ -522,11 +522,6 @@ return baseclass.extend({
|
||||
];
|
||||
|
||||
const isMac = /Mac|iP(ad|hone|od)/.test(navigator.platform);
|
||||
const keyEl = trigger.querySelector(".cmdk-trigger-key");
|
||||
if (keyEl) {
|
||||
keyEl.textContent = isMac ? "⌘K" : "Ctrl+K";
|
||||
keyEl.hidden = false;
|
||||
}
|
||||
trigger.setAttribute("aria-keyshortcuts", isMac ? "Meta+K" : "Control+K");
|
||||
trigger.addEventListener("click", () => this.openPalette());
|
||||
|
||||
|
||||
@@ -116,6 +116,7 @@ return baseclass.extend({
|
||||
"data-sidebar-collapsed",
|
||||
next ? "true" : "false",
|
||||
);
|
||||
document.documentElement.setAttribute("data-sidebar-anim", "");
|
||||
localStorage.setItem(SIDEBAR_KEY, next);
|
||||
|
||||
this._hideCollapsedPopover();
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync, statSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const output = resolve(import.meta.dirname, "../../htdocs/luci-static");
|
||||
const read = (path) => readFileSync(resolve(output, path), "utf8");
|
||||
const bytes = (path) => statSync(resolve(output, path)).size;
|
||||
|
||||
test("pruned login.css keeps every consumed variable resolvable", () => {
|
||||
const css = read("shadcn/login.css");
|
||||
const declared = new Set(
|
||||
[...css.matchAll(/[{;](--[\w-]+):/g)].map((m) => m[1]),
|
||||
);
|
||||
const registered = new Set(
|
||||
[...css.matchAll(/@property\s+(--[\w-]+)/g)].map((m) => m[1]),
|
||||
);
|
||||
// sysauth.ut declares it inline at render time.
|
||||
const injected = new Set(["--login-bg-image"]);
|
||||
|
||||
const unresolvable = [];
|
||||
for (const [, name, delim] of css.matchAll(/var\(\s*(--[\w-]+)\s*([,)])/g))
|
||||
if (
|
||||
delim !== "," &&
|
||||
!declared.has(name) &&
|
||||
!registered.has(name) &&
|
||||
!injected.has(name)
|
||||
)
|
||||
unresolvable.push(name);
|
||||
assert.deepEqual([...new Set(unresolvable)], []);
|
||||
|
||||
for (const adminOnly of ["--sidebar-bg:", "--terminal-foreground:"])
|
||||
assert.ok(!css.includes(adminOnly), `${adminOnly} should be pruned`);
|
||||
assert.ok(
|
||||
css.includes("-webkit-backdrop-filter"),
|
||||
"prefixes must survive the prune",
|
||||
);
|
||||
});
|
||||
|
||||
// uhttpd serves identity bytes, so raw size is what every cold load pays.
|
||||
// Set 2026-09 from main 129213, login 10521, menu 15859, router 15771,
|
||||
// sidebar 6107, Inter latin 48256, logo 258.
|
||||
test("production assets stay within raw-transfer budgets", () => {
|
||||
const main = bytes("shadcn/main.css");
|
||||
const login = bytes("shadcn/login.css");
|
||||
const menu = bytes("resources/menu-shadcn.js");
|
||||
const router = bytes("resources/router-shadcn.js");
|
||||
const sidebar = bytes("resources/sidebar-shadcn.js");
|
||||
const font = bytes("shadcn/inter-latin-wght-normal.woff2");
|
||||
const logo = bytes("shadcn/images/logo.svg");
|
||||
|
||||
assert.ok(main <= 130_000, `main.css ${main} B exceeds 130 KB`);
|
||||
assert.ok(login <= 11_000, `login.css ${login} B exceeds 11 KB`);
|
||||
assert.ok(menu <= 16_000, `menu-shadcn.js ${menu} B exceeds 16 KB`);
|
||||
assert.ok(router <= 16_000, `router-shadcn.js ${router} B exceeds 16 KB`);
|
||||
assert.ok(sidebar <= 6_500, `sidebar-shadcn.js ${sidebar} B exceeds 6.5 KB`);
|
||||
assert.ok(
|
||||
main + menu + router + sidebar + font <= 216_000,
|
||||
"admin assets exceed 216 KB",
|
||||
);
|
||||
assert.ok(login + font + logo <= 60_000, "login assets exceed 60 KB");
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import { luciDev, injectMockBar } from "@eamonxg/luci-theme-devkit/vite/dev";
|
||||
import { existsSync, readdirSync } from "fs";
|
||||
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
||||
import { dirname, join, resolve } from "path";
|
||||
import { transform as lightningcssTransform } from "lightningcss";
|
||||
import { minify as terserMinify } from "terser";
|
||||
import { defineConfig, loadEnv, Plugin, ResolvedConfig } from "vite";
|
||||
import config from "./luci-theme.config.js";
|
||||
@@ -108,6 +109,68 @@ function createPatchAliasPlugin(): Plugin {
|
||||
};
|
||||
}
|
||||
|
||||
/* login.css imports the full token sheet but the login page consumes a
|
||||
fraction of it. Drop every custom property (and --tw-* @property) no var()
|
||||
can reach, following --a: var(--b) chains; consumers are never touched, so
|
||||
sysauth.ut's inline --login-bg-image keeps working. Same as aurora's. */
|
||||
function createLoginCssPrunePlugin(): Plugin {
|
||||
return {
|
||||
name: "login-css-prune",
|
||||
apply: "build",
|
||||
enforce: "post",
|
||||
async closeBundle() {
|
||||
const path = resolve(BUILD_OUTPUT, "shadcn/login.css");
|
||||
if (!existsSync(path)) return;
|
||||
const css = await readFile(path, "utf-8");
|
||||
|
||||
const VALUE = `(?:"[^"]*"|'[^']*'|[^;{}"'])*`;
|
||||
const roots = new Set<string>();
|
||||
const edges = new Map<string, Set<string>>();
|
||||
for (const [, name, value] of css.matchAll(
|
||||
new RegExp(`(?<=[{;])(--[\\w-]+|[a-zA-Z-]+):(${VALUE})`, "g"),
|
||||
)) {
|
||||
const refs = [...value.matchAll(/var\(\s*(--[\w-]+)/g)].map(
|
||||
(m) => m[1],
|
||||
);
|
||||
if (!name.startsWith("--")) refs.forEach((r) => roots.add(r));
|
||||
else {
|
||||
const deps = edges.get(name) ?? new Set<string>();
|
||||
refs.forEach((r) => deps.add(r));
|
||||
edges.set(name, deps);
|
||||
}
|
||||
}
|
||||
const keep = new Set(roots);
|
||||
const stack = [...roots];
|
||||
while (stack.length) {
|
||||
for (const dep of edges.get(stack.pop()!) ?? []) {
|
||||
if (!keep.has(dep)) {
|
||||
keep.add(dep);
|
||||
stack.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pruned = css
|
||||
.replace(
|
||||
new RegExp(`(?<=[{;])(--[\\w-]+):${VALUE};?`, "g"),
|
||||
(decl, name) => (keep.has(name) ? decl : ""),
|
||||
)
|
||||
.replace(/@property\s+(--[\w-]+)\{[^{}]*\}/g, (rule, name) =>
|
||||
keep.has(name) ? rule : "",
|
||||
)
|
||||
.replace(/(?<=[{}])[^{}@]+\{\}/g, "");
|
||||
|
||||
// Parse only: a re-minify without Vite's targets would strip the
|
||||
// -webkit- prefixes older Safari still needs.
|
||||
lightningcssTransform({
|
||||
filename: "login.css",
|
||||
code: Buffer.from(pruned),
|
||||
});
|
||||
await writeFile(path, pruned);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig(({ mode, command }) => {
|
||||
const env = loadEnv(mode, CURRENT_DIR);
|
||||
// VITE_OPENWRT_HOST is just the router address — a bare IP/hostname like
|
||||
@@ -133,6 +196,7 @@ export default defineConfig(({ mode, command }) => {
|
||||
...luciDev(config, { sshHost: OPENWRT_SSH_HOST }),
|
||||
createLuciJsCompressPlugin(),
|
||||
createPatchAliasPlugin(),
|
||||
createLoginCssPrunePlugin(),
|
||||
],
|
||||
build: {
|
||||
outDir: BUILD_OUTPUT,
|
||||
|
||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=A modern sidebar LuCI theme for OpenWrt, built with shadcn/ui design language.
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=0.5.4
|
||||
PKG_RELEASE:=3
|
||||
PKG_VERSION:=0.5.10
|
||||
PKG_RELEASE:=4
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
@@ -1 +1 @@
|
||||
"use strict";"require baseclass";"require ui";const ICON_MAP={status:"activity",system:"settings",network:"network",services:"layers",nas:"hard-drive",control:"sliders-horizontal",vpn:"shield",docker:"container",statistics:"chart-bar",nlbw:"gauge",asterisk:"phone",logout:"log-out",default:"layout-dashboard"},CACHE_KEY="shadcn.sidebar.cache",RECENTS_KEY="shadcn.palette.recents";return baseclass.extend({__init__(){ui.menu.load().then(e=>{this.renderSidebarChrome(),this.render(e),this.initUciIndicator(),this.cacheSidebar(),window.addEventListener("pagehide",()=>this.cacheSidebar()),document.dispatchEvent(new Event("shadcn-sidebar-ready"))})},cacheSidebar(){if(window.shadcnSuppressSidebarCache)return;const e=document.getElementById("sidebar"),t=document.getElementById("sidebar-nav");if(e&&t)try{sessionStorage.setItem(CACHE_KEY,JSON.stringify({v:1,lang:document.documentElement.lang||"",html:e.innerHTML,scroll:t.scrollTop||0}))}catch(e){}},_mediaBase(){const e=document.documentElement.getAttribute("data-shadcn-media"),t=document.getElementById("sidebar"),a=t&&t.getAttribute("data-media");return String(e||a||"/luci-static/shadcn").replace(/\/$/,"")},_sectionIcon(e,t){const a=String(e||"").toLowerCase(),s=ICON_MAP[a]||ICON_MAP.default;return this._iconFile(s,t)},_isLogoutMenuItem(e){const t=String(e&&null!=e.name?e.name:"").toLowerCase();return"logout"===t||t.endsWith("/logout")},_iconFile(e,t){const a=this._mediaBase();return E("img",{src:`${a}/icons/${e}.svg`,width:String(t),height:String(t),alt:"",loading:"lazy",class:"shadcn-icon"})},_resolveMenuBranch(e){const t=L.env.dispatchpath||[],a=L.env.requestpath||[],s=ui.menu.getChildren(e);let n=a.length&&a[0]||t.length&&t[0]||"admin",i=null;for(let e=0;e<s.length;e++)if(s[e].name===n){i=s[e];break}return i||1!==s.length||(i=s[0],n=i.name),i||(i=e),{branch:i,branchUrl:i.name||n}},renderSidebarChrome(){const e=document.getElementById("sidebar");if(!e)return;if("1"===e.getAttribute("data-shadcn-built")){const t=e.querySelector(".sidebar-brand-text"),a=e.getAttribute("data-hostname");return void(t&&a&&t.textContent!==a&&(t.textContent=a))}e.setAttribute("data-shadcn-built","1");const t=this._mediaBase(),a=e.getAttribute("data-hostname")||"Shadcn";for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(E("div",{class:"sidebar-header"},[E("a",{class:"sidebar-brand",href:"/"},[E("img",{class:"sidebar-logo",src:`${t}/images/logo.svg`,alt:"Shadcn",width:"24",height:"24"}),E("span",{class:"sidebar-brand-text"},[a])]),E("button",{id:"sidebar-toggle-btn",class:"sidebar-toggle",type:"button","aria-label":_("Navigation")},[E("span",{class:"icon-collapse"},[this._iconFile("panel-left-close",16)]),E("span",{class:"icon-expand"},[this._iconFile("panel-left-open",16)])])])),e.appendChild(E("nav",{class:"sidebar-nav",id:"sidebar-nav"})),e.appendChild(E("div",{class:"sidebar-footer",id:"sidebar-footer",hidden:""}))},render(e){const{branch:t,branchUrl:a}=this._resolveMenuBranch(e);this.tree=e,this.branch=t,this.branchUrl=a,this.renderSidebarNav(t,a),this.renderBreadcrumb(t,a),this.initPalette(t,a),this.renderTabs(e)},renderTabs(e){const t=L.env.dispatchpath||[],a=document.getElementById("tabmenu");a&&(a.innerHTML="",a.style.display="none");let s=e,n="";if(t.length>=3){for(var i=0;i<3&&s;i++){const e=t[i];if(!s.children||!s.children[e])break;s=s.children[e],n=n+(n?"/":"")+e}s&&this.renderTabMenu(s,n)}},syncRoute(){if(!this.tree)return;const e=document.getElementById("sidebar");if(e){e.querySelectorAll(".active").forEach(e=>e.classList.remove("active"));const t=new URL(L.url(...(L.env.dispatchpath||[]).slice(0,3)),location.href).pathname;let a=null,s=-1;if(e.querySelectorAll("a.sidebar-sub-link, a.sidebar-nav-parent, a.sidebar-logout").forEach(e=>{const n=e.pathname;(t===n||t.startsWith(n+"/"))&&n.length>s&&(a=e,s=n.length)}),a){const t=a.closest(".sidebar-sub-item");if(t){t.classList.add("active");const s=a.closest(".sidebar-accordion-item");s&&(s.classList.add("active"),"true"!==e.getAttribute("data-collapsed")&&e.querySelectorAll(".sidebar-accordion-item").forEach(e=>e.setAttribute("data-open",e===s?"true":"false")))}else a.classList.contains("sidebar-logout")?a.classList.add("active"):a.closest(".sidebar-nav-item")?.classList.add("active")}}this.renderBreadcrumb(this.branch,this.branchUrl),this.renderTabs(this.tree)},closeSurfaces(){this.closePalette(),window.ShadcnSidebar?.closeDrawer?.(),window.ShadcnSidebar?._hideCollapsedPopover?.()},renderSidebarNav(e,t){const a=document.getElementById("sidebar-nav"),s=document.getElementById("sidebar-footer");if(!a)return;const n=ui.menu.getChildren(e),i=L.env.dispatchpath||[],r="1"===document.getElementById("sidebar")?.getAttribute("data-shadcn-restored"),l=r?Array.from(a.querySelectorAll('.sidebar-accordion-item[data-open="true"]'),e=>e.getAttribute("data-section")):null,c=r?a.scrollTop:0;a.innerHTML="",s&&(s.innerHTML="",s.hidden=!0),n.forEach(e=>{if(this._isLogoutMenuItem(e)&&s){const a=i[1]==e.name,n=this._sectionIcon(e.name,18);returLine truncated
|
||||
"use strict";"require baseclass";"require ui";const ICON_MAP={status:"activity",system:"settings",network:"network",services:"layers",nas:"hard-drive",control:"sliders-horizontal",vpn:"shield",docker:"container",statistics:"chart-bar",nlbw:"gauge",asterisk:"phone",logout:"log-out",default:"layout-dashboard"},CACHE_KEY="shadcn.sidebar.cache",RECENTS_KEY="shadcn.palette.recents";return baseclass.extend({__init__(){ui.menu.load().then(e=>{this.renderSidebarChrome(),this.render(e),this.initUciIndicator(),this.cacheSidebar(),window.addEventListener("pagehide",()=>this.cacheSidebar()),document.dispatchEvent(new Event("shadcn-sidebar-ready"))})},cacheSidebar(){if(window.shadcnSuppressSidebarCache)return;const e=document.getElementById("sidebar"),t=document.getElementById("sidebar-nav");if(e&&t)try{sessionStorage.setItem(CACHE_KEY,JSON.stringify({v:1,lang:document.documentElement.lang||"",html:e.innerHTML,scroll:t.scrollTop||0}))}catch(e){}},_mediaBase(){const e=document.documentElement.getAttribute("data-shadcn-media"),t=document.getElementById("sidebar"),a=t&&t.getAttribute("data-media");return String(e||a||"/luci-static/shadcn").replace(/\/$/,"")},_sectionIcon(e,t){const a=String(e||"").toLowerCase(),s=ICON_MAP[a]||ICON_MAP.default;return this._iconFile(s,t)},_isLogoutMenuItem(e){const t=String(e&&null!=e.name?e.name:"").toLowerCase();return"logout"===t||t.endsWith("/logout")},_iconFile(e,t){const a=this._mediaBase();return E("img",{src:`${a}/icons/${e}.svg`,width:String(t),height:String(t),alt:"",loading:"lazy",class:"shadcn-icon"})},_resolveMenuBranch(e){const t=L.env.dispatchpath||[],a=L.env.requestpath||[],s=ui.menu.getChildren(e);let n=a.length&&a[0]||t.length&&t[0]||"admin",i=null;for(let e=0;e<s.length;e++)if(s[e].name===n){i=s[e];break}return i||1!==s.length||(i=s[0],n=i.name),i||(i=e),{branch:i,branchUrl:i.name||n}},renderSidebarChrome(){const e=document.getElementById("sidebar");if(!e)return;if("1"===e.getAttribute("data-shadcn-built")){const t=e.querySelector(".sidebar-brand-text"),a=e.getAttribute("data-hostname");return void(t&&a&&t.textContent!==a&&(t.textContent=a))}e.setAttribute("data-shadcn-built","1");const t=this._mediaBase(),a=e.getAttribute("data-hostname")||"Shadcn";for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(E("div",{class:"sidebar-header"},[E("a",{class:"sidebar-brand",href:"/"},[E("img",{class:"sidebar-logo",src:`${t}/images/logo.svg`,alt:"Shadcn",width:"24",height:"24"}),E("span",{class:"sidebar-brand-text"},[a])]),E("button",{id:"sidebar-toggle-btn",class:"sidebar-toggle",type:"button","aria-label":_("Navigation")},[E("span",{class:"icon-collapse"},[this._iconFile("panel-left-close",16)]),E("span",{class:"icon-expand"},[this._iconFile("panel-left-open",16)])])])),e.appendChild(E("nav",{class:"sidebar-nav",id:"sidebar-nav"})),e.appendChild(E("div",{class:"sidebar-footer",id:"sidebar-footer",hidden:""}))},render(e){const{branch:t,branchUrl:a}=this._resolveMenuBranch(e);this.tree=e,this.branch=t,this.branchUrl=a,this.renderSidebarNav(t,a),this.renderBreadcrumb(t,a),this.initPalette(t,a),this.renderTabs(e)},renderTabs(e){const t=L.env.dispatchpath||[],a=document.getElementById("tabmenu");a&&(a.innerHTML="",a.style.display="none");let s=e,n="";if(t.length>=3){for(var i=0;i<3&&s;i++){const e=t[i];if(!s.children||!s.children[e])break;s=s.children[e],n=n+(n?"/":"")+e}s&&this.renderTabMenu(s,n)}},syncRoute(){if(!this.tree)return;const e=document.getElementById("sidebar");if(e){e.querySelectorAll(".active").forEach(e=>e.classList.remove("active"));const t=new URL(L.url(...(L.env.dispatchpath||[]).slice(0,3)),location.href).pathname;let a=null,s=-1;if(e.querySelectorAll("a.sidebar-sub-link, a.sidebar-nav-parent, a.sidebar-logout").forEach(e=>{const n=e.pathname;(t===n||t.startsWith(n+"/"))&&n.length>s&&(a=e,s=n.length)}),a){const t=a.closest(".sidebar-sub-item");if(t){t.classList.add("active");const s=a.closest(".sidebar-accordion-item");s&&(s.classList.add("active"),"true"!==e.getAttribute("data-collapsed")&&e.querySelectorAll(".sidebar-accordion-item").forEach(e=>e.setAttribute("data-open",e===s?"true":"false")))}else a.classList.contains("sidebar-logout")?a.classList.add("active"):a.closest(".sidebar-nav-item")?.classList.add("active")}}this.renderBreadcrumb(this.branch,this.branchUrl),this.renderTabs(this.tree)},closeSurfaces(){this.closePalette(),window.ShadcnSidebar?.closeDrawer?.(),window.ShadcnSidebar?._hideCollapsedPopover?.()},renderSidebarNav(e,t){const a=document.getElementById("sidebar-nav"),s=document.getElementById("sidebar-footer");if(!a)return;const n=ui.menu.getChildren(e),i=L.env.dispatchpath||[],r="1"===document.getElementById("sidebar")?.getAttribute("data-shadcn-restored"),l=r?Array.from(a.querySelectorAll('.sidebar-accordion-item[data-open="true"]'),e=>e.getAttribute("data-section")):null,c=r?a.scrollTop:0;a.innerHTML="",s&&(s.innerHTML="",s.hidden=!0),n.forEach(e=>{if(this._isLogoutMenuItem(e)&&s){const a=i[1]==e.name,n=this._sectionIcon(e.name,18);returLine truncated
|
||||
@@ -1 +1 @@
|
||||
"use strict";"require baseclass";const THEME_KEY="shadcn.theme",SIDEBAR_KEY="shadcn.sidebar";return baseclass.extend({__init__(){const e=document.getElementById("sidebar");"1"!==e?.getAttribute("data-shadcn-built")?document.addEventListener("shadcn-sidebar-ready",()=>this._start(),{once:!0}):this._start()},_start(){if(this._started)return;const e=document.getElementById("sidebar");"1"===e?.getAttribute("data-shadcn-built")&&(this._started=!0,this.initTheme(),this.initSidebar(),this.initMobileDrawer(),this.initThemeToggle())},initTheme(){const e=localStorage.getItem(THEME_KEY)||"device";this.applyTheme(e)},applyTheme(e){const t="dark"===e||"device"===e&&window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.setAttribute("data-darkmode",t),localStorage.setItem(THEME_KEY,e),this._updateThemeIcon(t)},_updateThemeIcon(e){const t=document.getElementById("theme-toggle-btn");if(!t)return;const o=t.querySelector("img");if(!o)return;const s=document.documentElement.getAttribute("data-shadcn-media")||"/luci-static/shadcn",d=String(s).replace(/\/$/,"");o.src=d+(e?"/icons/moon.svg":"/icons/sun.svg")},initThemeToggle(){const e=document.getElementById("theme-toggle-btn");e&&(e.addEventListener("click",()=>{const e="true"===document.documentElement.getAttribute("data-darkmode");this.applyTheme(e?"light":"dark")}),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{"device"===(localStorage.getItem(THEME_KEY)||"device")&&this.applyTheme("device")}))},initSidebar(){const e=document.getElementById("sidebar");if(!e)return;const t=getComputedStyle(document.documentElement).getPropertyValue("--breakpoint-md").trim();this._mobileQuery=window.matchMedia(`(width < ${t})`),this._mobileQuery.addEventListener("change",e=>{e.matches||this._hideCollapsedPopover()});const o="true"===localStorage.getItem(SIDEBAR_KEY);e.setAttribute("data-collapsed",o);const s=document.getElementById("sidebar-toggle-btn");s&&s.addEventListener("click",()=>{this._mobileQuery.matches?this.closeDrawer():this.toggleCollapse()}),this._initAccordion(),this._initCollapsedPopover()},toggleCollapse(){const e=document.getElementById("sidebar");if(!e)return;const t="true"!==e.getAttribute("data-collapsed");e.setAttribute("data-collapsed",t),document.documentElement.setAttribute("data-sidebar-collapsed",t?"true":"false"),localStorage.setItem(SIDEBAR_KEY,t),this._hideCollapsedPopover(),t&&document.querySelectorAll(".sidebar-accordion-item[data-open='true']").forEach(e=>{this._closeAccordionItem(e)})},_initCollapsedPopover(){document.addEventListener("mouseover",e=>{const t=document.getElementById("sidebar");if("true"!==t?.getAttribute("data-collapsed"))return;if(e.target.closest("#sidebar-collapsed-popover"))return void clearTimeout(this._popoverTimer);const o=e.target.closest(".sidebar-accordion-item");o&&(clearTimeout(this._popoverTimer),this._showCollapsedPopover(o))}),document.addEventListener("mouseout",e=>{const t=document.getElementById("sidebar");if("true"!==t?.getAttribute("data-collapsed"))return;if(!e.target.closest(".sidebar-accordion-item")&&!e.target.closest("#sidebar-collapsed-popover"))return;const o=e.relatedTarget;o&&(o.closest(".sidebar-accordion-item")||o.closest("#sidebar-collapsed-popover"))||(this._popoverTimer=setTimeout(()=>this._hideCollapsedPopover(),100))})},_showCollapsedPopover(e){const t=e.getAttribute("data-section")||"",o=document.getElementById("sidebar-collapsed-popover");if(o&&o.getAttribute("data-section")===t)return;this._hideCollapsedPopover();const s=e.querySelectorAll(".sidebar-sub-link");if(!s.length)return;const d=e.querySelector(".sidebar-label"),i=d?d.textContent.trim():"",r=document.createElement("div");if(r.id="sidebar-collapsed-popover",r.className="sidebar-collapsed-popover",r.setAttribute("data-section",t),i){const e=document.createElement("div");e.className="sidebar-collapsed-popover-title",e.textContent=i,r.appendChild(e)}const a=document.createElement("ul");a.className="sidebar-collapsed-popover-list",s.forEach(e=>{const t=e.closest(".sidebar-sub-item")?.classList.contains("active"),o=document.createElement("li"),s=document.createElement("a");s.href=e.href,s.className="sidebar-collapsed-popover-link"+(t?" active":""),s.textContent=e.textContent,o.appendChild(s),a.appendChild(o)}),r.appendChild(a),document.body.appendChild(r);const n=document.getElementById("sidebar").getBoundingClientRect(),c=e.getBoundingClientRect();r.style.left=n.right+4+"px";const l=Math.min(c.top,window.innerHeight-r.offsetHeight-8);r.style.top=Math.max(8,l)+"px"},_hideCollapsedPopover(){clearTimeout(this._popoverTimer);const e=document.getElementById("sidebar-collapsed-popover");e&&e.remove()},_initAccordion(){document.addEventListener("click",e=>{const t=e.target.closest(".sidebar-nav-parent");if(!t)return;const o=document.getElementById("sidebar");if("true"===o?.getAttribute("data-collapsed"))return;const s=t.closest(".sidebar-accordion-item");if(!s)return;const d="true"===s.getAttributLine truncated
|
||||
"use strict";"require baseclass";const THEME_KEY="shadcn.theme",SIDEBAR_KEY="shadcn.sidebar";return baseclass.extend({__init__(){const e=document.getElementById("sidebar");"1"!==e?.getAttribute("data-shadcn-built")?document.addEventListener("shadcn-sidebar-ready",()=>this._start(),{once:!0}):this._start()},_start(){if(this._started)return;const e=document.getElementById("sidebar");"1"===e?.getAttribute("data-shadcn-built")&&(this._started=!0,this.initTheme(),this.initSidebar(),this.initMobileDrawer(),this.initThemeToggle())},initTheme(){const e=localStorage.getItem(THEME_KEY)||"device";this.applyTheme(e)},applyTheme(e){const t="dark"===e||"device"===e&&window.matchMedia("(prefers-color-scheme: dark)").matches;document.documentElement.setAttribute("data-darkmode",t),localStorage.setItem(THEME_KEY,e),this._updateThemeIcon(t)},_updateThemeIcon(e){const t=document.getElementById("theme-toggle-btn");if(!t)return;const o=t.querySelector("img");if(!o)return;const d=document.documentElement.getAttribute("data-shadcn-media")||"/luci-static/shadcn",s=String(d).replace(/\/$/,"");o.src=s+(e?"/icons/moon.svg":"/icons/sun.svg")},initThemeToggle(){const e=document.getElementById("theme-toggle-btn");e&&(e.addEventListener("click",()=>{const e="true"===document.documentElement.getAttribute("data-darkmode");this.applyTheme(e?"light":"dark")}),window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",()=>{"device"===(localStorage.getItem(THEME_KEY)||"device")&&this.applyTheme("device")}))},initSidebar(){const e=document.getElementById("sidebar");if(!e)return;const t=getComputedStyle(document.documentElement).getPropertyValue("--breakpoint-md").trim();this._mobileQuery=window.matchMedia(`(width < ${t})`),this._mobileQuery.addEventListener("change",e=>{e.matches||this._hideCollapsedPopover()});const o="true"===localStorage.getItem(SIDEBAR_KEY);e.setAttribute("data-collapsed",o);const d=document.getElementById("sidebar-toggle-btn");d&&d.addEventListener("click",()=>{this._mobileQuery.matches?this.closeDrawer():this.toggleCollapse()}),this._initAccordion(),this._initCollapsedPopover()},toggleCollapse(){const e=document.getElementById("sidebar");if(!e)return;const t="true"!==e.getAttribute("data-collapsed");e.setAttribute("data-collapsed",t),document.documentElement.setAttribute("data-sidebar-collapsed",t?"true":"false"),document.documentElement.setAttribute("data-sidebar-anim",""),localStorage.setItem(SIDEBAR_KEY,t),this._hideCollapsedPopover(),t&&document.querySelectorAll(".sidebar-accordion-item[data-open='true']").forEach(e=>{this._closeAccordionItem(e)})},_initCollapsedPopover(){document.addEventListener("mouseover",e=>{const t=document.getElementById("sidebar");if("true"!==t?.getAttribute("data-collapsed"))return;if(e.target.closest("#sidebar-collapsed-popover"))return void clearTimeout(this._popoverTimer);const o=e.target.closest(".sidebar-accordion-item");o&&(clearTimeout(this._popoverTimer),this._showCollapsedPopover(o))}),document.addEventListener("mouseout",e=>{const t=document.getElementById("sidebar");if("true"!==t?.getAttribute("data-collapsed"))return;if(!e.target.closest(".sidebar-accordion-item")&&!e.target.closest("#sidebar-collapsed-popover"))return;const o=e.relatedTarget;o&&(o.closest(".sidebar-accordion-item")||o.closest("#sidebar-collapsed-popover"))||(this._popoverTimer=setTimeout(()=>this._hideCollapsedPopover(),100))})},_showCollapsedPopover(e){const t=e.getAttribute("data-section")||"",o=document.getElementById("sidebar-collapsed-popover");if(o&&o.getAttribute("data-section")===t)return;this._hideCollapsedPopover();const d=e.querySelectorAll(".sidebar-sub-link");if(!d.length)return;const s=e.querySelector(".sidebar-label"),i=s?s.textContent.trim():"",r=document.createElement("div");if(r.id="sidebar-collapsed-popover",r.className="sidebar-collapsed-popover",r.setAttribute("data-section",t),i){const e=document.createElement("div");e.className="sidebar-collapsed-popover-title",e.textContent=i,r.appendChild(e)}const a=document.createElement("ul");a.className="sidebar-collapsed-popover-list",d.forEach(e=>{const t=e.closest(".sidebar-sub-item")?.classList.contains("active"),o=document.createElement("li"),d=document.createElement("a");d.href=e.href,d.className="sidebar-collapsed-popover-link"+(t?" active":""),d.textContent=e.textContent,o.appendChild(d),a.appendChild(o)}),r.appendChild(a),document.body.appendChild(r);const n=document.getElementById("sidebar").getBoundingClientRect(),c=e.getBoundingClientRect();r.style.left=n.right+4+"px";const l=Math.min(c.top,window.innerHeight-r.offsetHeight-8);r.style.top=Math.max(8,l)+"px"},_hideCollapsedPopover(){clearTimeout(this._popoverTimer);const e=document.getElementById("sidebar-collapsed-popover");e&&e.remove()},_initAccordion(){document.addEventListener("click",e=>{const t=e.target.closest(".sidebar-nav-parent");if(!t)return;const o=document.getElementById("sidebar");if("true"===o?.getAttribute("data-collapsed"))return;const d=t.closest(".sidebaLine truncated
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! tailwindcss v4.3.0 | 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-border-style:solid;--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-font-weight:initial;--tw-tracking:initial}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:.25rem;--breakpoint-md:48rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--radius-sm:calc(var(--radius-base) * .5);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--radius-2xl:1rem;--radius-3xl:1.5rem;--shadow-sm:var(--shadow-sm);--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);--color-sidebar-fg:var(--sidebar-foreground);--color-terminal-fg:var(--terminal-foreground)}}@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-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetLine 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-border-style:solid;}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--radius-2xl:1rem;--radius-3xl:1.5rem;}}@layer base{*,:before,:after{box-sizing:border-box}body,p{margin:calc(var(--spacing) * 0)}button,input{margin:calc(var(--spacing) * 0);border-style:var(--tw-border-style);padding:calc(var(--spacing) * 0);color:inherit;font:inherit;background-color:#0000;border-width:0}::placeholder{opacity:1}[hidden]{display:none!important}}@layer components,utilities;@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-wght-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--background:oklch(98.5% .004 275);--foreground:oklch(18% .02 275);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(55% .2 25);--border:oklch(89.65% .0058 275);--muted-foreground:oklch(52.62% .0131 275);--primary-hover:oklch(62.7% .158 275);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(55% .2 25/.08);--destructive-a30:oklch(55% .2 25/.3);--foreground-a5:oklch(18% .02 275/.05);--font-sans:"Inter Variable", ui-sans-serif, system-ui, sans-serif;}[data-darkmode=true]{--background:oklch(15.9% .005 264);--foreground:oklch(90% .006 264);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(63% .16 25);--border:oklch(26.27% .0051 264);--muted-foreground:oklch(58.14% .0056 264);--primary-hover:oklch(62.7% .158 275);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(63% .16 25/.08);--destructive-a30:oklch(63% .16 25/.3);--foreground-a5:oklch(90% .006 264/.05);}html{color-scheme:light}html[data-darkmode=true]{color-scheme:dark}.card-header img,.login-input-icon img,.login-pw-toggle img,img.shadcn-icon{filter:brightness(0)saturate()}html[data-darkmode=true] .card-header img,html[data-darkmode=true] .login-input-icon img,html[data-darkmode=true] .login-pw-toggle img,html[data-darkmode=true] img.shadcn-icon{filter:brightness(0)saturate()invert()}.card-header img,img.shadcn-icon,#sidebar img.shadcn-icon{opacity:.65}html[data-darkmode=true] #sidebar img.shadcn-icon{opacity:.7}.card-header button:hover img,#sidebar .sidebar-nav-item.active img,#sidebar .sidebar-accordion-item.active img,#sidebar .sidebar-nav-parent:hover img{opacity:1}html{background-color:var(--background);font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--foreground)}@keyframes fadeSlideIn{to{opacity:1;transform:translateY(0)}}@keyframes slideRightIn{to{opacity:1;transfLine truncated
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! tailwindcss v4.3.0 | 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-border-style:solid;--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-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-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-duration:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-ease:initial;--tw-content:"";--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-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--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-space-y-reverse:0;--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:.25rem;--breakpoint-md:48rem;--container-xs:20rem;--container-2xl:42rem;--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);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .5);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--shadow-sm:var(--shadow-sm);--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-sm:8px;--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);--color-sidebar-fg:var(--sidebar-foreground);--color-terminal-fg:var(--terminal-foreground)}}@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-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherLine 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-border-style:solid;--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-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-content:"";--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--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-space-y-reverse:0;--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-border-spacing-x:0;--tw-border-spacing-y:0}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--font-mono:var(--font-mono);--spacing:.25rem;--breakpoint-md:48rem;--container-xs:20rem;--container-2xl:42rem;--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);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--radius-sm:calc(var(--radius-base) * .5);--radius-md:calc(var(--radius-base) * .75);--radius-lg:var(--radius-base);--radius-xl:calc(var(--radius-base) * 1.5);--shadow-sm:var(--shadow-sm);--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:calc(var(--radius-base) * .5);--color-sidebar-fg:var(--sidebar-foreground);--color-terminal-fg:var(--terminal-foreground)}}@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-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-Line truncated
|
||||
@@ -195,7 +195,7 @@
|
||||
aria-label="{{ _('Type to filter…') }}"
|
||||
>
|
||||
<span class="cmdk-trigger-label">{{ _('Type to filter…') }}</span>
|
||||
<kbd class="cmdk-trigger-key" hidden></kbd>
|
||||
<kbd class="cmdk-trigger-key">{{ match(http.getenv('HTTP_USER_AGENT') ?? '', /Mac|iP(ad|hone|od)/) ? '⌘K' : 'Ctrl+K' }}</kbd>
|
||||
</button>
|
||||
<div id="indicators" class="topbar-indicators"></div>
|
||||
<button
|
||||
|
||||
Reference in new issue
Block a user