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

@maxaiagent/widget-sdk

v1.6.0

Published

MaxAI Widget SDK — embed AI chat with page context, client-side tools, and event system

Downloads

2,172

Readme

@maxaiagent/widget-sdk

SDK do osadzania widgetu czatu MaxAI na dowolnej stronie internetowej.

Instalacja

NPM (React, Vue, Next.js, itp.)

npm install @maxaiagent/widget-sdk
import { MaxAI } from '@maxaiagent/widget-sdk'

const widget = MaxAI.init({ key: 'TWOJ_KLUCZ' })

CDN (zwykły HTML)

<script src="https://maxaiagent.app/sdk.js" data-key="TWOJ_KLUCZ"></script>

Widget automatycznie się zainicjalizuje. Aby korzystać z API SDK:

<script src="https://maxaiagent.app/sdk.js" data-key="TWOJ_KLUCZ"></script>
<script>
  // MaxAI jest dostępny globalnie
  MaxAI.setPageContext({ pageType: 'product', visibleData: { name: 'Buty Nike', price: 549 } })
</script>

API

MaxAI.init(options)

Inicjalizuje widget. Zwraca WidgetInstance.

const widget = MaxAI.init({
  key: 'TWOJ_KLUCZ',            // Wymagane — klucz z panelu MaxAI
  position: 'bottom-right',      // 'bottom-right' | 'bottom-left'
  baseUrl: 'https://maxaiagent.app', // Opcjonalne — override URL serwera
  nonce: 'abc123',               // Opcjonalne — CSP nonce dla stylów
  lazy: false,                   // Opcjonalne — lazy load iframe
  styles: {                      // Opcjonalne — style widgetu (patrz sekcja Styles)
    theme: 'dark',
    density: 'compact',
  },
})

widget.setPageContext(context)

Wysyła kontekst strony do agenta AI. Agent automatycznie zbiera tytuł, URL, meta tagi i JSON-LD. Użyj tej metody, aby dodać dodatkowe dane.

widget.setPageContext({
  pageType: 'product',
  visibleData: {
    name: 'Nike Air Max 90',
    price: 549,
    currency: 'PLN',
    sizes: [40, 41, 42, 43, 44, 45, 46],
  },
  customContext: {
    user_type: 'premium',
    cart_count: '3',
  },
})

widget.registerTool(definition)

Rejestruje narzędzie po stronie klienta. Agent AI może je wywoływać w trakcie rozmowy. Zwraca funkcję do wyrejestrowania.

const unregister = widget.registerTool({
  slug: 'add_to_cart',
  name: 'Dodaj do koszyka',
  description: 'Dodaje produkt do koszyka użytkownika',
  schema: {
    type: 'object',
    properties: {
      product_id: { type: 'string', description: 'ID produktu' },
      quantity: { type: 'number', description: 'Ilość', default: 1 },
      size: { type: 'string', description: 'Rozmiar (np. "42")' },
    },
    required: ['product_id'],
  },
  requiresConfirmation: true, // Pokaż dialog potwierdzenia
  handler: async (args) => {
    const result = await myAPI.addToCart(args.product_id, args.quantity, args.size)
    return { success: true, cart_count: result.totalItems }
  },
})

// Później:
unregister()

widget.on(event, callback)

Nasłuchuje na zdarzenia widgetu. Zwraca funkcję do odsubskrybowania.

widget.on('message', (msg) => console.log(`${msg.role}: ${msg.content}`))
widget.on('tool:call', (call) => console.log(`Tool: ${call.name}`, call.args))
widget.on('open', () => console.log('Widget otwarty'))
widget.on('close', () => console.log('Widget zamknięty'))
widget.on('ready', () => console.log('Widget gotowy'))
widget.on('error', (err) => console.error('Błąd:', err.message))

widget.open() / widget.close() / widget.destroy()

widget.open()      // Otwórz panel czatu
widget.close()     // Zamknij panel czatu
widget.destroy()   // Usuń widget ze strony i wyczyść listenery

Style — widget.updateStyles(styles)

Steruj wyglądem widgetu z poziomu kodu. Style można podać przy inicjalizacji (init({ styles })) lub zmienić w runtime (np. przy zmianie theme).

Podanie przy inicjalizacji

