npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lithium-platform-sdk/chatbot

v0.1.0

Published

Drop-in React Native chat for the Lithium platform: a thin WebView host + capability bridge (clients embed this; UI/logic are served and updated OTA by us)

Readme

@lithium-platform-sdk/chatbot

Встраиваемый чат Lithium для React Native. Это тонкий хост: нативный react-native-webview + типизированный capability-мост. Сам интерфейс и логика чата живут в веб-приложении, которое хостим и обновляем мы (OTA), поэтому обновления чата не требуют релиза приложения клиента в сторах.

[ваше RN-приложение]
   └─ <LithiumChatView/>            ← этот пакет (WebView + мост)
        └─ apps/chat-web (наш URL)  ← UI/логика, обновляем мы
             └─ apps/server         ← bridge → Voximplant Kit + чатбот Литиум

Установка

npm install @lithium-platform-sdk/chatbot react-native-webview
# iOS:
cd ios && pod install

react, react-native, react-native-webview — peer-зависимости.

Быстрый старт

import { LithiumChatView } from '@lithium-platform-sdk/chatbot';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function CareChatScreen({ navigation, user }) {
  const insets = useSafeAreaInsets();
  return (
    <LithiumChatView
      webUrl="https://chat.lithium.example/"     // наш хостинг chat-web
      serverUrl="https://chat-api.lithium.example" // bridge-сервер
      identity={{
        clientId: user.id,
        displayName: user.fullName,
        phone: user.phone,
        email: user.email,
      }}
      context={{ source: 'care', title: 'Служба заботы' }}
      theme={{ primaryColor: '#D4016A', backgroundColor: '#F9F3F3' }}
      locale="ru"
      safeAreaInsets={insets}
      onClose={() => navigation.goBack()}
    />
  );
}

Identity

identity инъектируется в страницу до загрузки контента (не попадает в URL, логи и историю). Поля: clientId (для восстановления сессии), displayName, phone, email, avatar, locale.

Capability-мост

Мост даёт веб-чату доступ к нативным возможностям. Ключевой принцип:

Возможность включается, только если её реализует ваш хендлер, использующий уже подключённые в приложении модули и уже выданные разрешения. Если хендлер не передан — веб скрывает фичу (graceful degradation), новых системных permission-промптов пакет не создаёт.

import { LithiumChatView, nativeUploadAttachment, type CapabilityHandlers } from '@lithium-platform-sdk/chatbot';
import ImagePicker from 'react-native-image-crop-picker'; // уже в бинаре клиента
import { pick, types, isCancel } from '@react-native-documents/picker';

const capabilities: Partial<CapabilityHandlers> = {
  pickImage: async ({ multiple }) => {
    const result = await ImagePicker.openPicker({
      mediaType: 'photo',
      multiple: multiple ?? false,
      maxFiles: multiple ? 10 : 1,
    });
    const images = Array.isArray(result) ? result : [result];
    return {
      files: images.map((img) => ({
        uri: img.path,
        name: img.filename ?? 'photo.jpg',
        type: img.mime ?? 'image/jpeg',
        size: img.size,
      })),
    };
  },
  takePhoto: async () => {
    const img = await ImagePicker.openCamera({ mediaType: 'photo' });
    return {
      file: {
        uri: img.path,
        name: img.filename ?? 'photo.jpg',
        type: img.mime ?? 'image/jpeg',
        size: img.size,
      },
    };
  },
  pickFile: async ({ multiple }) => {
    const docs = await pick({ allowMultiSelection: multiple ?? true, type: [types.allFiles] });
    return {
      files: docs.map((d) => ({
        uri: d.uri,
        name: d.name ?? 'file',
        type: d.type ?? 'application/octet-stream',
        size: d.size,
      })),
    };
  },
  /** Upload on the native side — WebView cannot read file:// URIs or RN FormData. */
  uploadAttachment: (params) => nativeUploadAttachment(params),
};

CodePush и нативные модули

CodePush (Revopush) обновляет только JS-бандл и ассеты. Новые native-модули, изменения Info.plist / AndroidManifest.xml требуют релиза в сторах.

