Update VPN proxy client behavior
Build and Deploy Gateway / build-and-push (push) Successful in 18s
Build and Deploy Gateway / deploy (push) Successful in 13s

This commit is contained in:
2026-08-09 11:55:02 +03:00
parent 71ede44be0
commit 90433d7cd8
27 changed files with 770 additions and 254 deletions
+23 -3
View File
@@ -8,7 +8,7 @@
grid-column: 2;
display: grid;
justify-items: center;
gap: 18px;
gap: 12px;
text-align: center;
animation: client-power-arrive 850ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@@ -36,6 +36,22 @@
border-radius: 50%;
}
.client-power-section.is-gateway .client-power-control,
.client-power-section.is-gateway .client-power {
width: 68px;
height: 68px;
}
.client-power-section.is-gateway .client-power svg {
width: 30px;
height: 30px;
}
.client-power-section.is-gateway .client-power::before {
width: 44px;
height: 44px;
}
.client-power-control:focus-visible {
outline: 1px solid color-mix(in oklch, var(--client-accent) 64%, transparent);
outline-offset: 5px;
@@ -188,7 +204,7 @@
min-height: 44px;
display: grid;
place-items: center;
margin-top: 8px;
margin-top: 0;
}
.client-state-detail > * {
@@ -209,6 +225,10 @@
transition: opacity 260ms ease, filter 420ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-power-section.is-gateway .client-duration {
font-size: 15px;
}
.client-duration.is-active {
opacity: 1;
filter: blur(0);
@@ -324,7 +344,7 @@
justify-items: center;
gap: 5px;
width: 240px;
margin-top: 6px;
margin-top: 10px;
}
.client-proxies.is-gateway {
+90 -10
View File
@@ -746,12 +746,27 @@
stroke: var(--client-accent);
}
.client-device-traffic-lines .is-download {
stroke: var(--client-accent);
}
.client-device-traffic-lines .is-upload {
stroke: color-mix(in oklch, var(--client-accent) 42%, var(--client-text));
stroke-dasharray: 5 4;
}
.client-device-traffic-lines path.is-new {
stroke-dasharray: 1;
stroke-dashoffset: 1;
animation: client-device-traffic-line-draw 620ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.client-device-traffic-lines path.is-new.is-upload {
stroke-dasharray: 0.08 0.05;
stroke-dashoffset: 0;
animation: none;
}
.client-device-traffic-lines .is-point {
stroke-width: 3;
stroke-linecap: round;
@@ -775,6 +790,14 @@
stroke: var(--client-accent);
}
.client-device-traffic-cursor .is-point.is-download {
stroke: var(--client-accent);
}
.client-device-traffic-cursor .is-point.is-upload {
stroke: color-mix(in oklch, var(--client-accent) 42%, var(--client-text));
}
.client-device-traffic-time {
grid-column: 2;
grid-row: 2;
@@ -817,6 +840,10 @@
color: var(--client-accent);
}
.client-device-traffic-point-tooltip .is-upload {
color: color-mix(in oklch, var(--client-accent) 56%, oklch(0.92 0.008 145));
}
@keyframes client-device-traffic-shift {
from { opacity: 0.72; transform: translateX(calc(100% / var(--sample-count))); }
to { opacity: 1; transform: translateX(0); }
@@ -951,14 +978,14 @@
grid-column: 1 / -1;
grid-row: 2;
justify-self: center;
width: min(480px, calc(100% - 24px));
width: min(860px, calc(100% - 48px));
display: grid;
gap: 6px;
gap: 10px;
text-align: center;
}
.client-gateway-summary-kicker,
.client-gateway-traffic-heading > span {
.client-gateway-traffic-total > small {
color: var(--client-muted);
font-size: 9px;
font-weight: 700;
@@ -967,29 +994,59 @@
}
.client-gateway-traffic-heading {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
display: grid;
grid-template-columns: auto minmax(0, 1fr);
align-items: end;
gap: 20px;
text-align: left;
}
.client-gateway-traffic-heading strong {
.client-gateway-traffic-total {
display: flex;
align-items: baseline;
gap: 10px;
white-space: nowrap;
}
.client-gateway-traffic-total > small {
display: block;
}
.client-gateway-traffic-total > strong {
color: var(--client-text);
font-size: 15px;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
.client-gateway-traffic-speed {
min-width: 0;
display: flex;
justify-self: end;
gap: 18px;
font-size: 11px;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.client-gateway-traffic-speed .is-download {
color: var(--client-accent);
}
.client-gateway-traffic-speed .is-upload {
color: color-mix(in oklch, var(--client-accent) 48%, var(--client-text));
}
.client-gateway-traffic-chart {
width: 100%;
min-height: 88px;
min-height: 180px;
}
.client-gateway-traffic-chart .client-device-traffic-chart {
grid-column: auto;
grid-row: auto;
width: 100%;
height: 88px;
height: 180px;
}
.client-gateway-traffic-freshness {
@@ -1002,3 +1059,26 @@
.client-gateway-traffic-freshness.is-stale {
color: oklch(0.68 0.14 72);
}
@media (max-width: 560px) {
.client-gateway-summary {
width: min(100%, calc(100vw - 52px));
}
.client-gateway-traffic-heading {
grid-template-columns: 1fr;
gap: 6px;
}
.client-gateway-traffic-speed {
justify-self: start;
gap: 14px;
font-size: 10px;
}
.client-gateway-traffic-chart,
.client-gateway-traffic-chart .client-device-traffic-chart {
min-height: 130px;
height: 130px;
}
}
+94 -25
View File
@@ -1,6 +1,6 @@
.client-icon-tooltip {
width: 16px;
height: 16px;
width: 32px;
height: 32px;
display: grid;
place-items: center;
}
@@ -23,8 +23,8 @@
.client-subscription-refresh,
.client-subscription-delete {
width: 16px;
height: 16px;
width: 32px;
height: 32px;
display: grid;
place-items: center;
padding: 0;
@@ -247,9 +247,37 @@
}
.client-subscription-heading {
display: flex;
width: 100%;
display: grid;
grid-template-columns: 64px minmax(0, 1fr) 64px;
align-items: center;
gap: 8px;
}
.client-subscription-status {
grid-column: 2;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
color: var(--client-accent);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.client-subscription-status i {
width: 5px;
height: 5px;
border-radius: 50%;
background: currentColor;
box-shadow: 0 0 8px currentColor;
}
.client-subscription-actions {
grid-column: 3;
display: flex;
justify-content: flex-end;
}
.client-subscription-domain-button {
@@ -264,6 +292,11 @@
cursor: pointer;
}
.client-subscription-domain-button .client-subscription-label {
display: block;
margin-bottom: 3px;
}
.client-subscription-summary {
min-width: 0;
max-width: 100%;
@@ -272,11 +305,11 @@
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
gap: 5px;
padding: 0 2px;
color: var(--client-muted);
text-align: center;
cursor: pointer;
cursor: default;
overflow: visible;
}
@@ -288,19 +321,21 @@
}
.client-subscription-label {
font-size: 12px;
color: var(--client-muted);
font-size: 9px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
letter-spacing: 0.1em;
}
.client-subscription-domain-button strong {
display: block;
overflow: hidden;
color: var(--client-text);
font-size: 28px;
font-weight: 600;
letter-spacing: -0.055em;
opacity: 0.88;
font-size: 30px;
font-weight: 650;
letter-spacing: -0.05em;
opacity: 0.96;
text-shadow: 0 0 20px color-mix(in oklch, var(--client-accent) 24%, transparent);
transition: opacity 300ms cubic-bezier(0.16, 1, 0.3, 1), text-shadow 300ms cubic-bezier(0.16, 1, 0.3, 1), transform 300ms cubic-bezier(0.16, 1, 0.3, 1);
text-overflow: ellipsis;
@@ -314,24 +349,32 @@
}
.client-usage {
width: min(100%, 250px);
width: min(100%, 280px);
min-height: 76px;
display: grid;
gap: 7px;
margin: -10px auto 0;
align-content: start;
gap: 9px;
margin: -6px auto 0;
color: var(--client-muted);
text-align: center;
}
.client-usage > span {
.client-usage-summary {
display: grid;
gap: 3px;
}
.client-usage-summary > span,
.client-usage-details > span {
font-size: 9px;
font-weight: 700;
letter-spacing: 0.08em;
letter-spacing: 0.1em;
text-transform: uppercase;
}
.client-usage > strong {
.client-usage-summary > strong {
color: var(--client-text);
font-size: 14px;
font-size: 16px;
font-weight: 600;
transition: color 700ms ease, filter 700ms ease, text-shadow 700ms ease;
}
@@ -340,7 +383,7 @@
animation: client-usage-glow 1100ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-usage.is-updated > strong {
.client-usage.is-updated .client-usage-summary > strong {
color: var(--client-accent);
filter: brightness(1.25);
text-shadow: 0 0 8px var(--client-accent), 0 0 22px color-mix(in oklch, var(--client-accent) 70%, transparent);
@@ -358,7 +401,7 @@
30% { box-shadow: 0 0 6px var(--client-accent), 0 0 20px var(--client-accent); filter: brightness(1.45); }
}
.client-usage > strong small {
.client-usage-summary > strong small {
color: var(--client-muted);
font-size: 10px;
font-weight: 500;
@@ -379,13 +422,33 @@
}
.client-usage-details {
display: flex;
min-height: 24px;
display: grid;
grid-template-columns: auto auto;
justify-content: center;
gap: 12px;
align-items: baseline;
column-gap: 6px;
font-size: 9px;
white-space: nowrap;
}
.client-usage-details > span {
grid-column: 1 / -1;
font-size: 8px;
opacity: 0.72;
}
.client-usage-details > strong {
color: var(--client-text);
font-size: 10px;
font-weight: 600;
}
.client-usage-details > small {
color: var(--client-muted);
font-size: 9px;
}
.client-inline-error.is-subscription {
top: calc(100% + 2px);
}
@@ -419,3 +482,9 @@
transform: translateY(-2px);
}
}
@media (prefers-reduced-motion: reduce) {
.client-usage-summary > strong {
transition: none;
}
}
+44 -3
View File
@@ -189,6 +189,36 @@
gap: 9px;
}
.harbor-brand-content > svg,
.harbor-brand-name > strong,
.harbor-brand-name > em,
.harbor-mode-stack {
transition: filter 760ms ease, text-shadow 760ms ease, transform 760ms cubic-bezier(0.16, 1, 0.3, 1);
transform: translateY(0);
}
.harbor-brand-name > strong,
.harbor-brand-name > em {
display: inline-block;
}
.harbor-brand.is-connected .harbor-brand-content > svg {
filter: drop-shadow(0 0 5px color-mix(in oklch, var(--client-accent) 44%, transparent));
}
.harbor-brand.is-connected .harbor-brand-content > svg,
.harbor-brand.is-connected .harbor-brand-name > strong,
.harbor-brand.is-connected .harbor-brand-name > em,
.harbor-brand.is-connected .harbor-mode-stack {
transform: translateY(-4px);
}
.harbor-brand.is-connected .harbor-brand-name > strong,
.harbor-brand.is-connected .harbor-brand-name > em,
.harbor-brand.is-connected .harbor-mode-stack {
text-shadow: 0 0 12px color-mix(in oklch, var(--client-accent) 34%, transparent);
}
.client-shell.is-intro .harbor-brand-content {
animation: harbor-startup-brand 760ms cubic-bezier(0.16, 1, 0.3, 1);
}
@@ -367,6 +397,10 @@
animation: harbor-mode-arrows-discovered 260ms 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
.harbor-brand.is-connected .harbor-mode-swap {
transform: translateY(calc(-50% - 4px));
}
@keyframes harbor-mode-arrows-discovered {
from {
opacity: 0;
@@ -519,8 +553,9 @@
}
.client-panel.is-gateway-home {
grid-template-rows: minmax(0, 1fr) auto;
row-gap: 28px;
min-height: max(620px, calc(100dvh - 80px));
grid-template-rows: minmax(260px, 1fr) auto;
row-gap: 36px;
}
.client-gateway-route-summary {
@@ -558,7 +593,7 @@
}
.client-panel.is-gateway-home .client-state-copy {
min-height: 136px;
min-height: 84px;
}
@media (min-width: 921px) {
@@ -590,6 +625,12 @@
padding: 110px 6px 72px;
}
.client-panel.is-gateway-home {
min-height: calc(100dvh - 40px);
grid-template-rows: minmax(320px, 1fr) auto;
gap: 32px;
}
.client-power-section,
.client-form,
.client-panel.is-setup .client-form {
+12
View File
@@ -118,6 +118,11 @@
stroke-dashoffset: 0;
}
.client-device-traffic-lines path.is-new {
stroke-dashoffset: 0;
animation: none;
}
.client-local-rules,
.client-local-rules-toggle,
.client-local-rules-toggle svg,
@@ -151,6 +156,13 @@
animation: none;
}
.harbor-brand-content > svg,
.harbor-brand-name > strong,
.harbor-brand-name > em,
.harbor-mode-stack {
transition: none;
}
.client-shell.is-intro .client-panel,
.client-shell.is-intro .client-secondary-menu,
.client-shell.is-intro .harbor-versions {