const widget = MaxAI.init({
  key: 'KLUCZ',
  styles: {
    theme: 'dark',
    density: 'compact',
    radius: 14,
    fontSize: 13,
    hideHeader: true,
    hideLauncher: true,
    maxHeight: 500,
    container: {
      background: '#07111a',
      border: '1px solid rgba(255,255,255,0.08)',
      shadow: '0 8px 32px rgba(0,0,0,0.3)',
    },
    bubbles: {
      assistantBg: '#0c1824',
      userBg: '#13283a',
      assistantText: '#e2e8f0',
      userText: '#f1f5f9',
      borderRadius: 12,
    },
    input: {
      background: '#091521',
      border: '1px solid rgba(255,255,255,0.06)',
      textColor: '#f1f5f9',
      placeholderColor: '#7f92a6',
    },
    header: {
      background: '#0a1929',
      textColor: '#e2e8f0',
      border: '1px solid rgba(255,255,255,0.06)',
    },
  },
})

Zmiana w runtime (np. theme toggle)

// Aktualizuje WSZYSTKIE widgety: bubble + wszystkie inline
widget.updateStyles({ theme: 'light', density: 'default' })

Pełna lista WidgetStyles

| Pole | Typ | Opis | |---|---|---| | theme | "dark" \| "light" \| "auto" | Motyw kolorystyczny | | density | "default" \| "compact" \| "dense" | Gęstość treści (padding, gap, font) | | radius | number | Border radius kontenera w px | | fontSize | number | Bazowy rozmiar fontu w px | | maxHeight | string \| number | Max wysokość (inline mode) | | minHeight | string \| number | Min wysokość | | width | string \| number | Szerokość | | height | string \| number | Wysokość | | hideHeader | boolean | Ukryj nagłówek | | hideInput | boolean | Ukryj pole wpisywania (read-only) | | hideLauncher | boolean | Ukryj przycisk launchera | | container | { background, border, shadow, borderRadius } | Style kontenera | | bubbles | { assistantBg, userBg, assistantText, userText, borderRadius } | Style dymków | | input | { background, border, textColor, placeholderColor } | Style pola wpisywania | | header | { background, textColor, border } | Style nagłówka |

Eksportowane stałe

import { WIDGET_THEMES, WIDGET_DENSITIES } from '@maxaiagent/widget-sdk'
// WIDGET_THEMES = ["dark", "light", "auto"] as const
// WIDGET_DENSITIES = ["default", "compact", "dense"] as const

import type { WidgetTheme, WidgetDensity } from '@maxaiagent/widget-sdk'

Inline Widget — widget.mountInline(el, options?)

Osadź widget bezpośrednio w kontenerze na stronie (zamiast pływającego bubble). Zwraca InlineInstance — ref do sterowania tym konkretnym widgetem.

Sposób 1: Z kodu (SDK)

const widget = MaxAI.init({ key: 'KLUCZ' })

const chatRef = widget.mountInline('#news-chat-panel', {
  sessionKey: 'article-123',      // Opcjonalne — klucz sesji (patrz niżej)
  sessionPolicy: 'per-key',       // 'per-key' | 'sticky' | 'always-new'
  styles: { maxHeight: 500 },     // Opcjonalne — style per-instancja
})

Sposób 2: Z HTML (auto-mount)

Dodaj div z klasą maxai-inline — SDK automatycznie zamontuje widget (także dla elementów dodanych dynamicznie przez SPA):

<div
  class="maxai-inline"
  data-key="TWOJ_KLUCZ"
  data-session-key="article-123"
  style="height: 500px"
></div>

| Atrybut | Opis | |---|---| | data-key | Klucz widgetu (opcjonalny jeśli podany w init()) | | data-session-key | Klucz sesji — różne klucze = osobne rozmowy | | data-context-* | Dodatkowy kontekst, np. data-context-article-id="123" |

Dostęp do instancji przez ref (Vue / React)

Jeśli widget jest zamontowany przez auto-mount (klasa maxai-inline), możesz uzyskać dostęp do jego API przez getInlineInstance():

Vue 3:

<template>
  <div class="maxai-inline" data-key="KLUCZ" data-session-key="news-123" ref="chatDiv" />
</template>

<script setup>
import { ref, onMounted } from 'vue'

const chatDiv = ref()

onMounted(() => {
  const inst = MaxAI.getInlineInstance(chatDiv.value)
  inst?.addMessage('Witaj! Oto podsumowanie tego artykułu...')
})
</script>

React:

import { useRef, useEffect } from 'react'

function NewsChat({ newsId }: { newsId: string }) {
  const chatRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    const inst = MaxAI.getInlineInstance(chatRef.current!)
    inst?.setSessionKey(newsId)
    inst?.addMessage('Oto podsumowanie...')
  }, [newsId])

  return <div className="maxai-inline" data-key="KLUCZ" ref={chatRef} style={{ height: 500 }} />
}