| Что | Доставка | | --- | --- | | apps/chat-web (UI, action sheet, upload flow) | Наш хостинг — обновляем без участия клиента | | @lithium-platform-sdk/chatbot + handlers в клиенте | CodePush — если модуль уже в package.json бандла | | react-native-webview, image-crop-picker, @react-native-documents/picker | Уже в бинаре kdf-patient-app — не добавляем | | uploadAttachment handler | JS-onlynativeUploadAttachment() использует только fetch + { uri } FormData |

Не используйте includeBase64: true и передачу base64 через bridge для больших файлов — это перегружает RN bridge (image-picker docs).

Правильный поток: pickImage{ uri }uploadAttachment (native fetch) → sendMessage с URL от сервера.

На native host веб-чат показывает action sheet «Галерея / Камера / Файл».

Полный список — тип Capability из @lithium-platform-sdk/bridge-protocol.

Вариатор (пилотная раскатка)

Решение «нативный Voximplant (vox) или Lithium-чат (lithium)» принимает основной сервис Lithium, а не приложение и не bridge-сервер — так процентом раскатки управляем централизованно, без релиза клиента.

import { resolveChatVariant } from '@lithium-platform-sdk/chatbot';

const variant = await resolveChatVariant({
  endpoint: 'https://api.lithium.example/care-chat/variant',
  payload: { userId: user.id, phone: user.phone, platform: Platform.OS, appVersion },
  timeoutMs: 2000,
  fallback: 'vox', // безопасный откат при недоступности вариатора
});
if (variant === 'lithium') navigation.navigate('LithiumCareScreen');
else openVoximplantChat();

Есть и хук useVariator(req){ variant, loading }.

Props

| Prop | Тип | Описание | | --- | --- | --- | | webUrl | string | URL хостимого веб-чата (наш). | | serverUrl | string | URL bridge-сервера (инъектируется в страницу). | | identity | LithiumIdentity | Идентификация пользователя. | | context | object? | Произвольный контекст диалога. | | theme | LithiumChatTheme? | Цвета (по умолчанию палитра клиники). | | locale | string? | Локаль UI. | | variant | string? | Назначенный вариант (для аналитики). | | capabilities | Partial<CapabilityHandlers>? | Нативные хендлеры возможностей. | | safeAreaInsets | SafeAreaInsets? | Инсеты (например из safe-area-context). | | onClose | () => void | Веб попросил закрыть чат. | | onLoadError | (error) => void? | WebView не загрузил webUrl (аналитика / fallback). | | originWhitelist | string[]? | Лок навигации (по умолчанию origin webUrl). |

Клавиатура и WebView

LithiumChatView настраивает WebView так, чтобы веб-чат сам управлял layout композера без конфликта с нативной клавиатурой:

  • iOS: keyboardLayoutMode: 'webVisualViewport' — веб читает window.visualViewport; native не добавляет padding. WebView: scrollEnabled={false}, contentInsetAdjustmentBehavior="never", hideKeyboardAccessoryView.
  • Android (API ≤ 34): 'nativeResize' — окно сжимается через adjustResize, веб inset = 0.
  • Android (API 35+, edge-to-edge): 'nativePadding' — контейнер получает paddingBottom: safeAreaInsets.bottom + keyboardHeight; веб использует host:keyboard.height.

Режим передаётся в host:hello; веб не должен одновременно применять тот же inset (double-count → «улет» input при focus).

Bridge: надёжность

  • Сообщения native → web буферизуются до первого web:ready.
  • При возврате из background (AppStateactive) повторяется handshake.
  • На Android bootstrap инъектируется и через injectedJavaScriptObject (race с beforeContentLoaded); веб ретраит при getBootstrap() === null.

Dev: HTTP по LAN

  • iOS: NSAllowsLocalNetworking в Info.plist (см. apps/demo-rn).
  • Android: usesCleartextTraffic="true" только в debug manifest (android/app/src/debug/AndroidManifest.xml).

Протокол

Контракт postMessage (общий с веб-частью) — в пакете @lithium-platform-sdk/bridge-protocol. Версионируется PROTOCOL_VERSION; при handshake хост сообщает версию и список доступных capability.