uHamkorДокументация
Документация API

Чат-виджет: подключение, кастомизация и управление

Руководство по подключению чат-виджета и настройке его внешнего вида и поведения. Описаны атрибуты скрипта (data-launcher, data-greeting, data-open), изоляция стилей через Shadow DOM, CSS-переменные и селекторы ::part(), а также JavaScript API window.uHamkor — методы, снимок состояния, события и практические рецепты. Поддерживаются доступность, печать и reduced motion.

🎨 Чат-виджет: подключение, кастомизация и управление

Виджет подключается одним тегом <script>, изолирует стили через Shadow DOM и управляется с помощью CSS-переменных, селекторов ::part() и JavaScript API window.uHamkor.


🚀 Подключение

<script
  src="https://your-widget-host.com/widget.js"
  data-project-uuid="YOUR_PROJECT_UUID"
  data-language="uz"
  defer
></script>

Разместите тег внутри <head> страницы. Виджет инициализируется только на доменах, указанных в списке domains проекта.

⚙️ Атрибуты скрипта

  • data-project-uuid — обязательный. Проект, которому принадлежит виджет.
  • data-languageuz / ru / en. По умолчанию: uz.
  • data-user — внешний ID пользователя (идентифицирует посетителя).
  • data-launchertrue / false. По умолчанию true. Отрисовывает плавающую кнопку.
  • data-greetingtrue / false. По умолчанию true. Приветственное облачко.
  • data-opentrue / false. По умолчанию false. Открывает окно чата сразу после монтирования.

data-language, data-user, data-launcher и data-greeting отслеживаются в рантайме — при изменении атрибута виджет обновляется сразу.


🎯 CSS-переменные

Переопределяйте переменные на классе .chat-widget:

.chat-widget {
  /* Цвета (HSL без hsl()) */
  --cw-primary: 221 83% 53%;
  --cw-secondary: 258 83% 66%;
  --cw-background: 0 0% 100%;
  --cw-foreground: 222.2 84% 4.9%;

  /* Расположение */
  --cw-z-index: 50;
  --cw-button-bottom: 20px;
  --cw-button-right: 20px;
  --cw-window-bottom: 20px;
  --cw-window-right: 20px;

  /* Радиус скругления */
  --cw-radius: 1rem;
}

Высота окна чата вычисляется из --cw-window-bottom, поэтому при поднятии виджета окно остаётся в пределах экрана.


🧩 Селекторы ::part()

Стилизация внутренних элементов:

.chat-widget::part(button) {}
.chat-widget::part(window) {}
.chat-widget::part(header) {}
.chat-widget::part(body) {}
.chat-widget::part(footer) {}

🖨️ Печать

При печати виджет скрывается автоматически. Дополнительная настройка не требуется.

🎞️ Reduced Motion

Для пользователей с prefers-reduced-motion: reduce анимации отключаются автоматически.


💡 Примеры

📍 Изменить положение виджета

.chat-widget {
  --cw-button-bottom: 100px;
  --cw-button-right: 30px;
  --cw-window-bottom: 100px;
  --cw-window-right: 30px;
}

🔝 Более высокий z-index (поверх модальных окон)

.chat-widget {
  --cw-z-index: 99999;
}

🚫 Скрыть виджет на определённых страницах

.no-chat .chat-widget {
  display: none;
}

🕹️ JavaScript API (window.uHamkor)

Сразу после монтирования виджет публикует глобальный API в window.uHamkor. Через него виджетом можно управлять из вашего интерфейса: кнопка в шапке, ссылка «Поддержка», бейдж непрочитанных сообщений.

📦 Подключение с очередью команд

Вызовы, сделанные до загрузки widget.js, попадают в очередь и выполняются после загрузки — ждать через setTimeout не нужно:

<script>
  window.uHamkor = window.uHamkor || function () {
    (window.uHamkor.q = window.uHamkor.q || []).push(arguments);
  };
  uHamkor('hideLauncher'); // сайт рисует свою кнопку
</script>
<script
  src="https://your-widget-host.com/widget.js"
  data-project-uuid="YOUR_PROJECT_UUID"
  data-language="uz"
  data-launcher="false"
  defer
></script>

После загрузки работают оба стиля вызова:

uHamkor.open();   // форма метода
uHamkor('open');  // форма команды (её же использует очередь)

