Add local routing rules to Harbor
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 17s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-07-11 21:52:03 +03:00
parent 306a9b8ced
commit a0c66edb02
17 changed files with 823 additions and 26 deletions

View File

@@ -717,6 +717,317 @@ p {
visibility: hidden;
}
.client-local-rules-toggle {
position: fixed;
top: 50%;
right: 14px;
z-index: 30;
width: 42px;
height: 54px;
display: grid;
place-items: center;
padding: 0;
border: 0;
background: transparent;
color: var(--client-muted);
cursor: pointer;
transform: translateY(-50%);
transition: color 240ms ease, transform 440ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rules-toggle svg {
width: 23px;
height: 23px;
fill: none;
stroke: currentColor;
stroke-width: 1.6;
stroke-linecap: round;
stroke-linejoin: round;
transition: color 240ms ease, filter 360ms ease, transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rules-toggle span {
position: absolute;
right: 48px;
width: max-content;
color: var(--client-text);
font: 700 10px/1.2 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, monospace;
letter-spacing: 0.04em;
opacity: 0;
filter: blur(5px);
pointer-events: none;
transform: translateX(8px);
transition: opacity 350ms ease, filter 350ms ease, transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rules-toggle:hover,
.client-local-rules-toggle:focus-visible {
outline: none;
color: var(--client-accent);
transform: translateY(-50%) translateX(-3px);
}
.client-local-rules-toggle:hover svg,
.client-local-rules-toggle:focus-visible svg {
filter: drop-shadow(0 0 7px color-mix(in oklch, var(--client-accent) 52%, transparent));
}
.client-local-rules-toggle:hover span,
.client-local-rules-toggle:focus-visible span {
opacity: 1;
filter: blur(0);
transform: translateX(0);
}
.client-local-rules-toggle.is-open {
color: var(--client-accent);
}
.client-local-rules-toggle.is-open svg {
transform: rotate(90deg);
}
.client-local-rules-toggle.is-open span {
opacity: 0;
visibility: hidden;
}
.client-local-rules {
position: fixed;
inset: 0 0 0 auto;
z-index: 20;
width: min(480px, 100vw);
overflow-y: auto;
background: color-mix(in oklch, var(--client-bg) 99%, var(--client-panel));
color: var(--client-text);
box-shadow: -26px 0 72px oklch(0.09 0.015 145 / 0.12);
opacity: 0;
visibility: hidden;
transform: translateX(104%);
transition: transform 760ms cubic-bezier(0.16, 1, 0.3, 1), opacity 500ms ease, visibility 0s 760ms;
}
.client-local-rules.is-open {
opacity: 1;
visibility: visible;
transform: translateX(0);
transition-delay: 0s;
}
.client-local-rules-sheet {
min-height: 100%;
padding: 54px 72px 72px 34px;
font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, monospace;
}
.client-local-rules-header {
display: grid;
gap: 9px;
margin: 0 8px 34px;
}
.client-local-rules-header > span,
.client-local-rules-group > span {
color: var(--client-muted);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.client-local-rules-header h2 {
font-size: 22px;
letter-spacing: -0.045em;
}
.client-local-rules-header p {
max-width: 46ch;
color: var(--client-muted);
font-size: 11px;
line-height: 1.7;
}
.client-local-rules-form,
.client-local-rules-group,
.client-local-rules-list {
display: grid;
}
.client-local-rules-form {
gap: 28px;
}
.client-local-rules-group {
gap: 11px;
}
.client-local-rules-list {
gap: 8px;
}
.client-local-rule {
min-width: 0;
display: grid;
grid-template-columns: 112px minmax(0, 1fr) 32px;
align-items: center;
gap: 8px;
padding: 8px;
border-radius: 13px;
background: color-mix(in oklch, var(--client-panel) 56%, var(--client-bg));
box-shadow: 0 10px 28px oklch(0.1 0.015 145 / 0.055);
transition: background 240ms ease, box-shadow 320ms ease, transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-local-rule:focus-within {
background: color-mix(in oklch, var(--client-panel) 74%, var(--client-bg));
box-shadow: 0 14px 34px oklch(0.1 0.015 145 / 0.09);
transform: translateY(-1px);
}
.client-local-rule.is-built-in {
grid-template-columns: 92px minmax(0, 1fr) auto;
color: var(--client-muted);
}
.client-local-rule.is-built-in strong {
font-size: 10px;
font-weight: 700;
}
.client-local-rule.is-built-in code {
color: var(--client-accent);
font: 700 13px/1 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, monospace;
text-shadow: 0 0 12px color-mix(in oklch, var(--client-accent) 38%, transparent);
}
.client-local-rule.is-built-in small {
font-size: 8px;
white-space: nowrap;
}
.client-local-rule select,
.client-local-rule input {
min-width: 0;
height: 36px;
border: 0;
border-radius: 8px;
outline: 0;
background: color-mix(in oklch, var(--client-control) 72%, transparent);
color: var(--client-text);
font-size: 10px;
}
.client-local-rule select {
padding: 0 7px;
cursor: pointer;
}
.client-local-rule input {
padding: 0 10px;
}
.client-local-rule input::placeholder {
color: color-mix(in oklch, var(--client-muted) 72%, transparent);
}
.client-local-rule select:focus-visible,
.client-local-rule input:focus-visible,
.client-local-rule button:focus-visible,
.client-local-rule-add:focus-visible,
.client-local-rules-actions button:focus-visible {
outline: 2px solid var(--client-accent);
outline-offset: 2px;
}
.client-local-rule > button {
width: 32px;
height: 32px;
padding: 0;
border: 0;
border-radius: 8px;
background: transparent;
color: var(--client-muted);
font-size: 17px;
cursor: pointer;
transition: color 180ms ease, background 180ms ease, transform 220ms ease;
}
.client-local-rule > button:hover {
background: color-mix(in oklch, oklch(0.68 0.15 28) 10%, transparent);
color: oklch(0.68 0.15 28);
transform: scale(1.08);
}
.client-local-rules-empty {
padding: 14px 4px 4px;
color: var(--client-muted);
font-size: 10px;
line-height: 1.6;
}
.client-local-rule-add {
width: fit-content;
padding: 7px 0;
border: 0;
background: transparent;
color: var(--client-accent);
font-size: 10px;
font-weight: 700;
cursor: pointer;
}
.client-local-rule-add:hover {
text-shadow: 0 0 10px color-mix(in oklch, var(--client-accent) 55%, transparent);
}
.client-local-rules-note {
padding: 12px 14px;
border-radius: 12px;
background: color-mix(in oklch, var(--client-control) 44%, transparent);
color: var(--client-muted);
font-size: 9px;
line-height: 1.65;
}
.client-inline-error.is-routing {
position: static;
width: 100%;
transform: none;
}
.client-local-rules-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
}
.client-local-rules-actions button {
padding: 10px 14px;
border: 0;
border-radius: 9px;
background: color-mix(in oklch, var(--client-control) 72%, transparent);
color: var(--client-text);
font-size: 10px;
font-weight: 700;
cursor: pointer;
transition: background 200ms ease, color 200ms ease, transform 220ms ease;
}
.client-local-rules-actions button:hover:not(:disabled) {
background: var(--client-control);
transform: translateY(-1px);
}
.client-local-rules-actions .is-primary {
background: color-mix(in oklch, var(--client-accent) 18%, var(--client-control));
color: var(--client-accent);
}
.client-local-rules-actions button:disabled {
opacity: 0.45;
cursor: wait;
}
.client-instructions {
position: fixed;
inset: 0 auto 0 0;
@@ -2197,10 +2508,43 @@ p {
left: 8px;
}
.client-local-rules-toggle {
right: 8px;
}
.client-instructions-sheet {
padding: 40px 18px 60px 58px;
}
.client-local-rules-sheet {
padding: 40px 58px 60px 18px;
}
.client-local-rules-header h2 {
font-size: 18px;
}
.client-local-rule,
.client-local-rule.is-built-in {
grid-template-columns: 1fr 32px;
}
.client-local-rule select,
.client-local-rule.is-built-in strong {
grid-column: 1 / -1;
}
.client-local-rule input,
.client-local-rule.is-built-in code {
grid-column: 1;
}
.client-local-rule.is-built-in small {
grid-column: 2;
grid-row: 2;
white-space: normal;
}
.client-instructions-header h2 {
font-size: 18px;
}
@@ -2260,6 +2604,17 @@ p {
animation: none;
}
.client-local-rules,
.client-local-rules-toggle,
.client-local-rules-toggle svg,
.client-local-rules-toggle span,
.client-local-rule,
.client-local-rule > button,
.client-local-rules-actions button {
transition: none;
animation: none;
}
.harbor-brand {
transition: none;
animation: none;