Очистить репозиторий перед переносом продукта

This commit is contained in:
AidarKC
2026-07-20 18:46:56 +04:00
parent cf33f9a3bd
commit 81493ac4be
665 changed files with 231 additions and 1124 deletions
+21 -21
View File
@@ -1,14 +1,14 @@
import {
TELEGRAM_EMOJI_GROUPS,
getTelegramEmojiAssetUrl,
getTelegramEmojiPreviewUrl,
} from './telegram-emoji-catalog.js?v=202607151545';
TWEMOJI_GROUPS,
getTwemojiAssetUrl,
getTwemojiPreviewUrl,
} from './twemoji-catalog.js?v=202607151545';
const RECENT_STORAGE_KEY = 'shine-ui-recent-emojis-v1';
const MAX_RECENT = 28;
const ALL_EMOJI_ITEMS = TELEGRAM_EMOJI_GROUPS.flatMap((group) => group.items);
const ALL_EMOJI_ITEMS = TWEMOJI_GROUPS.flatMap((group) => group.items);
const EMOJI_ASSET_BY_CHAR = new Map(ALL_EMOJI_ITEMS.map((item) => [item.emoji, item]));
const TELEGRAM_EMOJI_MATCHER = new RegExp(
const TWEMOJI_MATCHER = new RegExp(
[...EMOJI_ASSET_BY_CHAR.keys()]
.sort((left, right) => right.length - left.length)
.map((emoji) => emoji.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'))
@@ -38,11 +38,11 @@ function saveRecent(items) {
}
function appendAnimatedAsset(root, item) {
const previewUrl = item.previewUrl || getTelegramEmojiPreviewUrl(item.file);
const animatedUrl = item.animatedUrl || getTelegramEmojiAssetUrl(item.file);
const previewUrl = item.previewUrl || getTwemojiPreviewUrl(item.emoji);
const animatedUrl = item.animatedUrl || getTwemojiAssetUrl(item.emoji);
const hasAnimation = animatedUrl && animatedUrl !== previewUrl;
const image = document.createElement('img');
image.className = 'telegram-emoji-image';
image.className = 'twemoji-image';
image.alt = '';
image.decoding = 'async';
image.setAttribute('aria-hidden', 'true');
@@ -134,7 +134,7 @@ function appendAnimatedAsset(root, item) {
root.append(image);
}
export function stopAllTelegramEmojiAnimations() {
export function stopAllTwemojiAnimations() {
activeAnimation?.deactivate();
activeAnimation = null;
}
@@ -155,14 +155,14 @@ function createEmojiButton(item, onSelect) {
return button;
}
export function isTelegramAnimatedEmoji(emoji) {
export function hasTwemojiAsset(emoji) {
return EMOJI_ASSET_BY_CHAR.has(emoji);
}
export function createTelegramAnimatedEmoji(emoji, { className = '' } = {}) {
export function createTwemojiAsset(emoji, { className = '' } = {}) {
const item = EMOJI_ASSET_BY_CHAR.get(emoji);
const root = document.createElement('span');
root.className = `telegram-emoji ${className}`.trim();
root.className = `twemoji ${className}`.trim();
root.setAttribute('aria-label', emoji);
root.setAttribute('role', 'img');
@@ -175,16 +175,16 @@ export function createTelegramAnimatedEmoji(emoji, { className = '' } = {}) {
return root;
}
export function appendTelegramAnimatedText(root, text, { className = '' } = {}) {
export function appendTwemojiText(root, text, { className = '' } = {}) {
const source = String(text || '');
let cursor = 0;
let found = false;
TELEGRAM_EMOJI_MATCHER.lastIndex = 0;
for (const match of source.matchAll(TELEGRAM_EMOJI_MATCHER)) {
TWEMOJI_MATCHER.lastIndex = 0;
for (const match of source.matchAll(TWEMOJI_MATCHER)) {
const index = Number(match.index || 0);
if (index > cursor) root.append(document.createTextNode(source.slice(cursor, index)));
root.append(createTelegramAnimatedEmoji(match[0], { className }));
root.append(createTwemojiAsset(match[0], { className }));
cursor = index + match[0].length;
found = true;
}
@@ -224,7 +224,7 @@ export function createEmojiPicker({ onSelect }) {
categoryGrid.className = 'emoji-picker-grid';
categorySection.append(categoryTitle, categoryGrid);
let activeGroup = TELEGRAM_EMOJI_GROUPS[0];
let activeGroup = TWEMOJI_GROUPS[0];
const tabs = new Map();
const rememberAndSelect = (emoji) => {
@@ -238,13 +238,13 @@ export function createEmojiPicker({ onSelect }) {
const recentItems = (loadRecent().length ? loadRecent() : DEFAULT_RECENT)
.map((emoji) => EMOJI_ASSET_BY_CHAR.get(emoji))
.filter(Boolean);
stopAllTelegramEmojiAnimations();
stopAllTwemojiAnimations();
recentGrid.replaceChildren(...recentItems.map((item) => createEmojiButton(item, rememberAndSelect)));
};
const renderGroup = () => {
categoryTitle.textContent = activeGroup.label;
stopAllTelegramEmojiAnimations();
stopAllTwemojiAnimations();
categoryGrid.replaceChildren(...activeGroup.items.map((item) => createEmojiButton(item, rememberAndSelect)));
tabs.forEach((tab, groupId) => {
const active = groupId === activeGroup.id;
@@ -253,7 +253,7 @@ export function createEmojiPicker({ onSelect }) {
});
};
TELEGRAM_EMOJI_GROUPS.forEach((group) => {
TWEMOJI_GROUPS.forEach((group) => {
const tab = document.createElement('button');
tab.type = 'button';
tab.className = 'emoji-picker-tab';
@@ -1,51 +0,0 @@
import { TELEGRAM_EMOJI_GROUP_ACTIVITY } from './telegram-emoji-activity.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_ANIMALS_NATURE } from './telegram-emoji-animals_nature.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_FLAGS } from './telegram-emoji-flags.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_FOOD_DRINK } from './telegram-emoji-food_drink.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_OBJECTS } from './telegram-emoji-objects.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_PEOPLE } from './telegram-emoji-people.js?v=202607151545';
import { TELEGRAM_EMOJI_GROUP_SMILEYS } from './telegram-emoji-smileys.js?v=202607151545';
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_PREVIEW_BASE_URL = '/assets/emoji/telegram-previews/';
function getTelegramEmojiName(file) {
return String(file || '').split('/').pop()?.replace(/\.webp$/i, '') || '';
}
function withPreviewUrls(group) {
return {
...group,
items: group.items.map((item) => ({
...item,
name: item.name || getTelegramEmojiName(item.file),
previewUrl: getTelegramEmojiPreviewUrl(item.file),
animatedUrl: getTelegramEmojiAssetUrl(item.file),
})),
};
}
export const TELEGRAM_EMOJI_GROUPS = [
TELEGRAM_EMOJI_GROUP_SMILEYS,
TELEGRAM_EMOJI_GROUP_PEOPLE,
TELEGRAM_EMOJI_GROUP_ANIMALS_NATURE,
TELEGRAM_EMOJI_GROUP_FOOD_DRINK,
TELEGRAM_EMOJI_GROUP_TRAVEL_PLACES,
TELEGRAM_EMOJI_GROUP_ACTIVITY,
TELEGRAM_EMOJI_GROUP_OBJECTS,
TELEGRAM_EMOJI_GROUP_SYMBOLS,
TELEGRAM_EMOJI_GROUP_FLAGS,
].map(withPreviewUrls);
export function getTelegramEmojiAssetUrl(file) {
// Анимированные 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('/')}`;
}
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_ACTIVITY = {
export const TWEMOJI_GROUP_ACTIVITY = {
"id": "activity",
"label": "Активности",
"icon": "🎉",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_ANIMALS_NATURE = {
export const TWEMOJI_GROUP_ANIMALS_NATURE = {
"id": "animals-and-nature",
"label": "Природа и животные",
"icon": "🌿",
+52
View File
@@ -0,0 +1,52 @@
import { TWEMOJI_GROUP_ACTIVITY } from './twemoji-activity.js?v=202607151545';
import { TWEMOJI_GROUP_ANIMALS_NATURE } from './twemoji-animals_nature.js?v=202607151545';
import { TWEMOJI_GROUP_FLAGS } from './twemoji-flags.js?v=202607151545';
import { TWEMOJI_GROUP_FOOD_DRINK } from './twemoji-food_drink.js?v=202607151545';
import { TWEMOJI_GROUP_OBJECTS } from './twemoji-objects.js?v=202607151545';
import { TWEMOJI_GROUP_PEOPLE } from './twemoji-people.js?v=202607151545';
import { TWEMOJI_GROUP_SMILEYS } from './twemoji-smileys.js?v=202607151545';
import { TWEMOJI_GROUP_SYMBOLS } from './twemoji-symbols.js?v=202607151545';
import { TWEMOJI_GROUP_TRAVEL_PLACES } from './twemoji-travel_places.js?v=202607151545';
const TWEMOJI_SVG_BASE_URL = 'https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/';
function getTwemojiCodepoint(emoji) {
return Array.from(String(emoji || ''))
.map((char) => char.codePointAt(0))
.filter((codepoint) => codepoint && codepoint !== 0xfe0f)
.map((codepoint) => codepoint.toString(16))
.join('-');
}
function withPreviewUrls(group) {
return {
...group,
items: group.items.map((item) => ({
...item,
name: item.name || String(item.emoji || ''),
previewUrl: getTwemojiPreviewUrl(item.emoji),
animatedUrl: getTwemojiAssetUrl(item.emoji),
})),
};
}
export const TWEMOJI_GROUPS = [
TWEMOJI_GROUP_SMILEYS,
TWEMOJI_GROUP_PEOPLE,
TWEMOJI_GROUP_ANIMALS_NATURE,
TWEMOJI_GROUP_FOOD_DRINK,
TWEMOJI_GROUP_TRAVEL_PLACES,
TWEMOJI_GROUP_ACTIVITY,
TWEMOJI_GROUP_OBJECTS,
TWEMOJI_GROUP_SYMBOLS,
TWEMOJI_GROUP_FLAGS,
].map(withPreviewUrls);
export function getTwemojiAssetUrl(emoji) {
return getTwemojiPreviewUrl(emoji);
}
export function getTwemojiPreviewUrl(emoji) {
const codepoint = getTwemojiCodepoint(emoji);
return codepoint ? `${TWEMOJI_SVG_BASE_URL}${codepoint}.svg` : '';
}
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_FLAGS = {
export const TWEMOJI_GROUP_FLAGS = {
"id": "flags",
"label": "Флаги",
"icon": "🏳️",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_FOOD_DRINK = {
export const TWEMOJI_GROUP_FOOD_DRINK = {
"id": "food-and-drink",
"label": "Еда и напитки",
"icon": "🍓",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_OBJECTS = {
export const TWEMOJI_GROUP_OBJECTS = {
"id": "objects",
"label": "Предметы",
"icon": "💡",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_PEOPLE = {
export const TWEMOJI_GROUP_PEOPLE = {
"id": "people",
"label": "Люди",
"icon": "🧑",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_SMILEYS = {
export const TWEMOJI_GROUP_SMILEYS = {
"id": "smileys",
"label": "Смайлы",
"icon": "🙂",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_SYMBOLS = {
export const TWEMOJI_GROUP_SYMBOLS = {
"id": "symbols",
"label": "Символы",
"icon": "❤️",
@@ -1,4 +1,4 @@
export const TELEGRAM_EMOJI_GROUP_TRAVEL_PLACES = {
export const TWEMOJI_GROUP_TRAVEL_PLACES = {
"id": "travel-and-places",
"label": "Путешествия",
"icon": "✈️",
+21 -12
View File
@@ -19,10 +19,10 @@ import {
import { startOutgoingCall } from '../services/call-service.js';
import {
createEmojiPicker,
createTelegramAnimatedEmoji,
appendTelegramAnimatedText,
isTelegramAnimatedEmoji,
stopAllTelegramEmojiAnimations,
createTwemojiAsset,
appendTwemojiText,
hasTwemojiAsset,
stopAllTwemojiAnimations,
} from '../components/emoji-picker.js?v=202607152130';
import { openSpeechInputModal } from '../components/speech-input-modal.js';
import { isSpeechToTextConfigured, isTextToSpeechConfigured, speakTextBySettings } from '../services/speech-tools-service.js';
@@ -473,16 +473,21 @@ function scrollToLatestMessage(list) {
function renderMessageText(textNode, messageText) {
const text = String(messageText || '');
const emoji = text.trim();
if (text === emoji && isTelegramAnimatedEmoji(emoji)) {
textNode.classList.add('bubble-text--animated-emoji');
textNode.append(createTelegramAnimatedEmoji(emoji, { className: 'chat-telegram-emoji' }));
if (text === emoji && hasTwemojiAsset(emoji)) {
textNode.classList.add('bubble-text--twemoji-only');
textNode.append(createTwemojiAsset(emoji, { className: 'chat-twemoji' }));
return;
}
if (appendTelegramAnimatedText(textNode, text, { className: 'chat-telegram-emoji chat-telegram-emoji--inline' })) {
textNode.classList.add('bubble-text--telegram-emoji-inline');
if (appendTwemojiText(textNode, text, { className: 'chat-twemoji chat-twemoji--inline' })) {
textNode.classList.add('bubble-text--twemoji-inline');
}
}
function isTwemojiOnlyMessage(messageText) {
const emoji = String(messageText || '').trim();
return Boolean(emoji && String(messageText || '') === emoji && hasTwemojiAsset(emoji));
}
function scrollToLatestMessageSmart(list, { smoothIfNearBottom = false } = {}) {
if (!list) return;
const scrollContainer = list.closest('.screen-content') || list.parentElement || list;
@@ -547,6 +552,10 @@ function renderLog(list, chatId, { onOpenActions, markAsRead = true, scrollMode
const autoKind = parsedText.callSummary ? 'call-tech' : '';
const bubbleKind = String(msg?.kind || autoKind || '').trim();
bubble.className = `bubble ${msg.from}${bubbleKind ? ` ${bubbleKind}` : ''}`;
const emojiOnly = !parsedText.callSummary && !parsedText.replyRef && isTwemojiOnlyMessage(parsedText.displayText || '');
if (emojiOnly) {
bubble.classList.add('bubble--emoji-only');
}
if (parsedText.callSummary) {
const callCard = document.createElement('div');
@@ -909,7 +918,7 @@ export function render({ navigate, route }) {
const closeEmojiPicker = () => {
if (!emojiPickerOpen) return;
emojiPickerOpen = false;
stopAllTelegramEmojiAnimations();
stopAllTwemojiAnimations();
emojiSlot.hidden = true;
emojiToggle?.setAttribute('aria-expanded', 'false');
};
@@ -1253,7 +1262,7 @@ export function render({ navigate, route }) {
emojiPickerOpen = !emojiPickerOpen;
emojiSlot.hidden = !emojiPickerOpen;
emojiToggle.setAttribute('aria-expanded', String(emojiPickerOpen));
// Как в Telegram: либо клавиатура, либо пикер. На тач-устройствах при
// Либо клавиатура, либо пикер. На тач-устройствах при
// открытии пикера прячем клавиатуру, чтобы поле ввода оставалось видно.
if (emojiPickerOpen && !isFinePointer) input?.blur();
});
@@ -1360,7 +1369,7 @@ export function render({ navigate, route }) {
void sendReadReceiptsForVisible(chatId);
screen.cleanup = () => {
setChatKeyboardOpen(false);
stopAllTelegramEmojiAnimations();
stopAllTwemojiAnimations();
window.removeEventListener('shine-chat-messages-updated', handleIncomingChatRefresh);
window.visualViewport?.removeEventListener('resize', syncKeyboardUi);
window.removeEventListener('resize', syncKeyboardUi);