🛠️ Методы

  • open() / close() / toggle() — управление окном чата.
  • isOpen() — состояние окна (boolean).
  • showLauncher() / hideLauncher() — показать/скрыть плавающую кнопку. Пока чат открыт, кнопка остаётся видимой и работает как кнопка закрытия.
  • setGreetingEnabled(enabled) — включить/выключить приветственное облачко.
  • setLanguage(lang)en, ru, uz, uz_latn, uz_cyrl.
  • getLanguage() — текущий язык.
  • identify(identity){ externalUserId, firstName, lastName, phoneNumber }.
  • logout() — очищает сессию, токен и кешированные сообщения.
  • prefill(text) — подставляет текст в поле ввода без отправки.
  • sendMessage(text) — отправляет сообщение; возвращает Promise<boolean> (false — отправить не удалось).
  • getUnreadCount() — количество непрочитанных сообщений.
  • getState() — полный снимок состояния.
  • subscribe(listener) — срабатывает на каждое изменение состояния; возвращает функцию отписки.
  • on(event, handler) / once / off — подписка на события.
  • setPosition({ bottom, right }) — перемещает кнопку и окно (в пикселях).
  • setColors(primary, secondary?) — меняет цвета темы в рантайме.
  • destroy() — размонтирует виджет и удаляет его со страницы.

📊 Снимок состояния

{
  projectUuid: string;
  isOpen: boolean;
  isLauncherVisible: boolean;
  isWidgetEnabled: boolean;   // виджет выключен в проекте
  unreadCount: number;
  language: string;
  isAuthorized: boolean;
  externalUserId: string | null;
  isTyping: boolean;          // оператор/бот печатает
  isOnline: boolean;          // поддержка онлайн
  isBotActive: boolean;       // в диалоге нет живого оператора
  isAIThinking: boolean;
  isResolved: boolean;
  callStatus: 'idle' | 'calling' | 'incoming' | 'active' | 'ended' | 'failed';
  callType: 'audio' | 'video';
}

📡 События

  • ready — виджет смонтирован; payload: снимок состояния.
  • state — изменилось любое поле снимка.
  • open / close — без payload.
  • unread{ count }.
  • message{ direction: 'incoming' | 'outgoing', message }.
  • operator:joined — оператор подключился к диалогу.
  • chat:resolved — диалог завершён.
  • call:incoming / call:started / call:ended{ type }.
  • language:change{ language }.
  • identify{ externalUserId }.
  • destroy — виджет удалён.

Каждое событие также отправляется на document под именем uhamkor:<event>, поэтому фреймворки могут слушать его без обращения к глобальной переменной:

document.addEventListener('uhamkor:unread', e => {
  badge.textContent = e.detail.count;
});

🍳 Рецепты

🔔 Своя кнопка с бейджем непрочитанных

uHamkor('hideLauncher');
uHamkor('on', 'state', state => {
  badge.hidden = state.unreadCount === 0;
  badge.textContent = state.unreadCount;
  trigger.setAttribute('aria-expanded', String(state.isOpen));
});
trigger.addEventListener('click', () => uHamkor.toggle());

👤 Привязать авторизованного пользователя

uHamkor.identify({
  externalUserId: user.id,
  firstName: user.firstName,
  lastName: user.lastName,
  phoneNumber: user.phone,
});

💬 Начать диалог по действию на странице

uHamkor.open();
uHamkor.sendMessage('Нужна помощь по заказу #1234');

🚪 Очистить сессию при выходе

uHamkor.logout();

🔄 Поведение виджета

  • Плавающую кнопку можно перетаскивать. При открытии чата она возвращается в стандартный угол, а перетаскивание отключается — так кнопка остаётся выровненной с окном.
  • Пока чат открыт, кнопка не скрывается: она выполняет роль кнопки закрытия.
  • Приветственное облачко скрывается при открытом окне чата.
  • Анимации открытия/закрытия идут поэтапно (шапка → сообщения → поле ввода) и отключаются при prefers-reduced-motion.
  • Первое приветственное сообщение помечается прочитанным, поэтому unreadCount растёт только на реальных новых сообщениях.
  • setPosition({ bottom }) перемещает окно вместе с кнопкой.

♿ Доступность

  • role="dialog" и aria-modal="true" у окна чата.
  • aria-label у всех интерактивных кнопок.
  • aria-haspopup="dialog" у плавающей кнопки.
  • Поддержка навигации с клавиатуры.