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
- - `; - 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; }