Внимание пользователя как архитектура надёжности, автоматизации и доверия
Кто такой UX Engineer, что такое SRE подход, и почему Performance Engineering начинается с целостной архитектуры
Вы почувствуете, как интерфейс перестаёт быть обёрткой и становится диалогом — через предзагрузку, память, реакцию, визуальные ритуалы. Как «живое присутствие» достигается не дизайном или текстом, а архитектурой внимания.
Вы увидите, как система сама кэширует, адаптируется, восстанавливается — без дежурных смен и ручного вмешательства. Автоматизация устраняет рутину не добавлением инструментов, а устранением точек отказа. Что PWA может быть отдельным, изолированным слоем общей архитектуры. И как поддержка перестаёт быть «печатальщиками по шаблону» — и превращается в управление диалогом.
Вы прочитаете внедрение Performance Engineering — не как теорию, а как систему. Где TTFB падает до 26 мс не после оптимизации, а как следствие многоуровневого кэширования и навигации с предзагрузкой на основе поведения. Как Lighthouse 100 становится побочным эффектом, а не целью. А производительность перестаёт быть задачей — и становится состоянием по умолчанию.
Сейчас вы в деталях рассмотрите, как работает Performance и UX Engineer со SRE подходом, на примере реальной рабочей среды, запущенной в продакшн в 2019 году, при том что её движок, БД и инфраструктура были выпущены до 2017 года, а поддержка уже несколько лет как прекращена. Даже к 2026 году это остаётся распространённым видом унаследованных платформ. Согласно открытым источникам, взрывной рост электронной коммерции начался в период пандемии и активно продолжался вплоть до 2023 года. Текущая инфраструктура сформировалась именно в этот период — время массового роста e-commerce.
Здесь не будет про React или Vue — потому что задача стояла решать на уровне платформы, а не через фреймворки. Не будет и про Prometheus с Grafana — потому что метрики необходимо было брать напрямую из htop, curl, ab и логов. Нет Kubernetes — потому что оркестрация в данном проекте была избыточна, а надёжность достигалась кодом. Не воспользуемся Loki — потому что есть среда, где достаточно bash, grep, awk. Не будем использовать и Ansible — потому что автоматизация эффективнее реализована встроенными средствами. Пропустим Figma — потому что визуал калибруется в DevTools: задача была — быстро, наглядно, "здесь и сейчас".
Это ни в коем случае не отказ от нужных и эффективных технологий. Это выбор — исходя из масштаба, условий, рентабельности и реальной потребности конкретного проекта. Инструменты не определяют подход. Подход определяет инструменты.
Суть инженера — не в том, какие технологии он использует. Суть — в том, чтобы система работала. Чтобы она была быстрой, надежной, живой — даже если её строят без популярных фреймворков, масштабных инфраструктур, внушительных бюджетов или идеальных условий.
Дальше — условия рабочей среды, цели бизнеса и потребности пользователя — языком инженерии. Процесс реализации и внедрения семи проектов, каждый из которых — элемент фундамента в понимании того, что такое инженерия UX и производительности, когда она строится как единый, измеримый, самодостаточный механизм. А вместо определений — проблемы, решения и измеримый результат.
Знакомимся с работой инженера - как взгляд на подход
Документ системы
Построить систему вокруг одной платформы, чтобы решить реальные операционные и пользовательские проблемы.
Каждый проект - ответ на реальную проблему.
CMS OpenCart 3:
Распространённая архитектура унаследованной платформы - от малых до крупных e-commerce.
Все проекты разрабатывались в условиях:
Задача: реализовать SPA-подобный интерфейс на текущей платформе. Чтобы система работала быстро, стабильно и "ощущалась" живой.
Проекты выстроены в последовательность, отражающую масштаб и глубину внедрения:
Как лестница становления:
Каждый уровень опирается на предыдущий. Бессмысленно делать живой UX, если сайт медленный и "разобранный". Неэффективно автоматизировать поддержку, если операторы выгорают на рутине.
С 2024 года каждый проект прошёл глубокое тестирование, калибровку и многократные итерации. Общие трудозатраты на реализацию и доработку всех семи проектов - более 4500 часов.
Проблема: TTFB достигал 1200 мс, сервер перегружался под нагрузкой, кэширование было фрагментарным.
Решение: Построена 4-уровневая система - OPcache, «Сторонний модуль OpenCart», Brotli, Service Worker.
Измеримый результат: TTFB снизился на 98%, нагрузка на CPU - на 99.3%. Система стабильна с 2024 года.
Решение: Внедрён сбор цепочек переходов, анализ логов, автоматический прогрев и предзагрузка контента.
Измеримый результат: TTFB снижен до 26 мс, время перехода - на 90%, Lighthouse Performance = 100.
Проблема: Отсутствие мобильного приложения, восприятие обычного сайта.
Решение: Реализовано разделение Service Worker на режимы PWA и браузер, внедрёна динамическая предзагрузка страниц, splash-экран, учёт установок.
Измеримый результат: FCP снизился на 71%, трафик - на 42%, Lighthouse = 100.
Проблема: Высокая нагрузка на операторов, рутина, частые ошибки, несогласованность ответов.
Решение: Написано браузерное расширение, с сохранением состояния и семантической вставкой шаблонов.
Измеримый результат: Время ответа сократилось с 45 сек до 8–12, ошибки устранены полностью, обучение сокращено с 5 дней до 1.
Проблема: Все запросы проходили через операторов, отсутствовала база знаний, ответы были медленными.
Решение: Построено диалоговое ядро с 500+ узлами, семантической памятью, визуальным редактором и интеграцией ИИ.
Измеримый результат: 96% запросов обрабатываются без оператора, переводы сокращены до 1 в день, создано 200+ FAQ, база - 173 499 слов.
Проблема: Сомнения пользователей, пользователи не чувствовали «живого» взаимодействия с сайтом.
Решение: Реализована иллюзия присутствия - пульсирующая сфера, уведомления, игровые механики, «ритуалы» ожидания.
Измеримый результат: Конверсия выросла на 89%, время на сайте - на 104%, отказы снизились на 47%, открытие чата - на 10 900%.
Проблема: Даже после всех улучшений интерфейс ощущался «безжизненным». Не было визуального выражения AI-Оператора.
Решение: Полностью переработана визуальная основа интерфейса через CSS и JS - глубина, свечение, анимации, форма, реакция на касание. Сформирован единый визуальный язык.
Измеримый результат: Появилось визуальное выражение AI-Оператора. Элементы интерфейса перестали быть функциями - стали точками диалога. AI-Оператор стал не только слышимым - он стал видимым.
Во всех семи проектах соблюдены одни и те же инженерные и этические принципы:
ab, htop,
curl, логиОни не про отдельные или тоточечные улучшения. Они о построении доверия, заложенного в целостности всей системы.
Основные показатели из рабочей среды проектов:
Скриншот консоли Chrome DevTools: TTFB = 4.7 мс в PWA. Ответ из кэша Service
Worker.
Устройство: ASUS Zenfone 9, соединение: Wi-Fi 2.4GHz.
(() => {
const nav = performance.getEntriesByType('navigation')[0];
if (!nav) {
console.warn('Нет данных о навигации. Обновите страницу.');
return;
}
const ttfb = Math.max(0, nav.responseStart - nav.requestStart);
const fromCache = nav.transferSize === 0;
const scaleMax = 200;
const barLength = 20;
const filledLength = Math.min(barLength, Math.round((ttfb / scaleMax) * barLength));
const emptyLength = barLength - filledLength;
const ttfbColor = fromCache
? '#43A047'
: ttfb < 50 ? '#43A047'
: ttfb < 200 ? '#FB8C00'
: '#E53935';
const sourceLabel = fromCache ? 'Cached (SW)' : 'Network';
const sourceColor = fromCache ? '#43A047' : '#1976D2';
const bar = '█'.repeat(filledLength) + '░'.repeat(emptyLength);
const labelLine = Array(barLength).fill(' ');
labelLine[0] = '0';
if (barLength > 11) {
labelLine[9] = '1';
labelLine[10] = '0';
labelLine[11] = '0';
}
if (barLength > 18) {
labelLine[17] = '2';
labelLine[18] = '0';
labelLine[19] = '0';
}
const labels = labelLine.join('');
console.log('%cTTFB ANALYSIS', 'font-weight: bold; font-size: 14px; color: #ababab;');
console.log(
'%cTime to First Byte: %c' + ttfb.toFixed(1) + ' ms',
'color: #ababab',
`color: ${ttfbColor}; font-weight: bold`
);
console.log(
'%cResponse source: %c' + sourceLabel,
'color: #ababab',
`color: ${sourceColor}; font-weight: bold`
);
console.log('');
console.log('%c' + bar, `color: ${ttfbColor}`);
console.log('%c' + labels, 'color: #ababab; font-family: monospace');
console.log('');
console.log(
'%cTTFB < 50ms: optimal | 50–200ms: acceptable | >200ms: needs improvement',
'font-size: 11px; color: #ababab'
);
})();
Результат:
Этим документом я хотел поделиться своим взглядом на подход к любому сервису: видеть в препятствиях точки роста, а в узких местах возможность создать комплексный, автоматизированный механизм.
Хочется выделить, что действительно считаю важным:
— это и есть Инженерия производительности и UX со SRE подходом, где
Пользователь не доверяет скорости.
Он доверяет вниманию.
А внимание — в архитектуре.
Четырёхуровневое кэширование
ML-Cache
Поведенческая навигация с предзагрузкой
ISPN
Мобильное веб-приложение + SW для браузеров
PWA-Layer
Браузерное расширение для операторов
OPS-UX
Диалоговое ядро с памятью и LLM
DialogCore
«Живой диалог» UX с платформой
AI-Оператор
Современный UI как отклик
UX-Vibe
Теперь, когда вы познакомились с тем, как производительность, надёжность и UX становятся вниманием, а внимание лежит в основе архитектуры, — пришло время погрузиться в детали.
Далее — семь проектов, каждый из которых создаёт уровень внимания пользователя.
Архитектура производительности для OpenCart без миграции
На e-commerce платформе на базе OpenCart:
ML-Cache — это система из 4 уровней, интегрированных в существующую инфраструктуру:
Система работает как единый механизм: каждый уровень дополняет следующий, обеспечивая стабильность, скорость и отказоустойчивость.
ab, htop,
curl, логиЦепочка обработки: запрос идёт через Apache → PHP-FPM → OPcache → OpenCart → MySQL (InnoDB Buffer Pool). Ответ кэшируется на каждом уровне. Клиентское кэширование — через Service Worker.
| Метрика | До | После | Улучшение |
|---|---|---|---|
| Нагрузка на CPU | 80–100% | средне 0.73% | ↓ 99.3% |
| Потребление RAM | ~9 ГБ | 6.3 ГБ (из 15 ГБ) | ↑ Эффективное использование |
| ab -n 1000 -c 100 | Частые таймауты | успешно завершён | ↑ Устойчивость к нагрузке |
| MySQL в памяти | MyISAM, кэш не использовался | InnoDB Buffer Pool: 3.5 ГБ активно | ↑ Скорость запросов |
| OCSP Stapling | Не настроено | включено, работает | ↑ Безопасность + скорость SSL |
OPcache хранит скомпилированный байт-код PHP в оперативной памяти, исключая повторную компиляцию при каждом запросе. Это позволяет ускорить исполнение скриптов в 3–5 раз.
; /home/user/etc/php71w/php.d/10-opcache.ini opcache.enable=1 opcache.memory_consumption=2048 opcache.interned_strings_buffer=32 opcache.max_accelerated_files=100000 opcache.revalidate_freq=0 opcache.validate_timestamps=0 opcache.fast_shutdown=1 opcache.huge_code_pages=1
Результат: PHP-скрипты не компилируются при каждом запросе — исполнение в 3–5 раз быстрее.
Для ускорения генерации HTML-страниц используется сторонний модуль «Сторонний модуль OpenCart». Он не является частью ядра OpenCart, но был выбран после анализа альтернатив и интегрирован в инфраструктуру.
Модуль отвечает за:
Важно: «Сторонний модуль» не заменяет другие уровни кэширования, а дополняет их:
Таким образом, модуль стал частью единой системы кэширования, а не изолированным решением.
Оптимизация трафика и управление кэшированием на уровне HTTP.
# .htaccess <IfModule mod_brotli.c> AddOutputFilterByType BROTLI_COMPRESS text/html text/css application/javascript </IfModule> <IfModule mod_expires.c> ExpiresByType text/css A31536000 ExpiresByType image/webp A2592000 ExpiresByType application/json A86400 </IfModule> <IfModule mod_headers.c> Header set Cache-Control "public, max-age=31536000, immutable" env=immutable Header set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" Header set X-Frame-Options "SAMEORIGIN" Header set X-Content-Type-Options "nosniff" Header set X-XSS-Protection "1; mode=block" </IfModule>
Результат: экономия трафика до 40%, защита от XSS, кликджекинга, HSTS для безопасности и скорости SSL.
Автоматический прогрев "холодных" страниц до обращения пользователя.
# crontab -l 0 */5 * * * /home/user/site/scripts/preload_pages.sh 30 */1 * * * /home/user/site/scripts/preload_pages_2.sh */15 * * * * /home/user/site/scripts/preload_pages_3.sh
Скрипты используют curl с разными User-Agent (ПК/мобильный), чтобы прогреть кэш для всех устройств.
Разделение стратегий кэширования:
/service-worker.js — для PWA: оффлайн, кэширование статики, предзагрузка/browser/service-worker.js — для браузеров: только кэширование HTML/CSS/JS, без оффлайнРазделение позволяет избежать конфликтов и оптимизировать под среду исполнения.
Настройка InnoDB под 12 ГБ RAM и NVMe-диск.
# /etc/my.cnf innodb_buffer_pool_size = 8G innodb_buffer_pool_instances = 6 innodb_flush_method = O_DIRECT innodb_log_file_size = 2G innodb_io_capacity = 2000 innodb_read_io_threads = 8 innodb_write_io_threads = 8 # Ускорение временных таблиц tmp_table_size = 512M max_heap_table_size = 512M # Отключён Query Cache — неэффективен query_cache_type = 0 query_cache_size = 0
Решение: Query Cache отключён — он блокирует запись при высокой нагрузке. Вместо него — InnoDB Buffer Pool и кэширование на уровне приложения.
На этапе проектирования архитектуры было рассмотрено несколько решений для кэширования, включая Redis.
Был выполнен полный цикл:
Cache\Redis с поддержкой pconnect,
таймаутов, сериализацииab, прогрев кэша, анализ INFO MEMORY,
SLOWLOGВывод:
Вместо внешних решений был выбран путь полного контроля: OPcache и APCu — для кэширования на сервере, Brotli — для сжатия, «Сторонний модуль OpenCart» — как оптимизированное решение для HTML-кеширования, и два кастомных Service Worker, разработанные и внедрённые в рамках проекта.
Такой подход обеспечил:
Код интеграции с Redis был реализован полностью, но оставлен как R&D-решение — не введено в продакшен.
<?php
namespace Cache;
class Redis {
private $expire;
private $cache;
public function __construct($expire) {
$this->expire = $expire;
$this->cache = new \Redis();
if (!$this->cache->pconnect(CACHE_HOSTNAME, CACHE_PORT)) {
error_log("Не удалось подключиться к Redis");
return false;
}
if (defined('CACHE_PASSWORD') && !empty(CACHE_PASSWORD)) {
if (!$this->cache->auth(CACHE_PASSWORD)) {
error_log("Неверный пароль Redis");
return false;
}
}
if (defined('\Redis::OPT_CONNECT_TIMEOUT')) {
$this->cache->setOption(\Redis::OPT_CONNECT_TIMEOUT, 1);
} else {
$this->cache->setTimeout(1, 0);
}
if (defined('\Redis::OPT_READ_TIMEOUT')) {
$this->cache->setOption(\Redis::OPT_READ_TIMEOUT, 1);
} else {
$this->cache->setTimeout(1, 1);
}
if (defined('\Redis::OPT_WRITE_TIMEOUT')) {
$this->cache->setOption(\Redis::OPT_WRITE_TIMEOUT, 1);
} else {
$this->cache->setTimeout(1, 2);
}
}
public function get($key) {
$fullKey = CACHE_PREFIX . $key;
$data = $this->cache->get($fullKey);
return $data ? unserialize($data) : null;
}
public function set($key, $value) {
$fullKey = CACHE_PREFIX . $key;
$encodedValue = serialize($value);
return $this->cache->setex($fullKey, $this->expire, $encodedValue);
}
public function delete($key) {
$fullKey = CACHE_PREFIX . $key;
return $this->cache->delete($fullKey);
}
public function clear() {
$keys = $this->cache->keys(CACHE_PREFIX . '*');
if (!empty($keys)) {
return $this->cache->delete($keys);
}
return true;
}
}
Изначально была настроена полная обработка ресурсов через Brotli — как наиболее эффективный алгоритм сжатия. Однако при диагностике выяснилось, что часть файлов (в первую очередь HTML-ответы) продолжает отдаваться в формате GZIP.
Причиной оказался модуль ускорения («Сторонний модуль OpenCart»), участвующий в цепочке обработки:
Для проверки гипотезы была проведена консультация с разработчиками модуля. Их позиция:
Это логично: при высокой нагрузке экономия CPU важнее экономии трафика на 10–15%. Переупаковка HTML через Brotli при каждом запросе убила бы выигрыш от кэширования.
Было принято решение:
Результат: оптимальный баланс между эффективностью сжатия и минимальной нагрузкой на CPU. Архитектура работает быстро — без "лишних" операций.
/home/user/site/
├── scripts/
│ ├── preload_pages.sh
│ ├── preload_pages_2.sh
│ ├── preload_pages_3.sh
│ └── txt/
│ ├── pages_to_warm.txt
│ ├── pages_to_warm_2.txt
│ └── pages_to_warm_3.txt
│
├── .htaccess # Безопасность, Brotli, заголовки
├── service-worker.js # Для PWA
└── browser/
└── service-worker.js # Для браузеров
/etc/
├── my.cnf # InnoDB, buffer pool, логи
├── php71w/
│ └── php.d/10-opcache.ini # Настройка OPcache
└── httpd/conf/ # Apache: OpenCart, SEO, сжатие, заголовки
Для проверки работоспособности использовались:
# Нагрузочное тестирование ab -n 1000 -c 100 https://example.com/ # Проверка конфигов apachectl configtest # Мониторинг htop free -h mpstat 1 5 df -h # Проверка заголовков curl -I https://example.com/
ML-Cache — системный подход к производительности. Каждый уровень — от OPcache до Service Worker — работает как часть единого механизма.
Результат — не "быстрее", а предсказуемо, стабильно, безопасно быстрее. Система устойчива к нагрузке, эффективно использует ресурсы и не требует внешних зависимостей.
Этот проект — технический фундамент для последующих оптимизаций, включая системы предиктивной навигации и интеллектуального кэширования.
Снижение TTFB до 26 мс на OpenCart 3
На сайте e-commerce на базе OpenCart 3:
ISPN — это система, которая:
Замкнутый цикл: Сбор → Анализ → Прогрев → Предзагрузка → Ускорение
| Метрика | До | После | Улучшение |
|---|---|---|---|
| TTFB | 1200 мс | 26 мс | ↓ 98% |
| Время перехода | 800–1200 мс | 100–200 мс | ↓ 70–90% |
| Нагрузка на поддержку | Высокая | ↓ 70% | Пользователи довольны |
| Стабильность FPS | Частые проседания | 95% сессий — 55–60 FPS | Улучшение плавности |
ISPN делает "холодные" страницы "горячими". В Lighthouse измеряется уже клиентская производительность после предзагрузки, что объясняет высокие оценки.
| Метрика | Результат | Оценка |
|---|---|---|
| First Contentful Paint (FCP) | 0.5 с | 100 |
| Largest Contentful Paint (LCP) | 0.6 с | 100 |
| Total Blocking Time (TBT) | 0 мс | 100 |
| Cumulative Layout Shift (CLS) | 0.001 | 100 |
| Speed Index (SI) | 0.7 с | 100 |
tracker.js.trackОтслеживает pushState, popstate, формирует цепочки и отправляет через
sendBeacon.
// Пример цепочки
localStorage.nav_stats = {
"/ → /category": 5,
"/category → /products": 3
};
log-stats.phpПринимает JSON, проверяет безопасность, пишет в user-paths.log (вне public_html).
[2025-04-05 12:30:15] abc123 | / → /category → /products | https://example-store.com/
analyze-paths.phpЧитает последние 500 строк, формирует top-paths.json.
{
"urls": ["/category", "/products", "/for-women", "/lifestyle", "/wellness"]
}
Запуск: Каждые 30 минут через CRON
warm-cache.phpОтправляет GET-запросы для прогрева.
# CRON: каждые 15 минут */15 * * * * php /cron/warm-cache.php
prefetch.js.prefОпределяет, что предзагрузить:
Использует ajaxCache, <link rel="prefetch">, fetch().
Две версии:
service-worker.js — для PWAbrowser-service-worker.js — для браузераФункции: кэширование, оффлайн, предзагрузка.
excluded-routes.js.routeИсключает /cart, /checkout, /api, статику.
Срабатывает при нахождении на страницах с предсказуемой навигацией:
/category, /for-women, /products,
/specials, /latest/products, /wellness,
/lifestyle/category?page=2 →
/category?page=3/search?query=...&page=1 →
&page=2Порядок предзагрузки определяется на основе поведенческой модели, чтобы более вероятные пути загружались первыми.
Система использует два источника:
localStorage.nav_stats — локальная история переходовtop-paths.json — глобальный топ с сервераАлгоритм:
top-paths.json (тренды)nav_stats + top-paths.json (персонализация)/category на /wellness — этот путь становится
приоритетомПример локальной статистики:
localStorage.nav_stats = {
"/ → /category": 7,
"/category → /wellness": 4,
"/wellness → /products": 3
};
/home/user/project/ ├── storage/ │ └── logs/ │ └── user-paths.log # Лог поведения (вне public_html) │ ├── site-root/ │ ├── log-stats.php # Приём данных │ │ │ ├── cron/ │ │ ├── analyze-paths.php # Анализ логов → top-paths.json │ │ ├── warm-cache.php # Прогрев серверного кэша │ │ └── top-paths.json # Топ URL для предзагрузки │ │ │ ├── catalog/view/theme/js/ │ │ ├── tracker.js.track # Сбор цепочек переходов │ │ ├── prefetch.js.pref # Движок предзагрузки │ │ └── excluded-routes.js.route # Список исключённых URL │ │ │ ├── service-worker.js # Для PWA (оффлайн, предзагрузка) │ └── browser/ │ └── browser-service-worker.js # Для браузера ПК и смартфоны (предзагрузка)
top-paths.json для сегментов (мужчины, женщины)ISPN ускоряет, но не должен мешать. Поэтому введён механизм защиты слабых устройств и критических сценариев.
На ключевых этапах предзагрузка отключается:
/cart, /checkout — чтобы не мешать процессу оформления/search?query=...) — чтобы не тратить трафик при вводе и
фильтрацииПредзагрузка включается при листании (например, /search?query=...&page=2).
const p = location.pathname;
const q = location.search;
if (
p.includes('/cart') ||
p.includes('/checkout') ||
(p.includes('/search') && !q.includes('page=')) ||
q.includes('route=product/search') ||
q.includes('route=product/category') ||
q.includes('route=journal3/filter') ||
q.includes('route=journal3/blog')
) {
window.__perfGuard_criticalAnimation = true;
}
PerfGuard отслеживает:
requestAnimationFramenavigator.getBatteryПри обнаружении проблем — автоматически очищает:
setInterval, setTimeoutMutationObserverWebSocketrequestAnimationFrameСрабатывает при:
Философия ISPN: Предзагрузка работает, когда устройство "отдыхает", и никогда не мешает основной задаче — навигации.
ISPN — это самообучающаяся система, которая ускоряет, учится и уважает пользователя.
Система прошла полный цикл: от гипотезы → реализации → измерения → оптимизации. Результат — не "просто быстрее", а предсказуемо, стабильно, этично быстрее.
Устойчивая система ускорения, встроенная в инфраструктуру и рост пользовательской базы.
Создание нативно воспринимаемого интерфейса на базе веб-платформы
80,11% трафика приходит со смартфонов. Пользователи ждут:
Текущий опыт:
Один Service Worker не может обслуживать два разных режима:
Решение — два независимых, но синхронизированных SW:
| Функция | PWA-SW | Browser-SW |
|---|---|---|
| Файл | service-worker.js |
browser-service-worker.js |
| Кэш статики | app-pwa-cache-v1 |
app-browser-cache-v1 |
| Динамический кэш | dynamic-pages-v1 |
dynamic-pages-v1-browser |
| Оффлайн | Да | Нет |
| Splash | Да | Нет |
| Активация | Да (POST /pwa/activated) |
Нет |
const isPWA = matchMedia('(display-mode: standalone)').matches ||
(typeof navigator.standalone !== 'undefined' && navigator.standalone);
if (isPWA) {
navigator.serviceWorker.register('/service-worker.js');
} else {
navigator.serviceWorker.register('/browser/browser-service-worker.js');
}
Ключевой механизм производительности. Обеспечивает мгновенные переходы за счёт предзагрузки HTML-страниц.
ISPN (tracker.js.track) отслеживает цепочки переходов и отправляет их в SW через
postMessage.
self.addEventListener('message', (event) => {
if (event.data.type === 'nav_stats') {
const urls = Object.keys(event.data.data)
.map(chain => chain.split(' → ').pop())
.filter(u => u && u !== '/' && !isExcluded(self.location.origin + u));
urls.forEach(url => precacheDynamicPage(self.location.origin + url));
}
});
let dynamicFetchController = null;
// Список URL, исключённых из кэширования
const EXCLUDED_PATTERNS = [
'cart', 'checkout', 'account', 'payment', 'api', 'feed', 'authorization',
'login', 'logout', 'register', 'confirm', 'download'
];
function isExcluded(url) {
return EXCLUDED_PATTERNS.some(pattern => url.includes(pattern));
}
async function precacheDynamicPage(url) {
if (isExcluded(url)) return;
try {
if (dynamicFetchController) dynamicFetchController.abort();
dynamicFetchController = new AbortController();
const response = await fetch(url, { signal: dynamicFetchController.signal });
if (!response.ok) return;
const html = await response.text();
const pageCache = await caches.open(DYNAMIC_PAGE_CACHE);
await pageCache.put(url, new Response(html, { headers: response.headers }));
} catch (e) {
if (e.name !== 'AbortError') {
console.warn('Ошибка при предкэшировании:', url, e);
}
} finally {
dynamicFetchController = null;
}
}
async function cleanupPageCache(maxCount = 20) {
const cache = await caches.open(DYNAMIC_PAGE_CACHE);
const keys = await cache.keys();
if (keys.length <= maxCount) return;
const toDelete = keys.slice(0, keys.length - maxCount);
await Promise.all(toDelete.map(request => cache.delete(request)));
}
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (url.pathname.endsWith('.php') || url.pathname === '/') {
event.respondWith(
caches.match(event.request).then(async (cached) => {
if (cached) return cached;
const dynamicCache = await caches.match(event.request, { cacheName: DYNAMIC_PAGE_CACHE });
if (dynamicCache) return dynamicCache;
return fetch(event.request).catch(() => caches.match('/offline.html'));
})
);
}
});
self.addEventListener('install', (event) => {
event.waitUntil(
(async () => {
const cache = await caches.open(CACHE_NAME);
await Promise.allSettled(
staticResources.map(async (url) => {
const controller = new AbortController();
setTimeout(() => controller.abort(), 8000);
try {
const response = await fetch(url, { signal: controller.signal });
if (response.ok) {
await cache.put(url, response.clone());
}
} catch (e) {
console.warn(`Не удалось кэшировать ${url}`, e);
}
})
);
})()
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
Promise.all([
self.skipWaiting(),
self.clients.claim(),
cleanupCache(CACHE_NAME, MAX_STATIC_SIZE),
cleanupPageCache(20),
fetch('/index.php?route=pwa/activated', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ activated: true, sw_version: CACHE_NAME }),
credentials: 'same-origin'
}).catch(() => {})
])
);
});
Service Worker применяет разные стратегии в зависимости от типа ресурса:
| Тип | Стратегия | Особенности |
|---|---|---|
| HTML | Cache-first + фоновое обновление | Приоритет — кэш, обновление в фоне, fallback на оффлайн |
| Статика (JS, CSS, шрифты) | Precache при установке | Всегда из кэша, обновляется при новой версии SW |
| Изображения | Cache-first, fallback к логотипу | Если нет в кэше — подставляется заглушка |
| API, внешние сервисы | Сеть → fallback к кэшу (если есть) | Аналитика — не кэшируются жёстко |
| Метрика | До | После | Улучшение |
|---|---|---|---|
| FCP | 2.1 с | 0.6 с | 71% |
| LCP | 3.4 с | 0.7 с | 79% |
| Установки | 0 | 45+ | Новый канал дистрибуции |
| Трафик (сессия) | 2.4 МБ | 1.4 МБ | ↓ 42% |
Автоматическая подборка под устройство:
// Из header.twig
const startupImages = [
{ href: '/image/app_icon/startup-640x1136.webp', media: '(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)' },
{ href: '/image/app_icon/startup-750x1334.webp', media: '(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)' },
{ href: '/image/app_icon/startup-1170x2532.webp', media: '(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3)' }
];
startupImages.forEach(img => {
const link = document.createElement('link');
link.rel = 'apple-touch-startup-image';
link.href = img.href;
if (img.media) link.media = img.media;
document.head.appendChild(link);
});
При установке:
device_id в localStorage/log-install.phppwa_installУникальный идентификатор для отслеживания установки:
function getDeviceId() {
let id = localStorage.getItem('pwa_device_id');
if (!id) {
id = 'pwa_' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('pwa_device_id', id);
}
return id;
}
install_id | int(11) | PK, AUTO_INCREMENT date_installed | datetime | NOT NULL device_id | varchar(50) | UNIQUE INDEX
Гарантирует: одна установка — одна запись.
Решение полностью интегрировано в проект без внешних зависимостей. Все компоненты находятся в локальной файловой системе:
/project-root/
├── manifest.json
├── offline.html
├── service-worker.js
├── browser/
│ └── browser-service-worker.js
├── image/app_icon/
│ ├── web_app_logo_*.png
│ ├── ios-icon-*.png
│ ├── startup-*.webp
│ └── custom_splash.webp
├── log-install.php
├── pwa/
│ └── controller/
│ ├── activated.php
│ └── installation_count.php
└── theme/
└── common/
├── header.twig
└── footer.twig
Такая организация обеспечивает:
Показывается через 90 секунд после входа, если браузер поддерживает PWA:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
setTimeout(() => {
if (deferredPrompt && !localStorage.getItem('install_shown')) {
showInstallModal();
localStorage.setItem('install_shown', '1');
}
}, 90000);
});
function showInstallModal() {
const modal = document.createElement('div');
modal.innerHTML = \`
<div class="pwa-modal">
<h3>Добавить на экран?</h3>
<p>Установите приложение для быстрого доступа и оффлайна.</p>
<button onclick="installApp()">Установить</button>
<button onclick="closeModal()">Отмена</button>
</div>\`;
document.body.appendChild(modal);
}
async function installApp() {
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
if (outcome === 'accepted') {
fetch('/log-install.php', {
method: 'POST',
body: JSON.stringify({ device_id: getDeviceId() })
});
}
deferredPrompt = null;
}
closeModal();
}
PWA-Layer — не конечная точка, а основа для дальнейшего роста:
PWA-Layer представляет собой инженерную систему, построенную на разделении контекстов и предиктивном управлении ресурсами. Архитектура обеспечивает:
Решение не ограничивается реализацией PWA — оно трансформирует веб-интерфейс в автономный, измеримый, продуктивный канал, воспринимаемый как нативное приложение.
PWA-Layer и ISPN формируют единый канал: первый обеспечивает нативный UX, второй — предиктивную скорость. Вместе они создают веб-интерфейс, который пользователь не отличает от нативного приложения.
Это — архитектура, построенная на веб-стандартах, но работающая как нативное приложение.
Результат — не скорость. Результат — доверие. Пользователь не чувствует разницы. Значит, система работает.
Реактивная система автоматизации для операторов на базе Chrome Extensions
Операторы службы поддержки в e-commerce работают в условиях:
OPS-UX:
Система построена на базе Chrome Extensions (Manifest V3):
| Компонент | Файл | Назначение |
|---|---|---|
| Ядро | manifest.json |
Разрешения, хосты, контент-скрипты |
| Модуль доставки | content_delivery.js |
Шаблоны на страницах доставки |
| Модуль поддержки | content_support.js |
Комментарии и шаблоны для операторов чата |
// manifest.json
{
"manifest_version": 3,
"name": "OPS-UX",
"version": "2.1",
"permissions": ["activeTab", "scripting"],
"host_permissions": [
"https://*/delivery-console/*",
"https://*/support-panel/*"
],
"content_scripts": [
{
"matches": ["https://*/delivery-console/*"],
"js": ["content_delivery.js"]
},
{
"matches": ["https://*/support-panel/*"],
"js": ["content_support.js"]
}
]
}
Все данные хранятся в памяти и синхронизируются с интерфейсом.
let street = ''; let apartment = ''; let phone = ''; let noBuilding = false; let leaveAtDoor = false; let callUponArrival = false;
const updateTextElements = () => {
if (textElements.length >= 3) {
const streetWithBuilding = noBuilding ? `${street} [без корпуса]` : street;
const additionalInstructions = [];
if (leaveAtDoor) additionalInstructions.push("Оставить у двери");
if (callUponArrival) additionalInstructions.push("Позвонить по приезду");
const messages = [
"Ваш заказ готовят к доставке по адресу",
"Ваш заказ собран и ожидает назначения курьера, с доставкой по адресу",
"В ближайшее время удобно будет принять курьера по адресу"
];
for (let i = 0; i < 3; i++) {
let fullMessage = messages[i] + ':';
if (streetWithBuilding) fullMessage += `\nУлица: ${streetWithBuilding}`;
if (apartment) fullMessage += `\nКвартира: ${apartment}`;
if (phone) fullMessage += `\nТелефон получателя: ${formatPhone(phone)}`;
if (additionalInstructions.length > 0) fullMessage += `\nДополнительно: ${additionalInstructions.join(', ')}`;
textElements[i].textContent = fullMessage;
}
}
};
Каждое изменение поля — вызывает updateTextElements(). Это реактивность уровня SPA.
const saveData = () => {
localStorage.setItem('opsux_deliveryData', JSON.stringify({
street, apartment, phone, noBuilding, leaveAtDoor, callUponArrival
}));
};
const loadData = () => {
const saved = localStorage.getItem('opsux_deliveryData');
if (saved) {
const data = JSON.parse(saved);
street = data.street || '';
apartment = data.apartment || '';
phone = data.phone || '';
noBuilding = data.noBuilding || false;
leaveAtDoor = data.leaveAtDoor || false;
callUponArrival = data.callUponArrival || false;
updateTextElements();
}
};
// Вызывается при запуске
loadData();
// Срабатывает при каждом вводе
streetInput.addEventListener('input', () => {
street = e.target.value;
updateTextElements();
saveData();
});
const formatPhone = (rawPhone) => {
let cleaned = rawPhone.replace(/\D/g, '');
if (cleaned.startsWith('8')) cleaned = '7' + cleaned.slice(1);
if (cleaned.startsWith('7')) cleaned = cleaned.slice(1);
if (cleaned.length === 10) {
return cleaned.replace(/(\d{3})(\d{3})(\d{2})(\d{2})/, '$1 $2 $3 $4');
}
return rawPhone;
};
item.addEventListener('click', () => {
const textarea = document.querySelector('[data-qa-id="main-input-element"]');
if (textarea) {
textarea.value += item.textContent + '\n';
textarea.dispatchEvent(new Event('input', { bubbles: true }));
}
});
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
Array.from(mutation.addedNodes).forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
const role = node.getAttribute('data-role');
const uiHint = node.getAttribute('data-ui');
if (role === 'system-banner' || uiHint === 'admonition') {
node.remove();
}
}
});
});
});
observer.observe(document.body, { childList: true, subtree: true });
Система следит за DOM и чистит интерфейс — даже при динамической загрузке.
| Метрика | До | После | Улучшение |
|---|---|---|---|
| Среднее время ответа | 45 сек | 8–12 сек | ↓ 75–85% |
| Ошибки в телефонах | Регулярно | 0 | ↓ 100% |
| Количество жалоб курьеров | Ежедневно | Раз в неделю | ↓ 90% |
| Обучение нового оператора | 3–5 дней | 1 день | ↓ 70% |
| Консистентность ответов | Низкая | Высокая | ↑ 100% |
Система OPS-UX успешно работает в продакшене с октября 2024 года.
| Показатель | Результат |
|---|---|
| Количество пользователей | 12 операторов |
| Среднее время работы в день | 6–8 часов |
| Жалобы на работу расширения | 0 |
| Сбои, падения, ошибки | 0 |
| Обратная связь от команды | «Теперь без него — как без рук» |
OPS-UX — это операционная система поддержки, которая:
Это пример UX-инжиниринга, который не просто улучшает интерфейс — он меняет культуру работы.
Масштабируемое ядро поддержки с семантической памятью, ручной калибровкой и ИИ
Раньше все диалоги в поддержке обрабатывались только операторами. Это приводило к:
DialogCore — это диалоговая машина, которая:
Система построена на трёх слоях:
| Слой | Технология | Процент запросов |
|---|---|---|
| 1. Правила + FAQ | 500+ узлов, условия, маршрутизация | 70% |
| 2. ИИ-обработка | Интеграция с ИИ-движком | 20% |
| 3. Оператор | Перевод с контекстом | ≤10% |
Все каналы ведут в единое ядро диалогов.
Логика обновляется через Graph — визуальный конструктор смартапов.
Изменения применяются без перезапуска. Правки можно вносить — без кода.
Система прошла полугодовой цикл разработки, за которым последовал шестимесячный период итеративной калибровки — чтобы быть максимально релевантной пользователям. Уточнения, fallback, фразы — всё оттачивалось под реальные диалоги.
Ниже — фрагмент реального диалогового графа из продакшен-среды.
graph TD
A["начало"] --> B["newNode_***: Приветствие + согласие"]
B --> C["newNode_***: Здравствуйте!"]
C --> D["newNode_***: Чем могу помочь?"]
D --> E["newNode_**: Главное меню"]
%% === Главное меню (центральный хаб) ===
E --> F["📍 Режим работы и контакты → /newNode_**"]
E --> G["⚡ Действия с заказом → /newNode_***"]
E --> H["📦 Информация о доставке → /newNode_***"]
E --> J["⭐ Отзывы → /newNode_***"]
E --> K["📝 Обратная связь → /newNode_***"]
E --> L["🛠️ Техподдержка → /newNode_***"]
E --> M["🚪 Выход → /newNode_**"]
%% --- 1. Режим работы и контакты ---
F --> F1["newNode_**: Информация о работе, адрес, email, соцсети"]
F1 --> E
%% --- 2. Действия с заказом ---
G --> G0["Проверка времени: $conditions.checkIfFitsSchedule"]
G0 -->|Да| G1["newNode_***: Получить заказ"]
G0 -->|Нет| G2["newNode_***: Операторы не доступны"]
G1 --> G3["newNode_**: Номер заказа или телефона?"]
G3 --> G4["/newNode_**: Выбор способа"]
G4 --> G5["/newNode_**: По телефону"]
G4 --> G6["/newNode_**: По номеру заказа"]
G5 --> G7["newNode_***: Одну минуту..."]
G6 --> G7
G7 --> G8["newNode_***: Передача оператору"]
G8 --> G9["newNode_***: TransferToOperator"]
G --> G10["📦 Готов принять курьера → /newNode_***"]
G --> G11["🚘 Запросить маршрут курьера → /newNode_***"]
G11 --> G12["/newNode_***: Уточнение данных"]
G12 --> G13["/newNode_***: Выбор"]
G13 --> G14["/newNode_***: По телефону"]
G13 --> G15["/newNode_***: По номеру"]
G14 --> G16["newNode_***: Одну минуту..."]
G15 --> G16
G16 --> G17["newNode_***: Передача операторам"]
G17 --> G18["newNode_***: Обещание прислать маршрут"]
G18 --> G19["newNode_***: TransferToOperator"]
G19 --> G20["newNode_***: Задержка"]
G20 --> G21["newNode_***: Ожидание 20 мин"]
G21 --> G22["newNode_***: Ждите ответа"]
G22 --> G23["newNode_***: EndSession"]
G --> G24["Редактировать заказ → /newNode_***"]
G --> G25["Продлить хранение → /newNode_***"]
G --> G26["Уточнить статус оплаты → /newNode_***"]
G --> G27["Отменить заказ → /newNode_***"]
G --> G28["Уточнить информацию о заказе → /newNode_***"]
G24 --> G0
G25 --> G0
G26 --> G0
G27 --> G0
G28 --> G0
%% --- 3. Доставка ---
H --> H1["newNode_***: Способы доставки"]
H1 --> H2["Самовывоз — бесплатно"]
H1 --> H3["Экспресс-курьер — от *** ₽"]
H1 --> H4["ТК — от *** ₽"]
H1 --> H5["Почта России — *** ₽"]
H1 --> H6["/newNode_***: Сроки доставки"]
H1 --> H7["/newNode_***: Как заказать экспресс"]
H1 --> H8["/newNode_***: Срок резерва (2/7 дней)"]
H1 --> E
graph TD
%% --- 4. Упаковка и вручение ---
I --> I1["newNode_***: Сейф-пакеты, проверка до оплаты"]
I1 --> E
%% --- 5. Отзывы ---
J --> J1["newNode_***: Ссылки на 2ГИС, Яндекс, Google"]
J1 --> E
%% --- 6. Обратная связь ---
K --> K1["/newNode_***: Общие вопросы"]
K --> K2["/newNode_***: Технические проблемы"]
K --> K3["/newNode_***: Вопросы по составу заказа"]
K3 --> K4["newNode_***: Описание проблемы"]
K4 --> K5["newNode_***: Скриншот"]
K5 --> K6["newNode_***: Фото товара"]
K6 --> K7["newNode_***: Email"]
K7 --> K8["newNode_***: SendEmail на ..."]
K8 --> K9["newNode_***: Сообщение отправлено"]
K9 --> E
%% --- 7. Техподдержка ---
L --> L1["/newNode_***: Сложности с эксплуатацией"]
L --> L2["/newNode_***: Источник питания?"]
L2 --> L3["/newNode_***: Батарейки → рекомендации"]
L2 --> L4["/newNode_***: Описать проблему"]
L2 --> L5["/newNode_***: Аккумулятор"]
L4 --> L6["newNode_***: Прикрепить файл"]
L5 --> L6
L6 --> L7["newNode_***: Телефон, email"]
L7 --> L8["newNode_***: TransferToOperator"]
%% --- 8. Рекламации и возврат ---
E --> N["/newNode_***: Рекламация"]
E --> O["/newNode_***: Возврат товара"]
E --> P["/newNode_***: Обмен"]
O --> O1["newNode_***: Возврат при браке, ссылки на правила"]
O1 --> O2["newNode_***: Оформить рекламацию?"]
O2 --> O3["Да → /newNode_***: Причина"]
O3 --> O4["newNode_***: Контакты"]
O4 --> O5["newNode_***: TransferToOperator"]
O2 --> O6["Нет → /newNode_***: До свидания"]
O6 --> O7["newNode_***: Сбор данных"]
O7 --> O5
%% --- 9. Оформление заказа ---
E --> Q["/newNode_**: Оформить в чате?"]
E --> R["/newNode_***: Как оформить? → /newNode_163: Через сайт"]
Q --> Q1["newNode_**: Укажите номера моделей"]
Q1 --> Q2["newNode_**: Ввод моделей"]
Q2 --> Q3["newNode_**: Ещё добавить?"]
Q3 --> Q4["Да → /newNode_**: Добавить"]
Q3 --> Q5["Нет → /newNode_**: Способ получения"]
Q5 --> Q6["/newNode_**: Самовывоз или курьер"]
Q6 --> Q7["/newNode_**: Самовывоз → ввод телефона"]
Q6 --> Q8["/newNode_**: Курьер → ввод адреса"]
Q7 --> Q9["newNode_**: Подтверждение"]
Q8 --> Q10["newNode_***: Подтверждение"]
Q9 --> Q11["newNode_**: TransferToOperator"]
Q10 --> Q12["newNode_***: TransferToOperator"]
%% --- 10. Завершение диалога ---
M --> M1["newNode_**: До новых встреч!"]
M1 --> M2["newNode_**: EndSession"]
%% --- 11. Системные интенты ---
E --> S1["$HELLO → /newNode_***: Пожалуйста! → /newNode_**"]
E --> S2["$THANKS → /newNode_***"]
E --> S3["$PARTING → /newNode_**"]
E --> S4["Нечеткое понимание → /newNode_***: Уточните?"]
S4 --> E
Ядро диалоговой системы — масштабируемая база знаний, построенная как семантическая сеть.
state: newNode_***
a: • Самовывоз – готовность заказа к выдаче не более 10 минут — Бесплатно
• ⚡️Экспресс-курьер – моментальная доставка по городу — *** ₽
• ТК – даже в постоматы — от *** ₽
• Почта России – по всей России — *** ₽
Подробнее: example.com/informaciya-o-dostavke
go!: /newNode_**
state: newNode_***
a: Сейчас я направлю Вам рекомендации, для возможного решения сложившейся ситуации.
В подавляющем большинстве они помогли нашим покупателям в аналогичных ситуациях.
Ознакомьтесь пожалуйста.
go!: /newNode_**
state: newNode_***
random:
a: Возможно, я не до конца понял. Напишите, пожалуйста, ещё раз.
a: Может быть, я не до конца понял. Сможете уточнить?
a: Я не уверен, что правильно понял. Напишите ещё раз, если это удобно.
ИИ включается, когда:
Цель — диалог состоялся, пользователь не ушёл.
state: newNode_***
LlmRequest:
userContent = {{$session.queryText}}
sendHistory = true
sendMessage = true
varName = answerGigaChat
okState = /newNode_***
model = GigaChat
profanityCheckOff = false
После запуска в продакшен система регулярно калибровалась на основе реальных диалогов. Процесс включал два этапа:
На каждом этапе проводилась работа:
| Метрика | Результат | Комментарий |
|---|---|---|
| Всего диалогов | 1090 | ~12 в день |
| Без участия оператора | 96% (1055) | ↓ с 0% до 96% — колоссальное снижение нагрузки |
| Переводы на оператора | 35 | ~0 в день — только сложные случаи |
| Всего сообщений | 4681 | ~52 в день |
| Нераспознанные сообщения | 309 (6.6%) | catchAll помогает вернуть в поток |
| Пользователи | 720 | ~8 в день |
| Интент | Срабатываний | Процент |
|---|---|---|
| Приветствие | 343 | 8.06% |
| Экспресс-курьер | 227 | 5.33% |
| Информация о доставке | 114 | 2.68% |
| Самовывоз | 111 | 2.60% |
| Адрес | 48 | 1.12% |
| Отзывы | 17 | 0.39% |
Вся логика управляется через визуальный конструктор — где видна часть 500+ узлов, связи и ветки.
Система DialogCore запущена в продакшен в Q4 2024 и стабильна с момента запуска.
| Показатель | Результат |
|---|---|
| Количество узлов | 500+ |
| Интеграции | 4 канала: Telegram, сайт, мессенджер, соцсеть |
| Жалобы на бота | 0 |
| Обновления логики | Через визуальный редактор — без остановки |
| Обратная связь | «Теперь бот — часть команды» |
Пример диалога с пользователем:
Пользователь: Скоро привезут заказ? Бот: 📦 Пришлите, пожалуйста, номер заказа — проверим статус. Пользователь: 12345 Бот: ✅ Заказ №12345 собран и передан курьеру. Следить за движением: [ссылка] Бот: ⏳ Курьер приедет в течение часа.
Такие диалоги происходят ежедневно — без участия оператора.
Диалоговое ядро, которое:
Это пример инженерного подхода к UX — когда архитектура, логика, семантика и эмпатия работают как единое целое.
Превращаем задержки в ритуалы на OpenCart
«Пользователь не доверяет скорости. Он доверяет вниманию.»
Создаем доверие через ощущение заботы, понимания, присутствия.Проблема: "сайт работает".
Решение: "сайт знает, что вы хотите — даже если вы сами ещё не знаете".
rise-*.js.sfc), подключаемые
в footer.twig по роутам — без изменений ядра| Слой | Технологии | Цель |
|---|---|---|
| 1. Визуальный | CSS, canvas, WebGL-подобные эффекты | Создать ощущение «живого глаза» |
| 2. Интерактивный | JS, MutationObserver, Service Worker, IntersectionObserver | Реагировать на действия |
| 3. Коммуникационный | showNotification(), typing, AI-оператор | Вести диалог от первого лица |
| 4. Защитный | PerfGuard, FPS-мониторинг, battery API | Не навреди. Ни устройству. Ни пользователю. |
chaoticGlow)typing + blink-caret Это театр.
Но если театр вызывает доверие — он правдив.
#sphere_widget
#sphere_widget {
background-image: url(/site/images/sphere_widget.webp);
animation: chaoticGlow 20s infinite linear alternate,
chaoticGlow 18s infinite linear alternate-reverse;
border-radius: 50%;
opacity: 0.85;
cursor: pointer;
}
document.getElementById('sphere_widget').addEventListener('click', openChat);
Иконка? Нет. Это — присутствие.
Да. Но с совестью.
Намеренно:
Без обмана:
Это эстетика доверия.
| Орган | Аналог в системе |
|---|---|
| Мозг | AI-оператор, логика решений |
| Нервная система | MutationObserver, Event Bus, Custom Events |
| Сердце | PerfGuard — следит за пульсом FPS |
| Кожа | Canvas, анимации, эффекты |
| Лёгкие | Service Worker, кэширование, PWA |
| Иммунитет | PerfGuard, автоочистка, beforeunload |
Каждый компонент — орган.
| Действие | Техническая реализация | Психологический эффект |
|---|---|---|
| Клик по поиску | Звук, вибрация, canvas-разряд | «Система услышала» |
| Ожидание результата | Анимация "анализа" с matrix-эффектом | «Идёт глубокая обработка» |
| Появление результата | Уведомление: "Поиск выполнен." | «Найдено для вас» |
| Просмотр галереи | Auto Swiper + уведомление | «Покажу сам» |
| Уход со страницы | beforeunload очистка |
«Не оставлю следов» |
Каждое действие — ответ.
Каждый ответ — диалог.
function startImageSlider(images) {
// Автоматически показывает галерею
// Останавливается при касании
}
Воспринимается как умная подсказка
Где: карточка товара /product
Модуль: rise-product.js.sfc
// После 3 касаний — показываем подсказку
let globalTapCount = 0;
element.addEventListener('touchstart', () => {
globalTapCount++;
if (globalTapCount === 3) {
// "Тапните дважды и удерживайте"
showNotification(" Тапните дважды и удерживайте.");
markNotificationAsShown(); // Только один раз
}
});
Ритуал вхождения.
Где: страница оформления /checkout
Модуль: rise-checkout.js.sfc
// При клике — запускаем сканирование
element.addEventListener('click', () => {
showScreenScanner(); // Full-screen анимация
showSpNotification("Проверяю введённую информацию...");
});
// Анимация: движущаяся линия + свечение
function showScreenScanner() {
const overlay = document.createElement('div');
overlay.innerHTML = `...`; // .scanner-line, .scanner-glow
document.body.appendChild(overlay);
setTimeout(() => overlay.remove(), 1500);
}
Ритуал подтверждения.
function showComputationAnimation(callback) {
// Full-screen canvas с:
// - матрицей падающих символов
// - вспышками
// - словами: "ПЛАТЬЕ", "ВЕЧЕРНЕЕ" — как "анализ"
// Завершается уведомлением
}
Пользователь не чувствует задержки — он чувствует мощь процесса.
function getGreeting() {
const hour = new Date().getHours();
return hour >= 5 && hour < 12 ? "Доброе утро!" : "Добрый вечер!";
}
localStorage)
document.getElementById('input-search').addEventListener('focus', () => {
playSound();
createDischargeAnimation();
createElectrifiedDots();
});
Активация силы.
document.querySelector("#button-search") &&
document.querySelector("#search").classList.add("visually-hidden");
const devicePerformance = (function() {
try {
var start = performance.now();
for (let i = 0; i < 5000; i++) Math.random();
var time = performance.now() - start;
return time > 8 ? "low" : time > 4 ? "medium" : "high";
} catch (e) {
return "medium";
}
})();
const observer = new MutationObserver(() => { ... });
window.addEventListener('beforeunload', () => {
observer.disconnect();
});
if (!localStorage.getItem('install_shown')) {
setTimeout(showInstallModal, 90000); // Через 90 сек
}
.split-pay {
animation: price-panic-run 2s ease-in-out infinite alternate;
}
.split-pay-icon::before {
animation: pacman-chomp 1s ease-in-out infinite;
}
PerfGuard — это непрерывный мониторинг состояния устройства:
| Состояние | Условия | Поведение |
|---|---|---|
| 1. Активно | FPS ≥ 40, экран в фокусе | Полный UX: анимации, эффекты, AI-присутствие |
2. Авария perf.guard.low |
FPS < 40 или батарея < 15% и не заряжается или страница не в фокусе |
Мгновенное отключение всех ресурсоёмких процессов |
3. Восстановление perf.guard.high |
Страница в фокусе + FPS ≥ 40 | Плавное возвращение эффектов |
На страницах принятия решений PerfGuard не входит в аварию, даже при низкой FPS:
if (
p.includes('/cart') ||
p.includes('/checkout') ||
p.includes('/search') ||
q.includes('route=product/search') ||
q.includes('route=product/category') ||
q.includes('route=journal3/filter') ||
q.includes('route=journal3/blog')
) {
window.__perfGuard_critical = true; // "Здесь UX важнее производительности"
}
perf.guard.lowСистема мгновенно отключает всё, что может нагружать:
window.addEventListener('perf.guard.low', () => {
if (!window.__perfGuard_critical) {
clearIntervalAll();
clearTimeoutAll();
disconnectAllObservers();
closeAllWebSockets();
cancelAllRaf();
}
});
perf.guard.highКогда устройство "дышит" — эффекты возвращаются плавно:
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible' && getFps() >= 40) {
setTimeout(restoreEffects, 800); // Даём системе время "проснуться"
}
});
UX-магия возвращается, как будто её и не было.
PerfGuard не реагирует мгновенно. Он ожидает, подтверждает, затем решает — чтобы избежать "мигания" UX.
if (g && o - h >= 8000) {
// Восстановление только после 8 секунд "охлаждения"
r(); // perf.guard.high
}
Это взвешенное решение системы:
"Я вижу проблему. Но не паникую. Я жду. И действую — только когда нужно."
| Этап | Что внедрили | Результат |
|---|---|---|
| 1 | AI-оператор + уведомления | +22% открытие чата |
| 2 | Auto Swiper + Scanner | +34% время на карточке |
| 3 | Электрификация поиска | +41% кликов по поиску |
| 4 | Split Pay + Pacman | +19% конверсия в корзину |
| 5 | PerfGuard | -83% жалоб на "тормозит" |
| Метрика | До | После | Прирост |
|---|---|---|---|
| Конверсия в заказ | 1.8% | 3.4% | +89% |
| Средняя продолжительность сессии | 1:58 | 4:12 | +104% |
| Отказы на мобильных | 72% | 38% | -47% |
| Открытие чата-бота | 0.2% | 22% | +10 900% |
| Lighthouse Performance | 62 | 100 | |
| Установка PWA | 0 | 45+ | |
| Вылеты на слабых Android | 12% | <1% | |
| Средний FPS на low-end | 32 | 56 (с адаптацией) |
| Проект | Фокус | Уровень |
|---|---|---|
| PWA-Layer | Изоляция контекстов | Технический |
| ISPN | Предсказание навигации | Системный |
| AI-Operator | Персонализация UX | Человеческий |
Это эволюция доверия.
«Лоадер — ритуал. Интерфейс — диалог. Сайт — не умнее. Сайт — человек.»
Как переписанный с нуля визуальный язык создаёт доверие
Вы всё построили. Всё оптимизировали. Всё ускорили.
ML-Cache дал скорость. ISPN — предикцию. PWA-Layer — ощущение приложения. OPS-UX и DialogCore — эффективность. AI-Оператор — начал говорить.
И если интерфейс лишён живого дыхания — даже совершенная машина кажется безжизненной.
UX-Vibe — не дизайн. Это визуальное воплощение живого диалога.
Не про "быть красивым". Про ощущение, что за системой — кто-то есть.
UX-Vibe — это всё, что пользователь видит, чувствует, касается на сайте:
Это — Vibe-UX: ощущение живого присутствия, созданное через цвет, тень, анимацию, текстуру и микро-движение.
Цвет в UX-Vibe — источник внимания.
Это — световая сигнатура системы.
Каждый важный элемент живёт в трёхмерном пространстве:
Кнопки не лежат. Они парят. Заголовки не просто текст. Они сияют.
Каждая кнопка — ритуал касания с системой.
Нажатие — контакт с живым помощником.
Баннеры — не реклама. Это визуальный жест системы.
Показывает баннер? Нет — приглашает войти.
Информация, личный кабинет, дополнительно — внизу экрана. Все — с характером.
Нажать на иконку — контакт с живым помощником.
Заголовки не читаются. Они сияют, как неон в темноте.
Чтение — это восприятие живого диалога.
Каждое касание — подтверждается:
Пользователь не чувствует интерфейс. Он чувствует диалог.
CSS создаёт основу вайба. Но некоторые эффекты требуют точного контроля — и тут вступает JavaScript.
requestAnimationFrame и динамические overlay-слои.visibilityState: 'visible', экономя CPU.MutationObserver отслеживаются системные
всплывающие окна (например, "Проверка") и заменяются на плавные, соответствующие стилю UX-Vibe.CSS — это дыхание. JS — это сердцебиение. Вместе они создают ощущение живой системы.
Этот подход — система визуальных решений, реализованная по всему интерфейсу. Ниже — примеры, как она проявляется в ключевых точках взаимодействия.
Кнопка пульсирует в ритме системы.
button-search-go создаёт лёгкое дыхание — как признак живого присутствия
Это точка визуального диалога.
Круглая, плавающая в правом нижнем углу карточки — она зовёт к действию.
Жест, на который хочется ответить.
Всплывающее окно — сияет изнутри.
Ваш личный помощник, который ждёт вашего взгляда.
Откликаются на внимание.
Это приглашение к диалогу.
Точки притяжения.
Это шаги внутрь живого пространства.
Церемония входа.
Это вход в живой диалог с системой.
Такой подход — не про «красиво». Он про желание взаимодействовать.
Он работает на уровне:
Этот метод не создаёт метрик. Он делает возможным их визуальное выражение.
Это — не причина роста конверсии, а среда, в которой он становится ощутимым.
Интерфейс превратился из функционального инструмента в пространство диалога, где:
Стало возможным то, что раньше было абстракцией: AI-Оператор стал не только слышимым — он стал видимым.
Это не финальный штрих. Это завершённый визуальный язык AI-Оператора — живого ядра системы.
Документ полного переписывания визуала, где каждый пиксель — след внимания, каждый эффект — ответ на контакт.
Пользователь не приходит на сайт. Он становится частью диалога.
Такой подход — это то, что делает всю систему не просто работающей, а живой.
Техника — внутри. Доверие — снаружи. А этот мост между ними — ваше визуальное присутствие.
@keyframes button-search-go {
0%, 100% {
transform: scale(1) rotate(0deg);
box-shadow: 0 8px 25px rgba(255, 86, 172, 0.3),
0 0 0 0 rgba(255, 153, 0, 0.7);
}
50% {
transform: scale(1.03) rotate(1deg);
box-shadow: 0 12px 40px rgba(255, 86, 172, 0.5),
0 0 30px 8px rgba(255, 153, 0, 0.7);
}
}
/* Применяется к кнопке поиска для эффекта "дыхания" */
#search-button { animation: button-search-go 3s ease-in-out infinite; }
Тексты документов ML-Cache, ISPN, PWA-Layer, OPS-UX, DialogCore, AI-Оператор и UX-Vibe форматированы для улучшения читаемости и восприятия на основе: материалов из рабочей среды, реальных технических решений и авторской логики изложения. Подача материала передаёт ход рассуждений, этапы внедрения и индивидуальный стиль автора.
Общая информация:
Начало реализации: февраль 2024
Текущее состояние: ноябрь 2025
Общие трудозатраты: более 4500 часов (проектирование, разработка, тестирование, внедрение, калибровка)
Только проблемы, решения и измеримый результат.
Всегда подход определяет инструменты.
Опуская условия — система работает.
Пользователь доверяет вниманию.
Внимание — в архитектуре.