Sesje — InlineInstance API

Każdy inline widget może mieć osobną sesję powiązaną z kluczem (sessionKey). To umożliwia np. osobną rozmowę per artykuł, per produkt, per ticket.

inline.setSessionKey(key)

Zmiana klucza sesji. Gdy klucz się zmienia — widget czyści rozmowę i startuje nową sesję. Gdy klucz jest taki sam — no-op (deduplikacja, bez resetu przy rerender).

const chat = widget.mountInline('#panel', { sessionKey: 'news-A' })

// Użytkownik przełącza się na inny news:
chat.setSessionKey('news-B')  // → nowa sesja

// Powrót do poprzedniego newsa — sesja jest zachowana:
chat.setSessionKey('news-A')  // → przywraca wcześniejszą rozmowę

Zachowanie sesji

| Akcja | Efekt | |---|---| | setSessionKey('X') gdy current = 'X' | no-op (deduplikacja) | | setSessionKey('Y') gdy current = 'X' | clear + nowa sesja dla 'Y' | | Rerender komponentu Vue/React | nic — nie resetuje sesji | | resetSession() | Force clear nawet dla tego samego klucza |

Sesje per-key są trzymane w localStorage jako maxai_st_{widgetKey}_scope_{sessionKey}. Powrót do wcześniejszego klucza przywraca zapisaną rozmowę.

sessionPolicy

| Wartość | Zachowanie | |---|---| | "per-key" (domyślne) | Nowa sesja gdy sessionKey się zmienia | | "sticky" | Nigdy nie resetuj sesji (nawet przy zmianie klucza) | | "always-new" | Nowa sesja przy każdym wywołaniu |


Wiadomości — inline.addMessage() / widget.addMessage()

Wstrzyknij wiadomość do czatu programowo (np. gotowy dymek z podsumowaniem).

// Na konkretnej instancji inline:
chat.addMessage('Oto podsumowanie tego artykułu...')
chat.addMessage('Mam pytanie o cenę', 'user')

// Na głównym bubble widgecie:
widget.addMessage('Witaj! Jak mogę Ci pomóc?')
widget.addMessage('Chcę zwrócić produkt', 'user')

Parametry:

  • content: string — treść wiadomości
  • role?: "assistant" | "user" — domyślnie "assistant"

Pełne API InlineInstance

Zwracane przez widget.mountInline() lub MaxAI.getInlineInstance(el).

| Metoda | Opis | |---|---| | setSessionKey(key) | Zmień klucz sesji (nowy klucz = nowa rozmowa) | | addMessage(content, role?) | Wstrzyknij wiadomość do czatu | | updateStyles(styles) | Zmień style tej instancji | | resetSession() | Wymuś nową sesję (nawet dla tego samego klucza) | | destroy() | Usuń widget z DOM | | sessionKey (readonly) | Aktualny klucz sesji | | container | HTMLElement kontenera | | iframe | HTMLIFrameElement |


Pełne API WidgetInstance

Zwracane przez MaxAI.init().

| Metoda | Opis | |---|---| | mountInline(el, options?) | Zamontuj inline widget, zwraca InlineInstance | | getInlineInstance(el) | Pobierz instancję zamontowaną na danym elemencie | | setPageContext(ctx) | Wyślij kontekst strony do agenta | | setIdentity(payload) | Ustaw tożsamość użytkownika (HMAC podpis) | | registerTool(def) | Zarejestruj narzędzie klienta | | unregisterTool(slug) | Wyrejestruj narzędzie | | on(event, callback) | Nasłuchuj na zdarzenia | | off(event, callback) | Odsubskrybuj zdarzenie | | updateStyles(styles) | Zmień style na bubble + wszystkich inline | | addMessage(content, role?) | Wstrzyknij wiadomość do bubble widgetu | | open() | Otwórz panel czatu | | close() | Zamknij panel czatu | | destroy() | Usuń widget i wyczyść listenery |


Atrybuty data-context-*

Dodatkowy kontekst można przekazać bezpośrednio w tagu <script> lub na kontenerze inline:

<script
  src="https://maxaiagent.app/sdk.js"
  data-key="TWOJ_KLUCZ"
  data-context-page="product"
  data-context-user-type="premium"
></script>

Agent otrzyma te dane jako customContext: { page: 'product', user_type: 'premium' }.


Przykłady

