fix(settings): harden Network Torrent settings layout

- Replace uneven Torrent tabs with concise localized controls
- Align supporting text and value fields with a responsive network grid
- Preserve keyboard tab navigation and style restart notes consistently
- Keep specialized row rules after generic macOS styles to prevent cascade regressions
This commit is contained in:
NimBold
2026-08-24 07:45:14 +03:30
parent 627e24da0f
commit 45c25da98e
8 changed files with 249 additions and 37 deletions
+35 -31
View File
@@ -1003,7 +1003,7 @@ runEngineChecks(false);
<div className="settings-content-shell w-full">
<div key={activeTab} className="settings-page-transition">
<h1 className="settings-title text-text-primary">{activeTabLabel}</h1>
<div className="settings-content max-w-[720px]">
<div className={`settings-content ${activeTab === 'network' ? 'settings-content--network' : 'max-w-[720px]'}`}>
{/* Downloads Pane */}
{activeTab === 'downloads' && (
@@ -1351,18 +1351,18 @@ runEngineChecks(false);
{/* Network Pane */}
{activeTab === 'network' && (
<div className="settings-pane max-w-[720px]">
<nav className="network-settings-tabs mb-4 flex gap-1 overflow-x-auto border-b border-border-color/60" role="tablist" aria-label={t($ => $.settings.tabs.network)}>
<div className="settings-pane settings-network-pane">
<nav className="network-settings-tabs" role="tablist" aria-label={t($ => $.settings.tabs.network)}>
{networkSettingsSections.map(section => {
const label = section === 'general'
? t($ => $.settings.network.proxy)
: section === 'discovery'
? t($ => $.settings.network.torrentPeerDiscovery)
? t($ => $.settings.network.torrentTabs.discovery)
: section === 'connection'
? t($ => $.settings.network.torrentNetwork)
? t($ => $.settings.network.torrentTabs.connection)
: section === 'limits'
? t($ => $.settings.network.torrentResourceLimits)
: t($ => $.settings.network.torrentAdvanced);
? t($ => $.settings.network.torrentTabs.limits)
: t($ => $.settings.network.torrentTabs.advanced);
return (
<button
key={section}
@@ -1371,7 +1371,7 @@ runEngineChecks(false);
aria-selected={networkSection === section}
aria-controls={`network-settings-panel-${section}`}
tabIndex={networkSection === section ? 0 : -1}
className={`whitespace-nowrap border-b-2 px-3 py-2 text-xs font-medium ${networkSection === section ? 'border-accent text-text-primary' : 'border-transparent text-text-muted hover:text-text-primary'}`}
className="network-settings-tab"
onClick={() => setNetworkSection(section)}
onKeyDown={event => {
const index = networkSettingsSections.indexOf(section);
@@ -1394,8 +1394,8 @@ runEngineChecks(false);
})}
</nav>
<div id="network-settings-panel-general" role="tabpanel" aria-labelledby="network-settings-tab-general" hidden={networkSection !== 'general'} tabIndex={0}>
<h2 className="settings-section-title">{t($ => $.settings.network.proxy)}</h2>
<div id="network-settings-panel-general" className="settings-network-panel" role="tabpanel" aria-labelledby="network-settings-tab-general" hidden={networkSection !== 'general'} tabIndex={0}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.proxy)}</h2>
<div className="mac-settings-group">
<div className="mac-settings-row settings-network-row settings-choice-row">
<div className="settings-row-label">
@@ -1483,10 +1483,10 @@ runEngineChecks(false);
</div>
</div>
<div id="network-settings-panel-discovery" role="tabpanel" aria-labelledby="network-settings-tab-discovery" hidden={networkSection !== 'discovery'} tabIndex={0}>
<h2 className="settings-section-title">{t($ => $.settings.network.torrentPeerDiscovery)}</h2>
<div id="network-settings-panel-discovery" className="settings-network-panel" role="tabpanel" aria-labelledby="network-settings-tab-discovery" hidden={networkSection !== 'discovery'} tabIndex={0}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.torrentPeerDiscovery)}</h2>
<div className="mac-settings-group">
<label className="mac-settings-row cursor-default">
<label className="mac-settings-row settings-network-row cursor-default">
<div className="settings-row-label">
<span>{t($ => $.settings.network.torrentDht)}</span>
<small>{t($ => $.settings.network.torrentDhtDescription)}</small>
@@ -1498,7 +1498,7 @@ runEngineChecks(false);
className="mac-switch"
/>
</label>
<label className="mac-settings-row cursor-default">
<label className="mac-settings-row settings-network-row cursor-default">
<div className="settings-row-label">
<span>{t($ => $.settings.network.torrentDht6)}</span>
<small>{t($ => $.settings.network.torrentDht6Description)}</small>
@@ -1511,7 +1511,7 @@ runEngineChecks(false);
className="mac-switch disabled:opacity-50"
/>
</label>
<label className="mac-settings-row cursor-default">
<label className="mac-settings-row settings-network-row cursor-default">
<div className="settings-row-label">
<span>{t($ => $.settings.network.torrentIpv6Enabled)}</span>
<small>{t($ => $.settings.network.torrentIpv6EnabledDescription)}</small>
@@ -1523,7 +1523,7 @@ runEngineChecks(false);
className="mac-switch"
/>
</label>
<label className="mac-settings-row cursor-default">
<label className="mac-settings-row settings-network-row cursor-default">
<div className="settings-row-label">
<span>{t($ => $.settings.network.torrentPex)}</span>
<small>{t($ => $.settings.network.torrentPexDescription)}</small>
@@ -1535,7 +1535,7 @@ runEngineChecks(false);
className="mac-switch"
/>
</label>
<label className="mac-settings-row cursor-default">
<label className="mac-settings-row settings-network-row cursor-default">
<div className="settings-row-label">
<span>{t($ => $.settings.network.torrentLpd)}</span>
<small>{t($ => $.settings.network.torrentLpdDescription)}</small>
@@ -1547,14 +1547,15 @@ runEngineChecks(false);
className="mac-switch"
/>
</label>
<p className="settings-group-footer">
{t($ => $.settings.network.torrentPeerDiscoveryRestartNote)}
<p className="settings-group-footer settings-network-note">
<Info size={14} aria-hidden="true" />
<span>{t($ => $.settings.network.torrentPeerDiscoveryRestartNote)}</span>
</p>
</div>
</div>
<div id="network-settings-panel-connection" role="tabpanel" aria-labelledby="network-settings-tab-connection" hidden={networkSection !== 'connection'} tabIndex={0}>
<h2 className="settings-section-title">{t($ => $.settings.network.torrentNetwork)}</h2>
<div id="network-settings-panel-connection" className="settings-network-panel" role="tabpanel" aria-labelledby="network-settings-tab-connection" hidden={networkSection !== 'connection'} tabIndex={0}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.torrentNetwork)}</h2>
<div className="mac-settings-group">
<TorrentNetworkTextInput
field="torrentListenPort"
@@ -1659,6 +1660,7 @@ runEngineChecks(false);
type="checkbox"
checked={settings.torrentSeparateSeedSlots}
onChange={(event) => settings.setTorrentSeparateSeedSlots(event.target.checked)}
className="mac-switch"
aria-label={t($ => $.settings.network.torrentSeparateSeedSlots)}
/>
</div>
@@ -1679,14 +1681,15 @@ runEngineChecks(false);
aria-label={t($ => $.settings.network.torrentMaxConcurrentSeeds)}
/>
</div>
<p className="settings-group-footer">
{t($ => $.settings.network.torrentNetworkRestartNote)}
<p className="settings-group-footer settings-network-note">
<Info size={14} aria-hidden="true" />
<span>{t($ => $.settings.network.torrentNetworkRestartNote)}</span>
</p>
</div>
</div>
<div id="network-settings-panel-limits" role="tabpanel" aria-labelledby="network-settings-tab-limits" hidden={networkSection !== 'limits'} tabIndex={0}>
<h2 className="settings-section-title">{t($ => $.settings.network.torrentResourceLimits)}</h2>
<div id="network-settings-panel-limits" className="settings-network-panel" role="tabpanel" aria-labelledby="network-settings-tab-limits" hidden={networkSection !== 'limits'} tabIndex={0}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.torrentResourceLimits)}</h2>
<div className="mac-settings-group">
<div className="mac-settings-row settings-network-row">
<div className="settings-row-label">
@@ -1733,8 +1736,8 @@ runEngineChecks(false);
</div>
</div>
<div id="network-settings-panel-advanced" role="tabpanel" aria-labelledby="network-settings-tab-advanced" hidden={networkSection !== 'advanced'} tabIndex={0}>
<h2 className="settings-section-title">{t($ => $.settings.network.torrentAdvanced)}</h2>
<div id="network-settings-panel-advanced" className="settings-network-panel" role="tabpanel" aria-labelledby="network-settings-tab-advanced" hidden={networkSection !== 'advanced'} tabIndex={0}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.torrentAdvanced)}</h2>
<div className="mac-settings-group">
<div className="mac-settings-row settings-network-row">
<div className="settings-row-label">
@@ -1753,14 +1756,15 @@ runEngineChecks(false);
aria-label={t($ => $.settings.network.torrentDhtMessageTimeout)}
/>
</div>
<p className="settings-group-footer">
{t($ => $.settings.network.torrentNetworkRestartNote)}
<p className="settings-group-footer settings-network-note">
<Info size={14} aria-hidden="true" />
<span>{t($ => $.settings.network.torrentNetworkRestartNote)}</span>
</p>
</div>
</div>
<section id="network-settings-group-general-identity" role="region" aria-label={t($ => $.settings.network.identity)} hidden={networkSection !== 'general'}>
<h2 className="settings-section-title">{t($ => $.settings.network.identity)}</h2>
<section id="network-settings-group-general-identity" className="settings-network-panel" role="region" aria-label={t($ => $.settings.network.identity)} hidden={networkSection !== 'general'}>
<h2 className="settings-section-title settings-network-section-title">{t($ => $.settings.network.identity)}</h2>
<div className="mac-settings-group settings-popup-group">
<div className="mac-settings-row settings-network-row">
<div className="settings-row-label">
+6
View File
@@ -969,6 +969,12 @@ const common = {
detectedSystemProxy: 'A system proxy was detected. Normal file downloads require an HTTP or HTTPS endpoint; media downloads can use SOCKS.',
noSystemProxy: 'No usable system proxy was detected. Downloads will use no proxy.',
systemProxyReadFailed: 'System proxy configuration could not be read. Choose No Proxy or try again when it is available.',
torrentTabs: {
discovery: 'Discovery',
connection: 'Connection',
limits: 'Limits',
advanced: 'Advanced',
},
torrentPeerDiscovery: 'BitTorrent peer discovery',
torrentDht: 'IPv4 DHT and UDP trackers',
torrentDhtDescription: 'Find peers without relying only on trackers. Disabling this also disables UDP tracker support.',
+6
View File
@@ -969,6 +969,12 @@ const fa = {
detectedSystemProxy: 'یک پروکسی سیستم شناسایی شد. دانلودهای فایل عادی به یک نقطه پایانی HTTP یا HTTPS نیاز دارند؛ دانلودهای رسانه می‌توانند از SOCKS استفاده کنند.',
noSystemProxy: 'هیچ پروکسی سیستم قابل استفاده‌ای شناسایی نشد. دانلودها از هیچ پروکسی‌ای استفاده نخواهند کرد.',
systemProxyReadFailed: 'پیکربندی پروکسی سیستم قابل خواندن نیست. بدون پروکسی را انتخاب کنید یا هنگامی که در دسترس است دوباره امتحان کنید.',
torrentTabs: {
discovery: 'کشف همتا',
connection: 'اتصال',
limits: 'محدودیت‌ها',
advanced: 'پیشرفته',
},
torrentPeerDiscovery: 'کشف همتا در بیت‌تورنت',
torrentDht: 'DHT نسخه IPv4 و ترکرهای UDP',
torrentDhtDescription: 'همتاها را بدون تکیه صرف بر ترکرها پیدا می‌کند. خاموش کردن آن پشتیبانی از ترکرهای UDP را هم خاموش می‌کند.',
+6
View File
@@ -969,6 +969,12 @@ const he = {
detectedSystemProxy: 'זוהה פרוקסי מערכת. הורדות קבצים רגילות דורשות נקודת קצה של HTTP או HTTPS; הורדות מדיה יכולות להשתמש ב-SOCKS.',
noSystemProxy: 'לא זוהה פרוקסי מערכת שימושי. ההורדות לא ישתמשו בפרוקסי.',
systemProxyReadFailed: 'לא ניתן לקרוא את תצורת פרוקסי המערכת. בחר "ללא פרוקסי" או נסה שוב כשהיא תהיה זמינה.',
torrentTabs: {
discovery: 'גילוי',
connection: 'חיבור',
limits: 'מגבלות',
advanced: 'מתקדם',
},
torrentPeerDiscovery: 'גילוי עמיתים ב-BitTorrent',
torrentDht: 'DHT של IPv4 ומעקבי UDP',
torrentDhtDescription: 'מאתר עמיתים בלי להסתמך רק על מעקבים. השבתה מכבה גם תמיכה במעקבי UDP.',
+6
View File
@@ -969,6 +969,12 @@ const ru = {
detectedSystemProxy: 'Обнаружен системный прокси. Для обычных загрузок файлов требуется HTTP- или HTTPS-прокси; для загрузки медиа можно использовать SOCKS.',
noSystemProxy: 'Подходящий системный прокси не обнаружен. Загрузки будут выполняться без прокси.',
systemProxyReadFailed: 'Не удалось прочитать конфигурацию системного прокси. Выберите «Без прокси» или повторите попытку, когда она станет доступна.',
torrentTabs: {
discovery: 'Обнаружение',
connection: 'Подключение',
limits: 'Лимиты',
advanced: 'Дополнительно',
},
torrentPeerDiscovery: 'Обнаружение пиров BitTorrent',
torrentDht: 'IPv4 DHT и UDP-трекеры',
torrentDhtDescription: 'Ищет пиры не только через трекеры. Отключение также отключает поддержку UDP-трекеров.',
+6
View File
@@ -969,6 +969,12 @@ const uk = {
detectedSystemProxy: 'Виявлено системний проксі. Звичайні завантаження файлів вимагають HTTP або HTTPS проксі; медіазавантаження можуть використовувати SOCKS.',
noSystemProxy: 'Не виявлено придатного системного проксі. Завантаження використовуватимуть пряме підключення (без проксі).',
systemProxyReadFailed: 'Не вдалося прочитати конфігурацію системного проксі. Виберіть "Без проксі" або спробуйте ще раз, коли він стане доступним.',
torrentTabs: {
discovery: 'Пошук',
connection: 'Підключення',
limits: 'Обмеження',
advanced: 'Розширені',
},
torrentPeerDiscovery: 'Пошук пірів BitTorrent',
torrentDht: 'IPv4 DHT і UDP-трекери',
torrentDhtDescription: 'Шукає пірів не лише через трекери. Вимкнення також вимикає підтримку UDP-трекерів.',
+6
View File
@@ -969,6 +969,12 @@ const zhCN = {
detectedSystemProxy: '检测到系统代理。普通文件下载需要 HTTP 或 HTTPS 端点;媒体下载可以使用 SOCKS。',
noSystemProxy: '未检测到可用的系统代理。下载将不使用代理。',
systemProxyReadFailed: '无法读取系统代理配置。请选择“无代理”,或者在其可用时重试。',
torrentTabs: {
discovery: '发现',
connection: '连接',
limits: '限制',
advanced: '高级',
},
torrentPeerDiscovery: 'BitTorrent 对等节点发现',
torrentDht: 'IPv4 DHT 与 UDP Tracker',
torrentDhtDescription: '不只依赖 Tracker 查找节点。关闭后也会禁用 UDP Tracker 支持。',
+178 -6
View File
@@ -2747,6 +2747,170 @@ html[data-list-density="relaxed"] {
background: transparent;
}
.settings-content--network {
max-width: 860px;
}
.settings-network-pane {
width: 100%;
}
.network-settings-tabs {
display: flex;
gap: 4px;
width: 100%;
margin-bottom: 24px;
padding: 4px;
overflow-x: auto;
border: 1px solid hsl(var(--border-color));
border-radius: 10px;
background: hsl(var(--surface-raised) / 0.72);
scrollbar-width: none;
}
.network-settings-tabs::-webkit-scrollbar {
display: none;
}
.network-settings-tab {
flex: 1 1 0;
min-width: 0;
min-height: 36px;
padding: 8px 12px;
border: 0;
border-radius: 7px;
background: transparent;
color: hsl(var(--text-muted));
font-size: 12px;
font-weight: 550;
line-height: 1.2;
text-align: center;
white-space: nowrap;
transition:
background-color 140ms ease,
box-shadow 140ms ease,
color 140ms ease;
}
.network-settings-tab:hover {
background: hsl(var(--item-hover));
color: hsl(var(--text-primary));
}
.network-settings-tab[aria-selected="true"] {
background: hsl(var(--accent-color) / 0.16);
box-shadow: inset 0 0 0 1px hsl(var(--accent-color) / 0.28);
color: hsl(var(--text-primary));
}
.network-settings-tab:focus-visible {
outline: 2px solid hsl(var(--accent-color) / 0.78);
outline-offset: 1px;
}
.settings-network-panel {
min-width: 0;
}
.settings-network-section-title {
margin-bottom: 10px;
padding-inline-start: 0;
color: hsl(var(--text-primary));
font-size: 15px;
font-weight: 600;
line-height: 1.3;
}
.settings-network-panel .mac-settings-group {
margin-bottom: 0;
border-radius: 12px;
}
.settings-network-row .settings-row-label {
gap: 4px;
}
.settings-network-row .settings-row-label > span {
font-size: 13px;
font-weight: 550;
line-height: 1.35;
}
.settings-network-row .settings-row-label small {
max-width: 58ch;
font-size: 11.5px;
line-height: 1.45;
}
.settings-network-row > .settings-network-input,
.settings-network-row > .settings-combobox {
width: 100%;
max-width: 340px;
min-height: 34px;
justify-self: end;
align-self: start;
margin-top: 0;
}
.settings-network-row > .settings-port-input {
width: 128px;
min-height: 34px;
justify-self: end;
align-self: start;
margin-top: 0;
}
.settings-network-row > .mac-switch,
.settings-network-row > input[type="checkbox"]:not(.mac-switch) {
justify-self: end;
align-self: start;
margin-top: 5px;
}
.settings-network-note {
display: flex;
align-items: flex-start;
gap: 8px;
background: hsl(var(--surface-raised) / 0.34);
line-height: 1.45;
}
.settings-network-note svg {
flex: 0 0 auto;
margin-top: 1px;
color: hsl(var(--accent-color));
}
@media (max-width: 640px) {
.settings-content--network {
max-width: none;
}
.network-settings-tab {
flex: 0 0 auto;
}
.settings-network-panel .settings-network-row {
grid-template-columns: minmax(0, 1fr);
gap: 10px;
}
.settings-network-panel .settings-network-row > .settings-network-input,
.settings-network-panel .settings-network-row > .settings-combobox {
max-width: none;
justify-self: stretch;
}
.settings-network-panel .settings-network-row > .settings-port-input {
justify-self: start;
}
.settings-network-panel .settings-network-row > .mac-switch,
.settings-network-panel .settings-network-row > input[type="checkbox"]:not(.mac-switch) {
justify-self: start;
}
}
.settings-content-shell {
padding: 32px;
}
@@ -2845,11 +3009,17 @@ html[data-list-density="relaxed"] {
.settings-network-row {
position: relative;
display: grid;
grid-template-columns: minmax(150px, 190px) minmax(0, 1fr);
gap: 18px;
grid-template-columns: minmax(0, 1fr) minmax(180px, 36%);
gap: 24px;
min-height: 68px;
padding: 15px 18px;
align-items: start;
}
.settings-network-row > .settings-row-label {
padding-top: 1px;
}
.settings-popup-group .settings-network-row:focus-within {
z-index: 20;
}
@@ -2859,10 +3029,6 @@ html[data-list-density="relaxed"] {
z-index: 0;
}
.settings-network-row > .settings-row-label {
padding-top: 3px;
}
.settings-network-input {
width: 100%;
min-width: 0;
@@ -4381,6 +4547,12 @@ html[dir="rtl"] .download-context-menu-chevron {
}
}
@media (min-width: 641px) and (max-width: 900px) {
.settings-network-panel .settings-choice-row {
grid-template-columns: minmax(0, 1fr) minmax(180px, 36%);
}
}
/* Logs Console */
.logs-toolbar {
height: 42px;