Polish routing rules UI and normalize pasted values
Build and Deploy Gateway / build-and-push (push) Successful in 22s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-17 16:27:25 +03:00
parent bc86741397
commit f3be0b2fd0
11 changed files with 254 additions and 97 deletions
+122 -39
View File
@@ -102,6 +102,7 @@
.client-local-rule {
--client-rule-drag-y: 0px;
--client-rule-route-color: var(--client-accent);
position: relative;
isolation: isolate;
min-width: 0;
@@ -110,8 +111,28 @@
align-items: center;
column-gap: 6px;
padding: 4px 0;
background: linear-gradient(90deg, color-mix(in oklch, var(--client-rule-route-color) 8%, transparent), transparent 58%);
animation: client-row-enter 560ms cubic-bezier(0.16, 1, 0.3, 1) both;
transition: background-color 180ms ease, box-shadow 180ms ease, opacity 260ms ease, filter 360ms ease;
transition: background 320ms ease, box-shadow 180ms ease, opacity 260ms ease, filter 360ms ease;
}
.client-local-rule[data-route='direct'] {
--client-rule-route-color: light-dark(oklch(0.52 0.11 240), oklch(0.76 0.1 240));
}
.client-local-rule::after {
content: '';
position: absolute;
top: 10px;
bottom: 10px;
left: 0;
width: 2px;
border-radius: 999px;
background: var(--client-rule-route-color);
box-shadow: 0 0 7px color-mix(in oklch, var(--client-rule-route-color) 44%, transparent);
opacity: 0.78;
pointer-events: none;
transition: background 320ms ease, box-shadow 320ms ease, opacity 260ms ease;
}
.client-local-rule + .client-local-rule::before {
@@ -140,23 +161,46 @@
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
border: 1px solid currentColor;
background: transparent;
opacity: 0.58;
transition: color 280ms ease, opacity 280ms ease, filter 360ms ease, transform 280ms cubic-bezier(0.16, 1, 0.3, 1);
transition: color 280ms ease, border 280ms ease, border-radius 280ms ease, background 280ms ease, opacity 280ms ease, filter 360ms ease, transform 280ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rule.is-active .client-rule-status-dot {
color: var(--client-accent);
border: 0;
background: currentColor;
opacity: 1;
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-accent) 52%, transparent));
transform: scale(1.14);
}
.client-local-rule.is-pending .client-rule-status-dot,
.client-local-rule.is-unsaved .client-rule-status-dot {
width: 7px;
height: 7px;
border: 1.5px solid currentColor;
border-radius: 1px;
color: var(--client-warning, oklch(0.72 0.12 72));
opacity: 1;
filter: drop-shadow(0 0 4px color-mix(in oklch, var(--client-warning, oklch(0.72 0.12 72)) 44%, transparent));
transform: rotate(45deg);
}
.client-local-rule.is-pending .client-rule-status-dot {
width: 8px;
height: 8px;
border: 1.5px dashed currentColor;
color: var(--client-warning, oklch(0.72 0.12 72));
opacity: 1;
}
.client-local-rule.is-disabled .client-rule-status-dot {
width: 8px;
height: 2px;
border: 0;
border-radius: 999px;
background: var(--client-muted);
transform: none;
}
.client-local-rule.is-disabled > :not(.client-rule-handle):not(.client-row-delete):not(.client-delete-strike) {
@@ -164,11 +208,16 @@
filter: saturate(0);
}
.client-local-rule.is-disabled::after {
opacity: 0.22;
filter: saturate(0);
}
.client-local-rule.is-dragging {
z-index: 6;
animation: none;
background: color-mix(in oklch, var(--client-accent) 5%, transparent);
box-shadow: 0 10px 28px color-mix(in oklch, var(--client-accent) 12%, transparent);
background: linear-gradient(90deg, color-mix(in oklch, var(--client-rule-route-color) 13%, transparent), transparent 72%);
box-shadow: 0 10px 28px color-mix(in oklch, var(--client-rule-route-color) 16%, transparent);
transform: translateY(var(--client-rule-drag-y));
}
@@ -177,6 +226,13 @@
animation: client-row-leave 820ms cubic-bezier(0.7, 0, 0.84, 0) both;
}
.client-local-rule-enabled-wrap {
width: 24px;
height: 44px;
display: grid;
place-items: center;
}
.client-local-rule-enabled {
width: 24px;
height: 32px;
@@ -228,43 +284,42 @@
}
.client-local-rule-enabled svg {
width: 18px;
height: 18px;
width: 20px;
height: 20px;
overflow: visible;
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-linejoin: round;
transition: color 260ms ease, filter 360ms ease, transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rule-enabled circle {
stroke-width: 1.4;
transition: fill 320ms ease, stroke 260ms ease;
.client-rule-switch-track {
fill: color-mix(in oklch, var(--client-muted) 16%, transparent);
stroke: currentColor;
stroke-width: 1;
transition: fill 260ms ease, stroke 260ms ease;
}
.client-rule-check {
stroke-width: 1.8;
stroke-dasharray: 12;
stroke-dashoffset: 12;
transition: opacity 160ms ease, stroke-dashoffset 360ms cubic-bezier(0.16, 1, 0.3, 1);
.client-rule-switch-thumb {
fill: currentColor;
stroke: none;
transform-box: fill-box;
transform-origin: center;
transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rule-enabled[aria-checked='true'] {
color: var(--client-accent);
color: var(--client-rule-route-color);
}
.client-local-rule-enabled[aria-checked='true'] svg {
filter: drop-shadow(0 0 7px color-mix(in oklch, var(--client-accent) 48%, transparent));
transform: scale(1.08);
filter: drop-shadow(0 0 7px color-mix(in oklch, var(--client-rule-route-color) 42%, transparent));
}
.client-local-rule-enabled[aria-checked='true'] circle {
fill: color-mix(in oklch, var(--client-accent) 12%, transparent);
.client-local-rule-enabled[aria-checked='true'] .client-rule-switch-track {
fill: color-mix(in oklch, var(--client-rule-route-color) 20%, transparent);
}
.client-local-rule-enabled[aria-checked='true'] .client-rule-check {
stroke-dashoffset: 0;
.client-local-rule-enabled[aria-checked='true'] .client-rule-switch-thumb {
transform: translateX(8px);
}
.client-rule-type {
@@ -422,15 +477,11 @@
border: 0;
border-radius: 50%;
background: transparent;
color: var(--client-muted);
color: var(--client-rule-route-color);
cursor: pointer;
transition: color 220ms ease, filter 320ms ease, background 220ms ease;
}
.client-rule-outbound button.is-direct {
color: var(--client-accent);
}
.client-rule-outbound svg {
width: 20px;
height: 20px;
@@ -448,15 +499,13 @@
.client-rule-outbound button:focus-visible {
outline: 0;
background: color-mix(in oklch, var(--client-accent) 10%, transparent);
color: var(--client-accent);
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-accent) 36%, transparent));
background: color-mix(in oklch, var(--client-rule-route-color) 10%, transparent);
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-rule-route-color) 36%, transparent));
}
.client-rule-outbound button:hover:not(:disabled) {
background: color-mix(in oklch, var(--client-accent) 10%, transparent);
color: var(--client-accent);
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-accent) 36%, transparent));
background: color-mix(in oklch, var(--client-rule-route-color) 10%, transparent);
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-rule-route-color) 36%, transparent));
}
.client-rule-outbound button.is-direct:hover:not(:disabled) svg,
@@ -490,8 +539,42 @@
text-transform: var(--type-control-transform);
}
.client-local-rules-note {
.client-local-rules-help {
grid-column: 1 / -1;
margin-top: 4px;
}
.client-local-rules-help summary {
width: fit-content;
color: var(--client-muted);
font: var(--type-label);
letter-spacing: var(--type-label-tracking);
text-transform: var(--type-label-transform);
cursor: pointer;
transition: color 220ms ease, text-shadow 300ms ease;
}
.client-local-rules-help summary:hover,
.client-local-rules-help summary:focus-visible {
outline: 0;
color: var(--client-accent);
text-shadow: 0 0 9px color-mix(in oklch, var(--client-accent) 34%, transparent);
}
.client-local-rules-help > div {
max-width: 48ch;
display: grid;
gap: 8px;
margin-top: 10px;
padding: 10px 12px;
border-radius: 10px;
background: color-mix(in oklch, var(--client-accent) 5%, transparent);
animation: client-local-rules-notice 280ms ease both;
}
.client-local-rules-help > div p {
max-width: 48ch;
margin: 0;
color: var(--client-muted);
font: var(--type-label);
letter-spacing: var(--type-label-tracking);