From cf6ca1d96bec05c6ce5592d0ec73088cba24c9ea9b98b69f93832f44baf69b0e Mon Sep 17 00:00:00 2001 From: Pixel Date: Thu, 16 Jul 2026 13:05:33 +0300 Subject: [PATCH] =?UTF-8?q?UI:=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=B0=D1=88=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BD=D1=8B=D0=B9=20=D0=B2=D0=B8=D0=B4=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8=D0=B8,?= =?UTF-8?q?=20Enter-=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D1=83=20?= =?UTF-8?q?=D0=B8=20=D1=88=D0=B0=D0=BF=D0=BA=D1=83=20=C2=AB=D0=A1=D0=B2?= =?UTF-8?q?=D1=8F=D0=B7=D0=B5=D0=B9=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit По итогам сверки после мержа с origin/main: - Регистрация: убрана кнопка «Проверить логин» (работает автопроверка через 1.5 с после ввода + повторная проверка на «Далее»), убрана кнопка «Назад» (есть ← в шапке), убран блок «Первый сервер SHiNE»; компактные классы registration-screen/registration-form, статус логина скрыт пока пуст. Промокоды, «глаз» пароля и заглушка коротких логинов origin — не тронуты. Осиротевший импорт defaultServerHttp удалён. - Чат: Enter снова отправляет сообщение, Ctrl+Enter — перенос строки (голосовой ввод и кнопка ➤ работают как раньше). - Шапка «Связей»: возвращён наш вариант (absolute, width:auto). - VERSION: client 1.2.315. Проверено в превью: регистрация — автопроверка вернула «Логин свободен ✅» без кнопки, FAQ-ссылка работает; чат — Enter отправляет (поле очищается), Ctrl+Enter вставляет перенос; консоль чистая. Co-Authored-By: Claude Opus 4.8 (1M context) --- VERSION.properties | 2 +- shine-UI/js/pages/chat-view.js | 25 +++++++++++++++- shine-UI/js/pages/register-view.js | 46 ++++++------------------------ shine-UI/styles/components.css | 8 ++---- 4 files changed, 36 insertions(+), 45 deletions(-) diff --git a/VERSION.properties b/VERSION.properties index ebc9a84..f173214 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.314 +client.version=1.2.315 server.version=1.2.292 diff --git a/shine-UI/js/pages/chat-view.js b/shine-UI/js/pages/chat-view.js index 6b56ea1..bdbd341 100644 --- a/shine-UI/js/pages/chat-view.js +++ b/shine-UI/js/pages/chat-view.js @@ -1252,9 +1252,32 @@ export function render({ navigate, route }) { if (!emojiPickerOpen || form.contains(event.target)) return; closeEmojiPicker(); }); + // Enter — отправить; Ctrl+Enter — перенос строки. input?.addEventListener('keydown', async (event) => { if (event.key !== 'Enter') return; - window.requestAnimationFrame(() => autoResizeComposer(input)); + if (event.ctrlKey) { + event.preventDefault(); + const start = Number(input.selectionStart ?? input.value.length); + const end = Number(input.selectionEnd ?? input.value.length); + const value = String(input.value || ''); + input.value = `${value.slice(0, start)}\n${value.slice(end)}`; + const nextPos = start + 1; + try { + input.setSelectionRange(nextPos, nextPos); + } catch { + // ignore + } + autoResizeComposer(input); + return; + } + event.preventDefault(); + const text = String(input.value || '').trim(); + if (!text) return; + input.value = ''; + autoResizeComposer(input); + closeEmojiPicker(); + await sendTextMessage(text); + focusInputToEnd(); }); form.querySelector('#chat-voice-input')?.addEventListener('click', async () => { diff --git a/shine-UI/js/pages/register-view.js b/shine-UI/js/pages/register-view.js index a1907d2..d02fe09 100644 --- a/shine-UI/js/pages/register-view.js +++ b/shine-UI/js/pages/register-view.js @@ -14,7 +14,6 @@ import { PASSWORD_WORDS_COUNT, } from '../services/password-words.js'; import { sha256Text } from '../services/crypto-utils.js'; -import { defaultServerHttp } from '../deploy-config.js'; import { openRegistrationFaq } from './registration-faq-view.js'; export const pageMeta = { id: 'register-view', title: 'Зарегистрироваться', showAppChrome: false }; @@ -109,12 +108,12 @@ function makePasswordToggleIcons() { export function render({ navigate }) { const screen = document.createElement('section'); - screen.className = 'stack'; + screen.className = 'stack registration-screen'; clearAuthMessages(); const form = document.createElement('div'); - form.className = 'card stack'; + form.className = 'card stack registration-form'; let passwordMode = String(state.registrationDraft.passwordMode || 'single') === 'words' ? 'words' : 'single'; let passwordWords = normalizePasswordWords(state.registrationDraft.passwordWords); @@ -217,16 +216,9 @@ export function render({ navigate }) { promoField.append(promoFieldLabel, promoInput, promoHint); const statusText = document.createElement('p'); - statusText.className = 'status-line'; + statusText.className = 'status-line registration-login-status'; statusText.style.display = 'none'; - const serverNotice = document.createElement('div'); - serverNotice.className = 'card stack'; - serverNotice.innerHTML = ` -

