Merge remote-tracking branch 'origin/main'

# Conflicts:
#	VERSION.properties
This commit is contained in:
AidarKC
2026-07-20 17:30:31 +04:00
4 changed files with 38 additions and 9 deletions
+6
View File
@@ -779,6 +779,12 @@ window.addEventListener('error', (event) => {
},
});
// Кросс-ориджин ошибки приходят как «Script error.» без файла/строки/стека —
// алертом такие не показываем (в лог и captureClientError выше уже записаны).
const isOpaqueCrossOriginError = String(event.message || '') === 'Script error.'
&& !event.filename && !(event.error?.stack);
if (isOpaqueCrossOriginError) return;
showGlobalErrorAlert('Поймана глобальная ошибка UI', {
message: event.message || 'Global JS error',
stack: event.error?.stack || '',
+13
View File
@@ -40,12 +40,25 @@ function saveRecent(items) {
function appendAnimatedAsset(root, item) {
const previewUrl = item.previewUrl || getTelegramEmojiPreviewUrl(item.file);
const animatedUrl = item.animatedUrl || getTelegramEmojiAssetUrl(item.file);
const hasAnimation = animatedUrl && animatedUrl !== previewUrl;
const image = document.createElement('img');
image.className = 'telegram-emoji-image';
image.alt = '';
image.decoding = 'async';
image.setAttribute('aria-hidden', 'true');
// Без отдельного анимационного ассета — просто статичная картинка,
// без hover/tap-подмены src и pointer capture (лишние запросы и «моргание»).
if (!hasAnimation) {
image.src = previewUrl;
image.onerror = () => {
root.classList.add('is-error');
image.removeAttribute('src');
};
root.append(image);
return;
}
let isActive = false;
let sourceVersion = 0;
@@ -8,7 +8,6 @@ import { TELEGRAM_EMOJI_GROUP_SMILEYS } from './telegram-emoji-smileys.js?v=2026
import { TELEGRAM_EMOJI_GROUP_SYMBOLS } from './telegram-emoji-symbols.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_TRAVEL_PLACES } from './telegram-emoji-travel_places.js?v=202607151545';
const TELEGRAM_EMOJI_ASSET_BASE_URL = 'https://raw.githubusercontent.com/Tarikul-Islam-Anik/Telegram-Animated-Emojis/main/';
const TELEGRAM_EMOJI_PREVIEW_BASE_URL = '/assets/emoji/telegram-previews/';
function getTelegramEmojiName(file) {
@@ -40,11 +39,13 @@ export const TELEGRAM_EMOJI_GROUPS = [
].map(withPreviewUrls);
export function getTelegramEmojiAssetUrl(file) {
return `${TELEGRAM_EMOJI_ASSET_BASE_URL}${file.split('/').map((part) => encodeURIComponent(part)).join('/')}`;
// Анимированные webp с raw.githubusercontent.com отключены: внешний хост
// блокируется у части пользователей (VPN/операторы) — тап по эмодзи вызывал
// сетевую ошибку и «моргание» картинки. Пока отдаём локальное статичное превью;
// возврат анимаций — только локальным паком в assets.
return getTelegramEmojiPreviewUrl(file);
}
export function getTelegramEmojiPreviewUrl(file) {
return `${TELEGRAM_EMOJI_PREVIEW_BASE_URL}${String(file || '').replace(/\.webp$/i, '.png').split('/').map((part) => encodeURIComponent(part)).join('/')}`;
}
export { TELEGRAM_EMOJI_ASSET_BASE_URL };
+14 -5
View File
@@ -914,6 +914,10 @@ export function render({ navigate, route }) {
emojiToggle?.setAttribute('aria-expanded', 'false');
};
// На сенсорных устройствах фокус в textarea поднимает клавиатуру поверх пикера —
// поэтому при вставке эмодзи фокусируем поле только там, где есть «точный» указатель.
const isFinePointer = window.matchMedia?.('(hover: hover) and (pointer: fine)')?.matches ?? true;
const insertEmoji = (emoji) => {
if (!input) return;
const value = String(input.value || '');
@@ -922,11 +926,13 @@ export function render({ navigate, route }) {
input.value = `${value.slice(0, start)}${emoji}${value.slice(end)}`;
const nextPosition = start + emoji.length;
emojiSelection = { start: nextPosition, end: nextPosition };
input.focus();
try {
input.setSelectionRange(nextPosition, nextPosition);
} catch {
// Ignore browsers that do not expose textarea selection during focus changes.
if (isFinePointer) {
input.focus();
try {
input.setSelectionRange(nextPosition, nextPosition);
} catch {
// Ignore browsers that do not expose textarea selection during focus changes.
}
}
autoResizeComposer(input);
};
@@ -1247,6 +1253,9 @@ export function render({ navigate, route }) {
emojiPickerOpen = !emojiPickerOpen;
emojiSlot.hidden = !emojiPickerOpen;
emojiToggle.setAttribute('aria-expanded', String(emojiPickerOpen));
// Как в Telegram: либо клавиатура, либо пикер. На тач-устройствах при
// открытии пикера прячем клавиатуру, чтобы поле ввода оставалось видно.
if (emojiPickerOpen && !isFinePointer) input?.blur();
});
screen.addEventListener('pointerdown', (event) => {
if (!emojiPickerOpen || form.contains(event.target)) return;