From ecc9efd4344409052ecb0e77d5db9563805e9e8608886e48abac86c78e548a3b Mon Sep 17 00:00:00 2001
From: AidarKC
Date: Sat, 20 Jun 2026 21:33:44 +0400
Subject: [PATCH] =?UTF-8?q?UI:=20=D1=81=D0=BA=D1=80=D1=8B=D1=82=D1=8C=20?=
=?UTF-8?q?=D0=BF=D0=B0=D1=80=D0=BE=D0=BB=D1=8C=20=D0=BF=D1=80=D0=B8=20?=
=?UTF-8?q?=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D0=B5=2012=20=D1=81=D0=BB=D0=BE?=
=?UTF-8?q?=D0=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
shine-UI/js/app.js | 6 +--
shine-UI/js/pages/login-password-view.js | 36 ++++++---------
shine-UI/js/pages/register-view.js | 58 ++++++++++--------------
3 files changed, 42 insertions(+), 58 deletions(-)
diff --git a/shine-UI/js/app.js b/shine-UI/js/app.js
index 3d0348c..ed368f8 100644
--- a/shine-UI/js/app.js
+++ b/shine-UI/js/app.js
@@ -34,8 +34,8 @@ import {
import * as startView from './pages/start-view.js?v=202606142105';
import * as entrySettingsView from './pages/entry-settings-view.js?v=202606161240';
-import * as registerView from './pages/register-view.js';
-import * as registrationFaqView from './pages/registration-faq-view.js';
+import * as registerView from './pages/register-view.js?v=202606201650';
+import * as registrationFaqView from './pages/registration-faq-view.js?v=202606201650';
import * as registrationPaymentView from './pages/registration-payment-view.js?v=202606180940';
import * as registrationKeysView from './pages/registration-keys-view.js';
import * as registrationDraftKeysView from './pages/registration-draft-keys-view.js';
@@ -44,7 +44,7 @@ import * as devnetTopupView from './pages/devnet-topup-view.js';
import * as loginView from './pages/login-view.js?v=202606150110';
import * as loginCameraView from './pages/login-camera-view.js';
import * as loginOtherDeviceView from './pages/login-other-device-view.js?v=202606180940';
-import * as loginPasswordView from './pages/login-password-view.js';
+import * as loginPasswordView from './pages/login-password-view.js?v=202606201650';
import * as keyStorageView from './pages/key-storage-view.js';
import * as profileView from './pages/profile-view.js';
diff --git a/shine-UI/js/pages/login-password-view.js b/shine-UI/js/pages/login-password-view.js
index eef0a47..c870886 100644
--- a/shine-UI/js/pages/login-password-view.js
+++ b/shine-UI/js/pages/login-password-view.js
@@ -53,7 +53,7 @@ function createWordsLayout({ words, onInput }) {
const preview = document.createElement('p');
preview.className = 'status-line';
- section.append(grid, hint, preview);
+ section.append(grid, hint);
return { section, inputs, preview };
}
@@ -119,24 +119,13 @@ export function render({ navigate }) {
hint.className = 'meta-muted';
hint.textContent = 'Введите логин. На следующем шаге сохраните ключи на устройстве.';
- const advanced = document.createElement('details');
- advanced.className = 'card stack';
- advanced.innerHTML = `
- Расширенные
-
Схема деривации: логин нормализуется как trim().toLowerCase(). При непустом пароле используется Argon2id, затем из результата строится секрет.
- Из секрета строятся root key, blockchain key и device key. Обычно можно не вникать в это подробно и просто хранить всё на своём устройстве.
- Режим 12 слов ничего не меняет в протоколе: слова просто склеиваются в один обычный пароль длиной до 256 символов.
- Если пароль пустой — используется прежний детерминированный режим совместимости.
- Профиль Argon2id сейчас фиксированный: t=2, m=65536 KiB (64 MiB), p=1, dkLen=32.
- `;
-
const status = document.createElement('p');
status.className = 'status-line is-unavailable';
status.style.display = 'none';
- const testLoginsHint = document.createElement('p');
- testLoginsHint.className = 'meta-muted';
- testLoginsHint.textContent = 'Основные тестовые логины: 1, 2, 3 (вход без пароля).';
+ let passwordField = null;
+ const passwordLengthText = document.createElement('p');
+ passwordLengthText.className = 'status-line';
function getCurrentPassword() {
return passwordMode === 'words' ? composePasswordFromWords(passwordWords) : String(passwordInput.value || '');
@@ -150,15 +139,17 @@ export function render({ navigate }) {
}
function updateWordsPreview() {
- const password = composePasswordFromWords(passwordWords);
- const nonEmptyCount = normalizePasswordWords(passwordWords).filter((word) => word.trim()).length;
- wordsPreview.textContent = `Заполнено слов: ${nonEmptyCount} из 12 · итоговая длина пароля: ${password.length} символов.`;
+ const password = getCurrentPassword();
+ const text = `Итоговая длина пароля: ${password.length} символов.`;
+ wordsPreview.textContent = text;
+ passwordLengthText.textContent = text;
}
function updatePasswordModeVisibility() {
const wordsMode = passwordMode === 'words';
- wordsSection.hidden = !wordsMode;
- passwordInput.parentElement.hidden = wordsMode;
+ wordsSection.style.display = wordsMode ? 'grid' : 'none';
+ if (passwordField) passwordField.style.display = wordsMode ? 'none' : 'grid';
+ passwordInput.style.display = wordsMode ? 'none' : '';
updateWordsPreview();
}
@@ -167,8 +158,9 @@ export function render({ navigate }) {
Пароль
`;
form.children[0].append(loginInput);
- form.children[1].append(passwordInput);
- form.append(passwordModeToggle, wordsSection, hint, advanced, status, testLoginsHint);
+ passwordField = form.children[1];
+ passwordField.append(passwordInput);
+ form.append(passwordModeToggle, wordsSection, passwordLengthText, hint, status);
updatePasswordModeVisibility();
syncDraftState();
diff --git a/shine-UI/js/pages/register-view.js b/shine-UI/js/pages/register-view.js
index d808732..e86cf2f 100644
--- a/shine-UI/js/pages/register-view.js
+++ b/shine-UI/js/pages/register-view.js
@@ -13,7 +13,7 @@ import {
PASSWORD_MAX_LENGTH,
PASSWORD_WORDS_COUNT,
} from '../services/password-words.js';
-import { openRegistrationFaq, REGISTRATION_FAQ_TOPICS } from './registration-faq-view.js';
+import { openRegistrationFaq } from './registration-faq-view.js';
export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false };
@@ -55,7 +55,7 @@ function createWordsLayout({ words, onInput }) {
const preview = document.createElement('p');
preview.className = 'status-line';
- section.append(grid, hint, preview);
+ section.append(grid, hint);
return { section, inputs, preview };
}
@@ -137,35 +137,20 @@ export function render({ navigate }) {
const faqText = document.createElement('p');
faqText.className = 'meta-muted';
- faqText.textContent = 'Нажмите на вопрос, чтобы открыть отдельный экран с кратким объяснением.';
+ faqText.textContent = 'Если хотите подробнее понять схему деривации, ключи, первый сервер и формат 12 слов, откройте отдельный экран с вопросами.';
- const faqButtons = document.createElement('div');
- faqButtons.className = 'registration-faq-grid';
- REGISTRATION_FAQ_TOPICS.forEach((topic) => {
- const button = document.createElement('button');
- button.className = 'ghost-btn';
- button.type = 'button';
- button.textContent = topic.shortTitle;
- button.addEventListener('click', () => openRegistrationFaq(navigate, topic.id));
- faqButtons.append(button);
- });
- faqCard.append(faqTitle, faqText, faqButtons);
+ const faqButton = document.createElement('button');
+ faqButton.className = 'ghost-btn';
+ faqButton.type = 'button';
+ faqButton.textContent = 'Частые вопросы';
+ faqButton.addEventListener('click', () => openRegistrationFaq(navigate, 'key-derivation'));
+
+ faqCard.append(faqTitle, faqText, faqButton);
const formError = document.createElement('p');
formError.className = 'status-line is-unavailable';
formError.style.display = 'none';
- const advanced = document.createElement('details');
- advanced.className = 'card stack';
- advanced.innerHTML = `
- Расширенные
- Схема деривации: логин и пароль проходят через Argon2id, после чего получается главный секрет.
- Из этого секрета строятся три ключа: root key для основной публичной записи и важных изменений, blockchain key для подписания действий SHiNE в блокчейне, device key для входа и работы конкретного устройства.
- Разделение нужно, чтобы можно было аккуратнее выдавать права устройствам. Но если у вас нет большой суммы на счёте и нет повышенного риска, обычно можно просто хранить всё на своём устройстве.
- Режим 12 слов не меняет формат пароля и не меняет API: слова просто склеиваются в одну строку длиной до 256 символов.
- Профиль Argon2id сейчас фиксированный: t=2, m=65536 KiB (64 MB), p=1, dkLen=32.
- `;
-
const checkButton = document.createElement('button');
checkButton.className = 'ghost-btn';
checkButton.type = 'button';
@@ -185,6 +170,9 @@ export function render({ navigate }) {
nextButton.type = 'button';
nextButton.textContent = 'Далее';
+ let passwordField = null;
+ const passwordLengthText = document.createElement('p');
+ passwordLengthText.className = 'status-line';
let lastCheckedLogin = '';
let lastCheckedFree = false;
let lastCheckedClassName = '';
@@ -195,15 +183,17 @@ export function render({ navigate }) {
}
function updateWordsPreview() {
- const password = composePasswordFromWords(passwordWords);
- const nonEmptyCount = normalizePasswordWords(passwordWords).filter((word) => word.trim()).length;
- wordsPreview.textContent = `Заполнено слов: ${nonEmptyCount} из 12 · итоговая длина пароля: ${password.length} символов.`;
+ const password = getCurrentPassword();
+ const text = `Итоговая длина пароля: ${password.length} символов.`;
+ wordsPreview.textContent = text;
+ passwordLengthText.textContent = text;
}
function updatePasswordModeVisibility() {
const wordsMode = passwordMode === 'words';
- wordsSection.hidden = !wordsMode;
- passwordInput.parentElement.hidden = wordsMode;
+ wordsSection.style.display = wordsMode ? 'grid' : 'none';
+ if (passwordField) passwordField.style.display = wordsMode ? 'none' : 'grid';
+ passwordInput.style.display = wordsMode ? 'none' : '';
updateWordsPreview();
}
@@ -365,9 +355,11 @@ export function render({ navigate }) {
Логин
Пароль
`;
- form.children[0].append(loginInput);
- form.children[1].append(passwordInput);
- form.append(passwordModeToggle, wordsSection, serverNotice, checkButton, statusText, faqCard, advanced, formError);
+ const loginField = form.children[0];
+ passwordField = form.children[1];
+ loginField.append(loginInput);
+ passwordField.append(passwordInput);
+ form.append(passwordModeToggle, wordsSection, passwordLengthText, serverNotice, checkButton, statusText, faqCard, formError);
actions.innerHTML = '';
actions.append(backButton, nextButton);
updatePasswordModeVisibility();