UI v4: новый стиль по макету V3/V4 и доводка экранов

Палитра «Сияние» по умолчанию, новые роли цветов. Лента канала: плоские записи, ряд действий
с равным шагом иконок, дата у записи. Обсуждение со строкой ввода, личный чат, шторки.
Вход и регистрация: стартовый экран (надпись «СИЯНИЕ», сияние мандалы), настройки входа
со статусами серверов, статус логина в одну строку, вход с другого устройства (код, таймер, шаги),
сохранение ключей с переключателями. Корневые разделы – заголовок по центру; фильтр чатов –
переключатель; уведомления – список как в чатах; фото – просмотр на весь экран.
Общее: один зазор под шапкой (--page-top-gap), переключатели высотой с поле ввода,
второстепенные кнопки по одной роли (в том числе на экранах устройств и серверов),
иконки только из ui-icon.js, полоса прокрутки скрыта, среднее тире в текстах интерфейса.

Что нужно от сервера и разработчика – docs/UI-Design/UI-v4-for-dev.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
q
2026-09-29 01:36:52 +03:00
co-authored by Claude Opus 5.5
parent 13693a0a53
commit 2e62616b46
84 changed files with 2507 additions and 1583 deletions
+21 -15
View File
@@ -1,4 +1,5 @@
import { createTopBar } from '../components/topbar.js';
import { iconHtml } from '../components/ui-icon.js';
import {
authService,
authorizeSession,
@@ -202,15 +203,27 @@ export function render({ navigate }) {
balanceValue.textContent = `${formatSol(parseBalanceSol(state.registrationPayment.balanceSOL), 6)} SOL`;
const refreshButton = document.createElement('button');
refreshButton.className = 'square-btn';
refreshButton.className = 'icon-btn payment-refresh-btn';
refreshButton.type = 'button';
refreshButton.textContent = '↻';
refreshButton.title = 'Обновить';
refreshButton.innerHTML = iconHtml('refresh');
refreshButton.title = 'Обновить баланс';
refreshButton.setAttribute('aria-label', 'Обновить баланс');
// Коротко и по-человечески; технические подробности — во всплывающей подсказке.
const showWalletError = (error, fallback) => {
status.className = 'status-line is-unavailable';
status.textContent = error?.code === 'NO_KEYS'
? 'Не хватает данных регистрации. Вернитесь назад и нажмите «Далее».'
: fallback;
status.title = error?.message || '';
status.style.display = '';
};
const refreshBalance = async ({ showError = true, addressOverride = '' } = {}) => {
const address = String(addressOverride || walletValue.value || '').trim();
if (!address) return null;
refreshButton.disabled = true;
refreshButton.classList.add('is-loading');
try {
const balance = await getBalanceSol({
endpoint: state.entrySettings.solanaServer,
@@ -220,20 +233,17 @@ export function render({ navigate }) {
balanceValue.textContent = `${formatSol(balance.sol, 6)} SOL`;
return Number(balance.sol) || 0;
} catch (error) {
if (showError) {
status.className = 'status-line is-unavailable';
status.textContent = `Не удалось обновить баланс: ${error?.message || 'unknown'}`;
status.style.display = '';
}
if (showError) showWalletError(error, 'Не удалось обновить баланс. Проверьте связь и попробуйте ещё раз.');
return null;
} finally {
refreshButton.disabled = false;
refreshButton.classList.remove('is-loading');
}
};
const deriveUserWalletAddress = async () => {
const keyBundle = state.registrationDraft.preGeneratedKeyBundle;
if (!keyBundle) throw new Error('Ключи ещё не сгенерированы. Вернитесь на предыдущий шаг.');
if (!keyBundle) throw Object.assign(new Error('Ключи ещё не сгенерированы'), { code: 'NO_KEYS' });
const { publicKeyB64 } = keyBundle.clientPair;
const raw = atob(publicKeyB64);
const bytes = new Uint8Array(raw.length);
@@ -261,9 +271,7 @@ export function render({ navigate }) {
await deriveUserWalletAddress();
navigate('topup-view');
} catch (error) {
status.className = 'status-line is-unavailable';
status.textContent = `Не удалось подготовить кошелёк: ${error?.message || 'unknown'}`;
status.style.display = '';
showWalletError(error, 'Не удалось подготовить кошелёк. Попробуйте ещё раз.');
}
});
@@ -386,9 +394,7 @@ export function render({ navigate }) {
const walletAddress = await deriveUserWalletAddress();
await refreshBalance({ addressOverride: walletAddress });
} catch (error) {
status.className = 'status-line is-unavailable';
status.textContent = `Не удалось подготовить client.key: ${error?.message || 'unknown'}`;
status.style.display = '';
showWalletError(error, 'Не удалось показать кошелёк. Попробуйте открыть экран ещё раз.');
}
})();