Обновить UI для меню и языка

This commit is contained in:
AidarKC
2026-08-29 15:56:01 +04:00
parent b47440dd72
commit 3e4583bbe9
12 changed files with 282 additions and 53 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
client.version=1.7.5
client.version=1.7.6
server.version=1.6.2
+2 -2
View File
@@ -1,4 +1,4 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="32" cy="24.5" r="10.5" stroke="#F2F5FB" stroke-width="3"/>
<path d="M15.5 48C17.8 38.9 24.2 34.5 32 34.5C39.8 34.5 46.2 38.9 48.5 48" stroke="#F2F5FB" stroke-width="3" stroke-linecap="round"/>
<circle cx="32" cy="23.5" r="10.5" stroke="#65B7FF" stroke-width="3.2"/>
<path d="M14.5 49C17.1 39.2 23.9 34.5 32 34.5C40.1 34.5 46.9 39.2 49.5 49" stroke="#65B7FF" stroke-width="3.2" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 316 B

+3 -3
View File
@@ -1,4 +1,4 @@
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 15.25A3.25 3.25 0 1 0 12 8.75a3.25 3.25 0 0 0 0 6.5Z" stroke="#F2F5FB" stroke-width="1.65"/>
<path d="M19.15 13.15c.04-.38.04-.77 0-1.15l1.55-1.2-1.75-3.02-1.82.74a7.4 7.4 0 0 0-1-.58L15.86 6h-3.5l-.27 1.94c-.35.16-.68.35-1 .58l-1.82-.74-1.75 3.02L9.07 12a6 6 0 0 0 0 1.15l-1.55 1.2 1.75 3.03 1.82-.75c.32.23.65.42 1 .58l.27 1.94h3.5l.27-1.94c.35-.16.68-.35 1-.58l1.82.75 1.75-3.03-1.55-1.2Z" stroke="#F2F5FB" stroke-width="1.55" stroke-linecap="round" stroke-linejoin="round"/>
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.1 11.5h9.8l1.4 6.1c1.5.6 2.9 1.4 4.2 2.4l6-1.9 4.9 8.5-4.6 4.2c.1.8.2 1.6.2 2.4s-.1 1.6-.2 2.4l4.6 4.2-4.9 8.5-6-1.9c-1.3 1-2.7 1.8-4.2 2.4l-1.4 6.1h-9.8l-1.4-6.1c-1.5-.6-2.9-1.4-4.2-2.4l-6 1.9-4.9-8.5 4.6-4.2a16 16 0 0 1 0-4.8l-4.6-4.2 4.9-8.5 6 1.9c1.3-1 2.7-1.8 4.2-2.4l1.4-6.1Z" stroke="#65B7FF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="32" cy="33.2" r="8.2" stroke="#65B7FF" stroke-width="3.2"/>
</svg>

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 558 B

+3 -4
View File
@@ -1,6 +1,5 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 24.5C13.5 20.9101 16.4101 18 20 18H41.5C45.0899 18 48 20.9101 48 24.5V26H23.5C21.0147 26 19 28.0147 19 30.5C19 32.9853 21.0147 35 23.5 35H48V39.5C48 43.0899 45.0899 46 41.5 46H20C16.4101 46 13.5 43.0899 13.5 39.5V24.5Z" stroke="#F2F5FB" stroke-width="3" stroke-linejoin="round"/>
<path d="M19 30.5C19 28.0147 21.0147 26 23.5 26H50.5V35H23.5C21.0147 35 19 32.9853 19 30.5Z" stroke="#F2F5FB" stroke-width="3" stroke-linejoin="round"/>
<circle cx="42.5" cy="30.5" r="2.4" fill="#F2F5FB"/>
<path d="M18.5 22H38" stroke="#F2F5FB" stroke-width="3" stroke-linecap="round" opacity="0.9"/>
<path d="M13 23.5C13 19.9 15.9 17 19.5 17H42C45.6 17 48.5 19.9 48.5 23.5V27H24C21.2 27 19 29.2 19 32C19 34.8 21.2 37 24 37H48.5V40.5C48.5 44.1 45.6 47 42 47H19.5C15.9 47 13 44.1 13 40.5V23.5Z" stroke="#65B7FF" stroke-width="3.2" stroke-linejoin="round"/>
<path d="M19 32C19 29.2 21.2 27 24 27H51V37H24C21.2 37 19 34.8 19 32Z" stroke="#65B7FF" stroke-width="3.2" stroke-linejoin="round"/>
<circle cx="42.5" cy="32" r="2.5" fill="#65B7FF"/>
</svg>

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 548 B

