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

teko-chat-sdk

v1.3.1

Published

TekoChatSDK - AI chatbot SDK for Teko ECOM apps

Downloads

1,333

Readme

TekoChatSDK

React component library giúp các ECOM app của Teko tích hợp AI chatbot với khả năng bidirectional communication — right panel có thể push context ngược lại cho AI.

How it works

ECOM App
 └── <TekoChatWidget chatBffUrl="..." transportMode="rest" panels={{...}} />
      │
      │  createTransport(transportMode):
      │    'websocket' → WebSocketTransport (persistent WS, streaming chunks)
      │    'sse'       → SSETransport       (fetch SSE / text/event-stream)
      │    'rest'      → RestTransport      (plain JSON POST)
      ▼
     BFF → LLM → Business Services

UI flow:

TekoChatBubble ──click──► navigate sang /chat ──► TekoChatWidget (fullscreen)

Cài đặt

yarn add teko-chat-sdk
# peer deps
yarn add react react-dom

Usage

TekoChatBubble là trigger điều hướng sang chat route. TekoChatWidget render fullscreen trong container.

import { TekoChatBubble } from 'teko-chat-sdk';

// App.tsx — bubble navigate sang /chat
<TekoChatBubble onClick={() => router.push('/chat')} />

// pages/chat.tsx — fullscreen chiếm toàn bộ container
type Product = { sku: string; name: string; canonical?: string };

export default function ChatPage() {
  const chatRef = useRef<TekoChatWidgetRef>(null);

  return (
    <div style={{ height: '100vh' }}>
      <TekoChatWidget
        ref={chatRef}
        appId="your-app-id"
        chatBffUrl="https://your-bff.example.com/chat"
        transportMode="rest"
        panels={{
          cart: {
            render: ({ sendContext }) => (
              <CartPage
                onUpdate={(items) => sendContext({ type: 'cart_updated', items })}
              />
            ),
          },
        }}
        intents={{
          INTENT_VIEW_CART: {
            onResponse: (_args, ctx) => ctx.openPanel('cart'),
          },
          INTENT_PRODUCT_SEARCH: {
            onResponse: (args) => {
              const products = args.products as Product[] | undefined;
              if (!products?.length) return;
              return products.slice(0, 8).map((p) => ({
                key: p.sku,
                label: p.name,
                payload: { sku: p.sku, canonical: p.canonical },
              }));
            },
            onOptionClick: (option, { sendContext, sendMessage }) => {
              sendContext(option.payload ?? {});
              sendMessage(option.label);
            },
          },
        }}
        getAppContext={() => ({ terminalId: 'T001' })}
      />
    </div>
  );
}

Mobile support: SDK không tự detect viewport — truyền layoutMode vào widget:

const [layoutMode, setLayoutMode] = useState(() =>
  window.innerWidth < 768 ? 'mobile' : 'desktop'
);
// <TekoChatWidget layoutMode={layoutMode} ... />

Mobile behavior: fullscreen = chat full-screen, right panel slide up dạng overlay khi host app gọi openPanel().

Bidirectional Communication

Right panel có thể push context ngược về AI để cải thiện intent detection — không cần user gõ lại thông tin.

SDK inject useChatContext() vào mọi component bên trong panel — không cần prop drilling.

// App.tsx
<TekoChatWidget
  ref={chatRef}
  panels={{
    cart: {
      render: ({ sendContext }) => <CartPage onUpdate={(items) => sendContext({ items })} />,
    },
  }}
  intents={{
    INTENT_VIEW_CART: {
      onResponse: (_args, ctx) => ctx.openPanel('cart'),
    },
  }}
/>

// CartPage.tsx — hoặc bất kỳ component lồng sâu nào
import { useChatContext } from 'teko-chat-sdk';

function CartPage() {
  const { sendContext } = useChatContext();

  const handleQtyChange = (qty: number) => {
    updateCart(qty);
    sendContext({
      type: 'cart_updated',
      items: cartItems,
      totalAmount: calculateTotal(),
    });
  };
}

useChatContext() trả về { sendContext, sendMessage, activePanel }. Nếu dùng bên ngoài TekoChatWidget, sendContext là no-op (không throw).

API

<TekoChatWidget> Props

3 props bắt buộc:

| Prop | Type | Mô tả | | --------------- | -------------------------------- | --------------------- | | appId | string | App identifier | | chatBffUrl | string | BFF endpoint | | transportMode | 'websocket' \| 'sse' \| 'rest' | Giao thức kết nối BFF |

Intent & panels

| Prop | Type | Mô tả | | --------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------- | | panels | Record<string, PanelConfig> | Panel registry — keyed by panel name. Gọi chatRef.current?.openPanel(key) để hiển thị. | | intents | Record<string, IntentConfig> | Unified intent handler map — xem chi tiết bên dưới. | | getAppContext | () => Record<string, unknown> \| Promise<...> | Inject app metadata vào mỗi BFF request, resolve tại send-time (luôn fresh). |

SuggestOption

Chip gợi ý hiển thị bên dưới message của AI.

interface SuggestOption {
  /** Unique key để SDK track, không hiển thị cho user. */
  key: string;
  /** Text hiển thị trên chip. Gửi lên BFF khi user click (default behavior). */
  label: string;
  /** Arbitrary data đính kèm — merge vào request context khi user click (default behavior). */
  payload?: Record<string, unknown>;
}

