Keep the server picker simple by default
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 15s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-07-12 16:30:53 +03:00
parent 9a539409f0
commit d551d41b71
5 changed files with 32 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({ export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.7.20', macClient: '0.7.21',
gatewayClient: '0.7.20', gatewayClient: '0.7.21',
gatewayBackend: '0.7.1', gatewayBackend: '0.7.1',
}); });

View File

@@ -10,6 +10,7 @@ import {
const FAVORITES_KEY = 'harbor-server-favorites'; const FAVORITES_KEY = 'harbor-server-favorites';
const RECENT_KEY = 'harbor-server-recent'; const RECENT_KEY = 'harbor-server-recent';
const AUTO_KEY = 'harbor-server-auto'; const AUTO_KEY = 'harbor-server-auto';
const SIMPLE_SERVER_LIMIT = 5;
function readList(key) { function readList(key) {
try { try {
@@ -188,7 +189,7 @@ export function ServerPicker({
const simpleServers = [ const simpleServers = [
...(selected ? [selected] : []), ...(selected ? [selected] : []),
...servers.filter(({ id }) => id !== selectedServerId), ...servers.filter(({ id }) => id !== selectedServerId),
].slice(0, SERVER_RESULT_WINDOW); ].slice(0, SIMPLE_SERVER_LIMIT);
return <section className="client-servers is-scalable" aria-label="Выберите сервер"> return <section className="client-servers is-scalable" aria-label="Выберите сервер">
{prompt && <span className="client-server-prompt">Выберите сервер</span>} {prompt && <span className="client-server-prompt">Выберите сервер</span>}
@@ -274,7 +275,7 @@ export function ServerPicker({
{autoActive ? serverHealthText(pings[selectedServerId]) : 'Первый стабильный сервер'} {autoActive ? serverHealthText(pings[selectedServerId]) : 'Первый стабильный сервер'}
</small> </small>
</button> </button>
{selected && !autoActive && <ServerRow {selected && <ServerRow
server={selected} server={selected}
selected selected
favorite={favorites.includes(selected.id)} favorite={favorites.includes(selected.id)}

View File

@@ -2057,10 +2057,7 @@ p {
min-width: 0; min-width: 0;
width: min(360px, 100%); width: min(360px, 100%);
max-height: min(640px, calc(100vh - 120px)); max-height: min(640px, calc(100vh - 120px));
overflow-y: auto; overflow: visible;
overscroll-behavior: contain;
scrollbar-width: thin;
scrollbar-color: rgba(125, 249, 255, 0.34) transparent;
display: grid; display: grid;
gap: 36px; gap: 36px;
transition: opacity 650ms cubic-bezier(0.16, 1, 0.3, 1), filter 650ms cubic-bezier(0.16, 1, 0.3, 1); transition: opacity 650ms cubic-bezier(0.16, 1, 0.3, 1), filter 650ms cubic-bezier(0.16, 1, 0.3, 1);
@@ -2919,8 +2916,17 @@ p {
overflow-y: auto; overflow-y: auto;
overscroll-behavior: contain; overscroll-behavior: contain;
padding-right: 5px; padding-right: 5px;
scrollbar-width: thin; scrollbar-width: none;
scrollbar-color: color-mix(in oklch, var(--client-accent) 36%, transparent) transparent; }
.client-server-scroll::-webkit-scrollbar {
display: none;
}
.client-server-mode-panel.is-simple .client-server-scroll {
max-height: none;
overflow: visible;
padding-right: 0;
} }
.client-server-scroll.is-leaving { .client-server-scroll.is-leaving {

View File

@@ -20,6 +20,7 @@ test('desktop layout keeps the power control on a symmetric center axis', () =>
assert.match(power, /grid-column:\s*2/); assert.match(power, /grid-column:\s*2/);
assert.match(form, /grid-column:\s*3/); assert.match(form, /grid-column:\s*3/);
assert.match(form, /position:\s*static/); assert.match(form, /position:\s*static/);
assert.match(form, /overflow:\s*visible/);
assert.doesNotMatch(form, /\bleft\s*:|translateX|transition:[^;]*(?:left|width|transform)/); assert.doesNotMatch(form, /\bleft\s*:|translateX|transition:[^;]*(?:left|width|transform)/);
assert.match(styles, /\.client-panel\.has-subscription \.client-form \{[\s\S]*height:\s*var\(--client-work-height\)/); assert.match(styles, /\.client-panel\.has-subscription \.client-form \{[\s\S]*height:\s*var\(--client-work-height\)/);
assert.match(rule('.client-form-content'), /align-content:\s*start/); assert.match(rule('.client-form-content'), /align-content:\s*start/);
@@ -29,6 +30,17 @@ test('desktop layout keeps the power control on a symmetric center axis', () =>
assert.match(styles, /\.client-panel\.has-subscription \.client-form-content \{[\s\S]*padding-top:\s*var\(--client-power-top\)/); assert.match(styles, /\.client-panel\.has-subscription \.client-form-content \{[\s\S]*padding-top:\s*var\(--client-power-top\)/);
}); });
test('server rows scroll without moving the subscription column or showing a scrollbar', () => {
const scroll = rule('.client-server-scroll');
const simpleScroll = rule('.client-server-mode-panel.is-simple .client-server-scroll');
assert.match(scroll, /overflow-y:\s*auto/);
assert.match(scroll, /scrollbar-width:\s*none/);
assert.match(styles, /\.client-server-scroll::-webkit-scrollbar\s*\{[\s\S]*display:\s*none/);
assert.match(simpleScroll, /max-height:\s*none/);
assert.match(simpleScroll, /overflow:\s*visible/);
});
test('tablet and mobile regions use normal flow with viewport-safe widths', () => { test('tablet and mobile regions use normal flow with viewport-safe widths', () => {
const responsive = /@media \(max-width: 920px\) \{([\s\S]*?)\n\}\n\n@media \(max-width: 560px\)/.exec(styles)?.[1] || ''; const responsive = /@media \(max-width: 920px\) \{([\s\S]*?)\n\}\n\n@media \(max-width: 560px\)/.exec(styles)?.[1] || '';

View File

@@ -63,5 +63,7 @@ test('server picker starts simple and reveals advanced controls on demand', () =
assert.match(picker, /client-server-mode-panel is-advanced/); assert.match(picker, /client-server-mode-panel is-advanced/);
assert.match(picker, /inert={advanced \? true : undefined}/); assert.match(picker, /inert={advanced \? true : undefined}/);
assert.match(picker, /inert={!advanced \? true : undefined}/); assert.match(picker, /inert={!advanced \? true : undefined}/);
assert.match(picker, /\.slice\(0, SERVER_RESULT_WINDOW\)/); assert.match(picker, /const SIMPLE_SERVER_LIMIT = 5/);
assert.match(picker, /\.slice\(0, SIMPLE_SERVER_LIMIT\)/);
assert.match(picker, /\{selected && <ServerRow/);
}); });