+21 -2
View File
@@ -229,9 +229,17 @@ window.__SHINE_BUILD_HASH__ = '20260826145335';
document.getElementById(menuId)?.remove();
} catch {}
};
const dismissError = () => {
removeMenu();
try {
document.getElementById('boot-error-overlay')?.remove();
setState(null);
} catch {}
};
const openMenu = () => {
try {
removeMenu();
document.dispatchEvent(new CustomEvent('shine:dropdown-open', { detail: { owner: 'boot-error' } }));
const report = buildReport();
const root = document.getElementById('modal-root') || document.body;
const shell = document.createElement('div');
@@ -246,7 +254,7 @@ window.__SHINE_BUILD_HASH__ = '20260826145335';
<button type="button" class="secondary-btn" data-action="copy">Скопировать текст ошибки</button>
<button type="button" class="secondary-btn" data-action="share">Отправить текст ошибки</button>
<button type="button" class="secondary-btn" data-action="send"${typeof window.__SHINE_SEND_CLIENT_ERROR_TO_SERVER__ === 'function' ? '' : ' disabled'}>Отправить в лог на сервере</button>
<button type="button" class="ghost-btn" data-action="close">Закрыть</button>
<button type="button" class="secondary-btn" data-action="dismiss">Закрыть</button>
</div>
</div>
`;
@@ -308,7 +316,18 @@ window.__SHINE_BUILD_HASH__ = '20260826145335';
close();
});
shell.querySelector('[data-action="close"]')?.addEventListener('click', close);
shell.querySelector('[data-action="dismiss"]')?.addEventListener('click', () => {
close();
try {
document.getElementById('boot-error-overlay')?.remove();
setState(null);
} catch {}
});
const onPeerDropdownOpen = (event) => {
if (event?.detail?.owner === 'boot-error') return;
close();
};
document.addEventListener('shine:dropdown-open', onPeerDropdownOpen, { once: true });
dialog?.focus?.();
window.addEventListener('keydown', onKeyDown);
} catch (error) {
+10 -1
View File
@@ -5,6 +5,7 @@ export function createDropdownMenu({
minWidth = 210,
offset = 7,
align = 'right',
leftShift = 0,
onOpen = null,
onClose = null,
} = {}) {
@@ -23,7 +24,8 @@ export function createDropdownMenu({
const rect = anchorEl.getBoundingClientRect();
const margin = 10;
const menuWidth = portal.offsetWidth || minWidth;
const desiredLeft = align === 'left' ? rect.left : rect.right - menuWidth;
const baseLeft = align === 'left' ? rect.left : rect.right - menuWidth;
const desiredLeft = baseLeft - Number(leftShift || 0);
const left = Math.max(margin, Math.min(window.innerWidth - menuWidth - margin, desiredLeft));
let top = rect.bottom + offset;
const menuHeight = portal.offsetHeight || 180;
@@ -36,6 +38,7 @@ export function createDropdownMenu({
const open = () => {
if (!anchorEl || portal) return;
document.dispatchEvent(new CustomEvent('shine:dropdown-open', { detail: { owner: anchorEl } }));
const menu = document.createElement('div');
menu.className = `dm-head-menu dm-head-menu--portal shared-dropdown-menu ${className}`.trim();
menu.setAttribute('role', 'menu');
@@ -99,6 +102,10 @@ export function createDropdownMenu({
if (portal.contains(event.target) || anchorEl?.contains(event.target)) return;
close();
};
const onPeerDropdownOpen = (event) => {
if (!portal || event?.detail?.owner === anchorEl) return;
close();
};
const onKeydown = (event) => {
if (event.key !== 'Escape' || !portal) return;
close();
@@ -110,6 +117,7 @@ export function createDropdownMenu({
anchorEl?.addEventListener('click', onAnchorClick);
document.addEventListener('click', onOutsideClick);
document.addEventListener('keydown', onKeydown);
document.addEventListener('shine:dropdown-open', onPeerDropdownOpen);
window.addEventListener('resize', position, { passive: true });
window.addEventListener('scroll', position, { passive: true, capture: true });
@@ -123,6 +131,7 @@ export function createDropdownMenu({
anchorEl?.removeEventListener('click', onAnchorClick);
document.removeEventListener('click', onOutsideClick);
document.removeEventListener('keydown', onKeydown);
document.removeEventListener('shine:dropdown-open', onPeerDropdownOpen);
window.removeEventListener('resize', position);
window.removeEventListener('scroll', position, true);
},
+1 -1
View File
@@ -84,7 +84,7 @@ export function renderToolbar(currentPageId, navigate) {
</span>
`;
} else if (isNetwork) {
btn.innerHTML = iconHtml(item);
btn.innerHTML = `${iconHtml(item)}<span>${item.label}</span>`;
btn.setAttribute('aria-label', item.label);
btn.title = item.label;
} else {
+11 -1
View File
@@ -966,6 +966,7 @@ function openTopChannelsMenu({
onFindChannel,
}) {
closeTopChannelsMenu(listState);
document.dispatchEvent(new CustomEvent('shine:dropdown-open', { detail: { owner: anchorEl } }));
const root = document.getElementById('modal-root');
if (!root || !anchorEl) return;
@@ -975,7 +976,9 @@ function openTopChannelsMenu({
left = Math.max(12, Math.min(left, window.innerWidth - menuWidth - 12));
const estimatedHeight = 250;
let top = rect.bottom + 8;
const titleAnchor = document.querySelector('.channels-filter-title');
const titleRect = titleAnchor?.getBoundingClientRect?.();
let top = (titleRect?.bottom || rect.bottom) + 7;
if (top + estimatedHeight > window.innerHeight - 10) {
top = Math.max(12, rect.top - estimatedHeight - 8);
}
@@ -1022,13 +1025,19 @@ function openTopChannelsMenu({
const onOverlayClick = (event) => {
if (event.target === overlay) closeTopChannelsMenu(listState);
};
const onPeerDropdownOpen = (event) => {
if (event?.detail?.owner === anchorEl) return;
closeTopChannelsMenu(listState);
};
const onWindowResize = () => closeTopChannelsMenu(listState);
overlay.addEventListener('click', onOverlayClick);
window.addEventListener('resize', onWindowResize);
document.addEventListener('shine:dropdown-open', onPeerDropdownOpen);
listState.topMenuCleanup = () => {
overlay.removeEventListener('click', onOverlayClick);
window.removeEventListener('resize', onWindowResize);
document.removeEventListener('shine:dropdown-open', onPeerDropdownOpen);
};
}
@@ -1368,6 +1377,7 @@ export function render({ navigate, route, chrome }) {
const channelsFilterMenu = createDropdownMenu({
anchorEl: topTitle,
align: 'left',
leftShift: 72,
minWidth: 220,
items: [
{ label: 'Все каналы', iconHtml: channelMenuIcon('all'), action: () => navigate(buildChannelsViewRoute(CHANNELS_VIEW_ALL)) },
+5 -5
View File
@@ -50,7 +50,7 @@ export function render({ navigate }) {
controls.className = 'row wrap-row';
const checkButton = document.createElement('button');
checkButton.className = 'ghost-btn server-check-btn';
checkButton.className = 'shine-btn shine-btn--settings server-check-btn';
checkButton.type = 'button';
checkButton.textContent = 'Проверить';
@@ -132,7 +132,7 @@ export function render({ navigate }) {
if (isLocalDemoAvailable()) {
const localDemoButton = document.createElement('button');
localDemoButton.className = 'ghost-btn preauth-local-demo-btn';
localDemoButton.className = 'shine-btn shine-btn--settings preauth-local-demo-btn';
localDemoButton.type = 'button';
localDemoButton.textContent = 'Открыть локальный тестовый режим';
localDemoButton.addEventListener('click', () => {
@@ -143,7 +143,7 @@ export function render({ navigate }) {
}
const serverUiButton = document.createElement('button');
serverUiButton.className = 'ghost-btn';
serverUiButton.className = 'shine-btn shine-btn--settings';
serverUiButton.type = 'button';
serverUiButton.textContent = 'Настроить свой сервер';
serverUiButton.addEventListener('click', () => {
@@ -152,13 +152,13 @@ export function render({ navigate }) {
});
const cancelButton = document.createElement('button');
cancelButton.className = 'ghost-btn';
cancelButton.className = 'shine-btn shine-btn--settings';
cancelButton.type = 'button';
cancelButton.textContent = 'Отмена';
cancelButton.addEventListener('click', () => navigate('start-view'));
const saveButton = document.createElement('button');
saveButton.className = 'primary-btn';
saveButton.className = 'shine-btn shine-btn--primary';
saveButton.type = 'button';
saveButton.textContent = 'Сохранить';
saveButton.addEventListener('click', async () => {
+30 -18
View File
@@ -12,6 +12,7 @@ export function render({ navigate }) {
const screen = document.createElement('section');
screen.className = 'stack language-screen';
const returnPage = resolveReturnPage();
let pendingLanguage = state.entrySettings.language === 'en' ? 'en' : 'ru';
screen.append(
renderHeader({
@@ -27,31 +28,42 @@ export function render({ navigate }) {
card.className = 'card stack language-choice-card';
card.innerHTML = `
<p class="meta-muted language-choice-hint">Выберите язык интерфейса</p>
<div class="language-choice-grid" role="radiogroup" aria-label="Язык интерфейса">
<button class="language-choice-option${state.entrySettings.language === 'ru' ? ' is-selected' : ''}" type="button" data-language="ru" role="radio" aria-checked="${state.entrySettings.language === 'ru'}">
<span class="language-choice-name">Русский</span>
<span class="language-choice-code">RU</span>
</button>
<button class="language-choice-option${state.entrySettings.language === 'en' ? ' is-selected' : ''}" type="button" data-language="en" role="radio" aria-checked="${state.entrySettings.language === 'en'}">
<span class="language-choice-name">English</span>
<span class="language-choice-code">EN</span>
</button>
<div class="language-segmented-control" role="radiogroup" aria-label="Язык интерфейса">
<button class="language-segment${pendingLanguage === 'ru' ? ' is-selected' : ''}" type="button" data-language="ru" role="radio" aria-checked="${pendingLanguage === 'ru'}">Русский</button>
<button class="language-segment${pendingLanguage === 'en' ? ' is-selected' : ''}" type="button" data-language="en" role="radio" aria-checked="${pendingLanguage === 'en'}">English</button>
<span class="language-segmented-thumb" aria-hidden="true"></span>
</div>
<div class="language-choice-actions">
<button class="shine-btn shine-btn--settings" type="button" data-action="cancel">Отмена</button>
<button class="shine-btn shine-btn--primary" type="button" data-action="ok">OK</button>
</div>
`;
const syncSelection = () => {
card.dataset.language = pendingLanguage;
card.querySelectorAll('[data-language]').forEach((item) => {
const selected = item.dataset.language === pendingLanguage;
item.classList.toggle('is-selected', selected);
item.setAttribute('aria-checked', String(selected));
});
};
syncSelection();
card.querySelectorAll('[data-language]').forEach((button) => {
button.addEventListener('click', () => {
const language = String(button.dataset.language || 'ru');
saveEntryLanguage(language);
card.querySelectorAll('[data-language]').forEach((item) => {
const selected = item === button;
item.classList.toggle('is-selected', selected);
item.setAttribute('aria-checked', String(selected));
});
sessionStorage.removeItem('shine-language-return-page');
navigate(returnPage);
pendingLanguage = button.dataset.language === 'en' ? 'en' : 'ru';
syncSelection();
});
});
card.querySelector('[data-action="cancel"]')?.addEventListener('click', () => {
sessionStorage.removeItem('shine-language-return-page');
navigate(returnPage);
});
card.querySelector('[data-action="ok"]')?.addEventListener('click', () => {
saveEntryLanguage(pendingLanguage);
sessionStorage.removeItem('shine-language-return-page');
navigate(returnPage);
});
screen.append(card);
return screen;
+10 -1
View File
@@ -227,6 +227,7 @@ export function render({ navigate, chrome }) {
const chatFilterMenu = createDropdownMenu({
anchorEl: filterTitle,
align: 'left',
leftShift: 72,
minWidth: 225,
items: [
{ label: 'Все чаты', action: () => { currentChatFilter = 'all'; filterTitle.textContent = filterLabels.all; reloadForFilter(); } },
@@ -259,11 +260,13 @@ export function render({ navigate, chrome }) {
const menuWidth = menuPortal.offsetWidth || 206;
const left = Math.max(margin, Math.min(window.innerWidth - menuWidth - margin, rect.right - menuWidth));
menuPortal.style.left = `${Math.round(left)}px`;
menuPortal.style.top = `${Math.round(rect.bottom + 7)}px`;
const titleRect = filterTitle?.getBoundingClientRect?.();
menuPortal.style.top = `${Math.round((titleRect?.bottom || rect.bottom) + 7)}px`;
};
const openHeadMenu = () => {
if (!menuButton || menuPortal) return;
document.dispatchEvent(new CustomEvent('shine:dropdown-open', { detail: { owner: menuButton } }));
const portal = document.createElement('div');
portal.className = 'dm-head-menu dm-head-menu--portal';
portal.setAttribute('role', 'menu');
@@ -304,6 +307,10 @@ export function render({ navigate, chrome }) {
if (menuPortal.contains(event.target) || menuButton?.contains(event.target)) return;
closeHeadMenu();
};
const onPeerDropdownOpen = (event) => {
if (!menuPortal || event?.detail?.owner === menuButton) return;
closeHeadMenu();
};
const onMenuKeydown = (event) => {
if (event.key !== 'Escape' || !menuPortal) return;
closeHeadMenu();
@@ -312,6 +319,7 @@ export function render({ navigate, chrome }) {
const onMenuViewportChange = () => positionHeadMenu();
document.addEventListener('click', onOutsideClick);
document.addEventListener('keydown', onMenuKeydown);
document.addEventListener('shine:dropdown-open', onPeerDropdownOpen);
window.addEventListener('resize', onMenuViewportChange, { passive: true });
window.addEventListener('scroll', onMenuViewportChange, { passive: true, capture: true });
@@ -487,6 +495,7 @@ function renderRow(item) {
chatFilterMenu.destroy();
document.removeEventListener('click', onOutsideClick);
document.removeEventListener('keydown', onMenuKeydown);
document.removeEventListener('shine:dropdown-open', onPeerDropdownOpen);
window.removeEventListener('resize', onMenuViewportChange);
window.removeEventListener('scroll', onMenuViewportChange, true);
};
+185 -14
View File
@@ -10040,10 +10040,11 @@ body.chat-topbar-overlay .composer-slot {
}
.profile-head-menu .shared-dropdown-menu__item > img {
width: 23px;
height: 23px;
min-width: 23px;
flex-basis: 23px;
width: 27px;
height: 27px;
min-width: 27px;
flex-basis: 27px;
filter: drop-shadow(0 0 6px rgba(101, 183, 255, 0.36));
}
.dm-head-filter-title,
@@ -10235,20 +10236,20 @@ body.chat-topbar-overlay .composer-slot {
.channels-screen--list .channel-row {
position: relative;
width: calc(100% - 8px);
grid-template-columns: 58px minmax(0, 1fr);
gap: 10px;
min-height: 64px;
margin: 2px 4px;
padding: 5px 8px 5px 5px;
width: calc(100% - 2px);
grid-template-columns: 46px minmax(0, 1fr);
gap: 8px;
min-height: 44px;
margin: 1px 1px;
padding: 3px 6px 3px 3px;
border-radius: 14px;
}
.channels-screen--list .channel-row .avatar {
width: 52px;
height: 52px;
min-width: 52px;
min-height: 52px;
width: 42px;
height: 42px;
min-width: 42px;
min-height: 42px;
}
.channels-screen--list .channel-row-main {
@@ -10314,3 +10315,173 @@ body.chat-topbar-overlay .composer-slot {
linear-gradient(180deg, rgba(255,255,255,.075), transparent 30%),
rgba(10, 24, 52, .68) !important;
}
/* ===== UX refinements 2026-08-29 ===== */
/* Start: larger breathing room between brand and actions. */
.auth-screen--welcome .auth-brand {
margin-bottom: clamp(34px, 6vh, 58px) !important;
}
.auth-screen--welcome .shine-actions {
margin-top: 0;
}
/* Language selector: actual two-position switch with explicit confirmation. */
.language-segmented-control {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
padding: 4px;
min-height: 58px;
border: 1px solid rgba(190, 210, 238, .18);
border-radius: 18px;
background: rgba(12, 22, 40, .72);
overflow: hidden;
}
.language-segmented-thumb {
position: absolute;
z-index: 0;
top: 4px;
bottom: 4px;
left: 4px;
width: calc(50% - 4px);
border: 1px solid rgba(99, 190, 255, .52);
border-radius: 14px;
background: linear-gradient(180deg, rgba(76, 166, 255, .28), rgba(30, 105, 204, .20));
box-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 0 18px rgba(63,160,255,.14);
transition: transform 180ms ease;
}
.language-choice-card[data-language="en"] .language-segmented-thumb { transform: translateX(100%); }
.language-segment {
position: relative;
z-index: 1;
min-height: 50px;
padding: 0 12px;
border: 0 !important;
background: transparent !important;
color: rgba(231,239,255,.64);
font-size: 16px;
font-weight: 650;
}
.language-segment.is-selected { color: #eef8ff; }
.language-choice-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 8px;
}
.language-choice-actions .shine-btn { height: 50px; }
/* Entry settings actions use the existing Start button visual language without huge width side effects. */
.screen-content.preauth-flow .server-check-btn.shine-btn {
width: auto;
height: 44px;
min-height: 44px;
padding-inline: 18px;
border-radius: 14px;
font-size: 15px;
}
.screen-content.preauth-flow .auth-footer-actions .shine-btn {
width: 100%;
}
/* Channels: much wider, substantially shorter rows. */
.channels-screen--list .channels-list-content,
.channels-screen--list .channels-groups,
.channels-screen--list .stack:has(> .channel-row) {
width: calc(100% + 16px) !important;
margin-left: -8px !important;
margin-right: -8px !important;
}
.channels-screen--list .channel-row {
width: 100% !important;
min-height: 54px !important;
margin: 1px 0 !important;
padding: 5px 6px 5px 4px !important;
grid-template-columns: 50px minmax(0, 1fr) !important;
gap: 8px !important;
border-radius: 15px !important;
}
.channels-screen--list .channel-row .avatar {
width: 48px !important;
height: 48px !important;
min-width: 48px !important;
min-height: 48px !important;
}
.channels-screen--list .channel-row-main {
gap: 0 !important;
padding-right: 3px !important;
align-self: center;
overflow: hidden;
}
.channels-screen--list .channel-row-title,
.channels-screen--list .channel-row-technical,
.channels-screen--list .channel-row-description,
.channels-screen--list .channel-row-owner {
margin-block: 0 !important;
line-height: 1.12 !important;
}
.channels-screen--list .channel-row-preview-line {
display: flex !important;
align-items: baseline !important;
gap: 7px !important;
min-width: 0 !important;
width: 100% !important;
line-height: 1.15 !important;
}
.channels-screen--list .channel-row-preview-line .channel-row-message {
flex: 1 1 auto !important;
min-width: 0 !important;
max-width: 100% !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
line-height: 1.15 !important;
}
.channels-screen--list .channel-row-preview-line .channel-row-time {
flex: 0 0 auto !important;
position: static !important;
align-self: baseline !important;
margin: 0 !important;
padding: 0 !important;
line-height: 1.15 !important;
transform: none !important;
white-space: nowrap !important;
}
.channels-screen--list .channel-row-controls { top: 3px !important; right: 5px !important; }
/* Toolbar: reserve enough vertical room for the Connections label and safe area. */
:root { --toolbar-height: 88px; }
.toolbar-slot {
min-height: calc(88px + env(safe-area-inset-bottom)) !important;
overflow: visible !important;
padding-bottom: env(safe-area-inset-bottom) !important;
}
.toolbar {
min-height: 82px !important;
overflow: visible !important;
padding-bottom: 6px !important;
align-items: end;
}
.toolbar-btn-network {
min-height: 72px !important;
align-content: end !important;
overflow: visible !important;
padding-bottom: 2px !important;
}
.toolbar-btn-network .toolbar-icon-img {
transform: translateY(-4px) !important;
}
.toolbar-btn-network > span:last-child {
display: block !important;
transform: translateY(-2px) !important;
line-height: 1.05 !important;
padding-bottom: 1px !important;
}
.channels-screen--list .channel-row-technical,
.channels-screen--list .channel-row-description,
.channels-screen--list .channel-row-owner {
display: none !important;
}