IntentConfig

Mỗi entry trong intents map ứng với một toolCall.name mà BFF có thể trả về.

interface IntentConfig {
  /**
   * Gọi khi BFF trả toolCall có tên khớp với key trong `intents`.
   *
   * @param args   - `toolCall.arguments` từ BFF (OpenAI tool_calls format).
   * @param ctx    - Helpers để tương tác với SDK mà không cần chatRef.
   *   - `openPanel(key)`         — Mở right panel theo key đã đăng ký trong `panels`.
   *   - `sendContext(data)`      — Lưu context; tự merge vào request khi user gửi message tiếp theo.
   *   - `sendMessage(text)`      — Gửi message lên BFF programmatically.
   *
   * @returns
   *   - `SuggestOption[]` — SDK tự render chips và gọi `appendSuggestions()`.
   *   - `void`            — Side effect only (VD: mở panel, lưu context).
   */
  onResponse: (
    args: Record<string, unknown>,
    ctx: {
      openPanel: (key: string) => void;
      sendContext: (data: Record<string, unknown>) => void;
      sendMessage: (text: string) => void;
    },
  ) => SuggestOption[] | void | Promise<SuggestOption[] | void>;

  /**
   * Gọi khi user click vào suggestion chip sinh ra bởi intent này.
   *
   * Không set → SDK dùng default behavior: gửi `option.label` lên BFF
   * và merge `option.payload` vào request context.
   *
   * @param option - Chip user đã click.
   * @param ctx    - `sendContext` và `sendMessage` (không có `openPanel`).
   */
  onOptionClick?: (
    option: SuggestOption,
    ctx: {
      sendContext: (data: Record<string, unknown>) => void;
      sendMessage: (text: string) => void;
    },
  ) => void;
}

Luồng hoạt động:

BFF trả toolCall { name: "INTENT_X", arguments: {...} }
        │
        ▼
intents["INTENT_X"].onResponse(args, ctx)
        │
        ├─ return SuggestOption[]  →  SDK render chips bên dưới message
        │                              User click chip
        │                                    │
        │                                    ▼
        │                          onOptionClick? (nếu có)
        │                          hoặc default: gửi label + payload lên BFF
        │
        └─ return void  →  side effect only (VD: ctx.openPanel('cart'))

PanelConfig

interface PanelConfig {
  /**
   * @param props.sendContext - Push context về AI từ bên trong panel.
   *   Dữ liệu được merge vào request khi user gửi message tiếp theo.
   */
  render: (props: {
    sendContext: (data: Record<string, unknown>) => void;
  }) => ReactNode;
}

UI customization

| Prop | Type | Default | Mô tả | | ---------------- | ----------------------- | ------------------------------ | --------------------------------------------------------------------- | | layoutMode | 'desktop' \| 'mobile' | 'desktop' | Desktop: split 35/65. Mobile: single column + right panel overlay. | | primaryColor | string | '#1a73e8' | Primary color (hex) | | botAvatar | string | — | Avatar URL cho AI | | locale | 'vi' \| 'en' | 'vi' | Ngôn ngữ hiển thị | | labels | Partial<ChatLabels> | — | Override từng label cụ thể | | loadingPhrases | string[] | Built-in phrases theo locale | Text xoay vòng hiển thị sau 2s khi AI đang xử lý. Truyền [] để tắt. |

Layout / spacing

| Prop | Type | Default | Mô tả | | -------------- | -------- | ------- | -------------------------------------------------- | | offsetTop | number | 0 | Offset từ top (px) — tránh che fixed header | | offsetBottom | number | 0 | Offset từ bottom (px) — tránh che fixed bottom nav | | zIndex | number | 1031 | CSS z-index |

TekoChatWidgetRef Methods

Truy cập qua ref.current:

| Method | Signature | Mô tả | | ------------- | ----------------------------------------------------------- | ----------------------------------------------------------------- | | openPanel | (componentKey: string) => void | Hiển thị right panel với component key | | sendMessage | (text: string, context?: Record<string, unknown>) => void | Gửi message lên BFF programmatically | | sendContext | (data: Record<string, unknown>) => void | Lưu context — tự merge vào request khi user gửi message tiếp theo |

<TekoChatBubble> Props

| Prop | Type | Default | Mô tả | | -------------- | ------------------------------------ | ----------- | ----------------------------------------- | | onClick | () => void | — | Callback khi user click bubble (required) | | primaryColor | string | '#1a73e8' | Primary color (hex) | | offsetBottom | number | 0 | Offset từ bottom (px) | | zIndex | number | 1031 | CSS z-index | | renderBubble | (onClick: () => void) => ReactNode | — | Custom bubble UI thay thế bubble mặc định |

BFF Protocol

Chọn transportMode theo giao thức BFF implement:

| transportMode | Giao thức | Ghi chú | | --------------- | --------- | --------------------------------------------------------------------- | | 'websocket' | WebSocket | Persistent connection, BFF push TransportFrame JSON qua ws.send() | | 'sse' | SSE | POST request, BFF stream text/event-stream (data: {...}\n\n) | | 'rest' | REST JSON | POST request, BFF trả JSON một lần (không stream) |

Contributing

See DEVELOPMENT.md for dev setup, Playground, project structure, and release process.