Terminal / News — inline widget z sesjami per artykuł

import { MaxAI, type InlineInstance } from '@maxaiagent/widget-sdk'

const widget = MaxAI.init({
  key: 'KLUCZ',
  styles: {
    theme: 'dark',
    density: 'compact',
    container: { background: '#07111a', border: '1px solid rgba(255,255,255,0.08)' },
    bubbles: { assistantBg: '#0c1824', userBg: '#13283a' },
    input: { background: '#091521', placeholderColor: '#7f92a6' },
  },
})

let chatRef: InlineInstance | null = null

function showNews(newsId: string, title: string, summary: string) {
  if (!chatRef) {
    chatRef = widget.mountInline('#news-chat', {
      sessionKey: newsId,
      styles: { maxHeight: 500, hideHeader: true, hideLauncher: true },
    })
  } else {
    chatRef.setSessionKey(newsId)
  }
  chatRef.addMessage(`**${title}**\n\n${summary}`)
}

// Użytkownik klika na news:
showNews('bybit-risk-2026', 'Bybit zmienia limity ryzyka', 'Bybit dostosowuje limity...')

// Przełącza na inny news:
showNews('shib-81b', 'SHIB nadpodaż', '81 bilionów tokenów...')

// Powrót do pierwszego — sesja zachowana:
showNews('bybit-risk-2026', 'Bybit zmienia limity ryzyka', 'Bybit dostosowuje limity...')

E-commerce — strona produktu (CDN)

<script src="https://maxaiagent.app/sdk.js" data-key="KLUCZ"></script>
<script>
  MaxAI.setPageContext({
    pageType: 'product',
    visibleData: {
      name: document.querySelector('h1').textContent,
      price: document.querySelector('.price').textContent,
    },
  })

  MaxAI.registerTool({
    slug: 'add_to_cart',
    name: 'Dodaj do koszyka',
    description: 'Dodaje produkt do koszyka',
    schema: {
      type: 'object',
      properties: {
        size: { type: 'string', description: 'Rozmiar' },
        quantity: { type: 'number', default: 1 },
      },
    },
    requiresConfirmation: true,
    handler: async (args) => {
      document.querySelector('.add-to-cart-btn').click()
      return { success: true }
    },
  })
</script>

SPA (Vue 3) — dynamiczny theme toggle

<template>
  <button @click="toggleTheme">Toggle theme</button>
</template>

<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { MaxAI } from '@maxaiagent/widget-sdk'

const isDark = ref(true)
let widget = null

onMounted(() => {
  widget = MaxAI.init({
    key: 'KLUCZ',
    styles: { theme: 'dark' },
  })
})

onUnmounted(() => widget?.destroy())

function toggleTheme() {
  isDark.value = !isDark.value
  widget?.updateStyles({ theme: isDark.value ? 'dark' : 'light' })
}
</script>

Typy TypeScript

import type {
  MaxAIInitOptions,
  PageContext,
  ToolDefinition,
  WidgetInstance,
  WidgetEvent,
  ChatMessage,
  ToolCallEvent,
  WidgetStyles,
  WidgetTheme,
  WidgetDensity,
  SessionPolicy,
  InlineMountOptions,
  InlineInstance,
} from '@maxaiagent/widget-sdk'

import { WIDGET_THEMES, WIDGET_DENSITIES } from '@maxaiagent/widget-sdk'

Funkcje automatyczne

Po włączeniu w panelu MaxAI (zakładka "Inteligencja"):

| Funkcja | Opis | Wymaga kodu? | |---|---|---| | Kontekst strony | Agent widzi tytuł, URL, meta, JSON-LD, treść <main> | Nie | | Point-and-Ask | Użytkownik wskazuje element na stronie | Nie | | Zgłaszanie problemów | Przycisk bug report z logami konsoli | Nie | | GDPR | Baner zgody przed zbieraniem danych | Nie | | Auto-adaptive theme | Widget dopasowuje kolory do strony | Nie | | Glassmorphism | Półprzezroczysty efekt szkła | Nie | | Narzędzia klienta | registerTool() — agent wywołuje funkcje | Tak | | Explicit context | setPageContext() — dodatkowe dane | Tak | | Inline widget | mountInline() — osadzony w kontenerze | Tak | | Style SDK | updateStyles() — zmiana wyglądu z kodu | Tak | | Sesje per-key | setSessionKey() — osobna rozmowa per kontekst | Tak | | Inject messages | addMessage() — wstrzyknij dymek programowo | Tak |