Первый сервер SHiNE

-

При регистрации адресом вашего первого сервера будет: ${state.entrySettings.shineServerHttp || defaultServerHttp}.

- `; - const formError = document.createElement('p'); formError.className = 'status-line is-unavailable'; formError.style.display = 'none'; @@ -238,20 +230,9 @@ export function render({ navigate }) { faqButton.textContent = 'Вопросы о регистрации'; faqButton.addEventListener('click', () => openRegistrationFaq(navigate, 'key-derivation')); - const checkButton = document.createElement('button'); - checkButton.className = 'ghost-btn'; - checkButton.type = 'button'; - checkButton.textContent = 'Проверить логин'; - const actions = document.createElement('div'); actions.className = 'auth-footer-actions'; - const backButton = document.createElement('button'); - backButton.className = 'ghost-btn'; - backButton.type = 'button'; - backButton.textContent = 'Назад'; - backButton.addEventListener('click', () => navigate('start-view')); - const nextButton = document.createElement('button'); nextButton.className = 'primary-btn'; nextButton.type = 'button'; @@ -259,7 +240,7 @@ export function render({ navigate }) { let passwordField = null; const passwordLengthText = document.createElement('p'); - passwordLengthText.className = 'status-line'; + passwordLengthText.className = 'password-length-hint'; let lastCheckedLogin = ''; let lastCheckedFree = false; let lastCheckedClassName = ''; @@ -278,7 +259,7 @@ export function render({ navigate }) { function setStatusMessage(message, kind = '') { statusText.textContent = message; - statusText.className = kind ? `status-line ${kind}` : 'status-line'; + statusText.className = kind ? `status-line registration-login-status ${kind}` : 'status-line registration-login-status'; statusText.style.display = message ? '' : 'none'; } @@ -394,9 +375,7 @@ export function render({ navigate }) { return lastCheckedFree && (lastCheckedClassName === 'free' || lastCheckedClassName === 'promo'); } - checkButton.disabled = true; - checkButton.textContent = 'Проверка...'; - if (automatic) setStatusMessage('Проверяем логин...'); + setStatusMessage('Проверяем логин...'); try { const check = await checkLoginExistsOnSolana({ login, @@ -451,15 +430,9 @@ export function render({ navigate }) { const details = formatSolanaErrorDetails(error); setStatusMessage(`${base}. Детали: ${details}`, 'is-unavailable'); return false; - } finally { - if (runId === loginCheckRunId) { - checkButton.disabled = false; - checkButton.textContent = 'Проверить логин'; - } } } - checkButton.addEventListener('click', () => runAvailabilityCheck()); passwordToggleButton.addEventListener('click', togglePasswordVisibility); loginInput.addEventListener('input', () => { @@ -561,7 +534,6 @@ export function render({ navigate }) { }); function renderInputStage() { - serverNotice.style.display = ''; form.innerHTML = ''; const loginField = document.createElement('label'); loginField.className = 'stack'; @@ -569,20 +541,19 @@ export function render({ navigate }) { const passwordLabel = document.createElement('label'); passwordLabel.className = 'stack registration-password-single'; passwordLabel.innerHTML = 'Пароль'; - form.append(loginField, statusText, checkButton, passwordLabel); + form.append(loginField, statusText, passwordLabel); passwordField = passwordLabel; loginField.append(loginInput); passwordField.append(passwordInputRow); form.append(passwordModeToggle, promoToggle, promoField, passwordLengthText, wordsSection, formError, faqButton); actions.innerHTML = ''; - actions.append(backButton, nextButton); + actions.append(nextButton); updatePasswordModeVisibility(); updatePromoVisibility(); syncDraftState(); } async function startGenerationStage() { - serverNotice.style.display = 'none'; const runId = ++generationRunId; form.innerHTML = ''; @@ -688,7 +659,6 @@ export function render({ navigate }) { leftAction: { label: '←', onClick: () => navigate('start-view') }, }), form, - serverNotice, actions, ); diff --git a/shine-UI/styles/components.css b/shine-UI/styles/components.css index a6df7f6..b8b7cd8 100644 --- a/shine-UI/styles/components.css +++ b/shine-UI/styles/components.css @@ -2136,8 +2136,8 @@ textarea.input { } .network-header-overlay { - position: sticky; - top: max(14px, calc(env(safe-area-inset-top) + 8px)); + position: absolute; + top: max(8px, env(safe-area-inset-top)); left: max(8px, env(safe-area-inset-left)); right: max(8px, env(safe-area-inset-right)); box-sizing: border-box; @@ -2148,9 +2148,7 @@ textarea.input { } .network-header-overlay.page-header { - display: grid; - grid-template-columns: minmax(42px, 1fr) auto minmax(58px, 1fr); - align-items: center; + width: auto; margin-bottom: 0; gap: 10px; }