TokElementsDOCS
SDK WIDGETÓW / V1
01

Pierwsze kroki

1
Napisz markup i style

Zakładki HTML i CSS rysują Twój widget. Ramka jest przezroczysta i unosi się nad obrazem – nigdy nie maluj pełnego, nieprzezroczystego tła.

2
Reaguj na zdarzenia

W zakładce JS subskrybuj przez TE.on('gift', fn). window.TE jest już załadowane – bez importów, bez konfiguracji.

3
Przetestuj i dodaj

Użyj Symuluj albo przycisków Fire, żeby podejrzeć offline, a potem Dodaj do overlayu – albo pozwól Claude zbudować go przez MCP.

Twój pierwszy widget – podziękowanie za prezent· edytuj JS, a podgląd reaguje na żywo
var box = document.getElementById('box');
TE.on('gift', function (ev) {
  box.textContent = ev.user.name + ' sent ' + ev.gift.name + '!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
Podgląd na żywo
02

Połącz Claude (MCP)

Twórz i edytuj te widgety prosto z Claude Desktop lub Claude Code. Wygeneruj token, dodaj konektor, a Claude będzie mógł wywoływać te narzędzia na Twoim koncie:

get_docsPełny poradnik tworzenia + każdy trigger i akcja (Claude czyta to najpierw).
list_widgetsLista zapisanych widgetów z ich id.
get_widgetPobiera html / css / js jednego widgetu.
create_widgetTworzy nowy widget HTML z nazwy + html/css/js.
update_widgetEdytuje istniejący widget.
delete_widgetUsuwa widget po id.
list_templatesLista wbudowanych szablonów do skopiowania.
get_templateOdczytuje html/css/js szablonu.
create_from_templateKopiuje szablon do nowego widgetu.
list_community_templatesPrzeglądaj widgety opublikowane przez społeczność.
install_community_templateInstaluje prywatną, edytowalną kopię szablonu od społeczności.
Ładowanie…
03

SDK TE

Wszystko, co robi widget, przechodzi przez globalny obiekt window.TE.

TE.on(type, fn)

Subskrybuje zdarzenie na żywo. `type` to dowolny trigger poniżej; `fn(ev)` uruchamia się za każdym razem, gdy ono wystąpi.

TE.on('*', fn)

Łapie każde zdarzenie. `fn(ev, type)` dostaje dane i nazwę zdarzenia.

TE.off(type, fn)

Usuwa wcześniej zarejestrowany handler.

TE.onGift(name, fn)

Odpala się tylko dla prezentów o pasującej nazwie (fragment tekstu, bez rozróżniania wielkości liter). Pomiń `name`, żeby łapać każdy prezent.

TE.onSticker(idOrUrl, fn)

Odpala się tylko dla jednej konkretnej emotki / naklejki subskrybenta – dopasowanej po id TikToka, URL obrazka albo źródle.

TE.rules.allowUser / allowGift

Filtry uprawnień wielokrotnego użytku dla ról, list dozwolonych/zabronionych, nazwy prezentu i minimalnej wartości w monetach.

TE.metrics

Najnowszy obiekt migawki streamu (viewers, likes, coins, followers, topGifters…). Dostarczany też przez TE.on('metrics', fn).

TE.demo

True tam, gdzie widget jest prezentowany (galeria, kafelki podglądu, przełącznik Demo w kreatorze). False na overlayu na żywo oraz podczas testów w kreatorze lub edytorze overlayu, gdzie pojawia się tylko to, co odpalisz, albo to, co przychodzi na żywo. Każdy widget pokazuje się tam sam: przykłady wyglądające jak prawdziwe zdarzenia (prawdziwe grafiki prezentów, nicki, awatary) umieść w bloku if (TE.demo), przez prawdziwą ścieżkę kodu widgetu. Widget z takim blokiem nie dostaje od hosta zdarzeń podglądu, więc nic nie odpala się dwa razy.

TE.defineSettings([...])

Deklaruje kontrolki, które streamer może zmieniać; zwraca aktualne wartości (domyślne połączone z wyborami streamera).

TE.settings

Aktualny obiekt z wartościami ustawień (ten sam kształt, który zwróciło defineSettings).

TE.on('settings', fn)

Uruchamia się, gdy streamer zmienia ustawienie na żywo – wyrenderuj ponownie z nowymi wartościami.

TE.state.get/set/increment

Atomowe wartości oparte na Promise do sum i wspólnego stanu gry.

TE.collection.*

Zarządzani przez serwer uczestnicy (dołączenie raz) i tabele wyników: join, increment, list, count, remove i clear.

TE.queue.*

Ograniczone kolejki FIFO po stronie serwera na media, prośby i akcje wywoływane przez widzów.

TE.shared.*

Łączy kilka widgetów w jedną nazwaną przestrzeń wspólnego stanu kanału; niepowiązane widgety pozostają odizolowane.

TE.random.draw(name, opts)

Bezpiecznie losuje jednego lub więcej zwycięzców z kolekcji i zapisuje wynik.

TE.cooldown.claim(scope, user, ms)

Atomowo zajmuje globalny lub per-użytkownik cooldown; zwraca claimed i retryAfterMs.

TE.timer.*

Uruchamia, wstrzymuje, resetuje i odczytuje zapisany timer czasu rzeczywistego, który przetrwa przeładowanie.

TE.points.trySpend(user, amount, reason)

Sprawdza i pobiera punkty w jednej transakcji; zawsze sprawdź result.ok przed uruchomieniem płatnej interakcji.

04

Triggery

Zdarzenia na żywo, których możesz nasłuchiwać. Każdy user zawiera { id, name, username, avatar, roles }.

TE.on('gift')

Widz wysłał prezent. coins = suma za combo; streakEnd oznacza koniec combo.

{ user, gift: { name, image|null, coins, repeat, combo, streakEnd } }
TE.on('follow')

Widz zaobserwował konto.

{ user }
TE.on('subscribe')

Widz wykupił subskrypcję. months = ile miesięcy z rzędu.

{ user, months }
TE.on('share')

Widz udostępnił LIVE.

{ user }
TE.on('chat')

Wiadomość na czacie. emotes = URL-e obrazków emotek subskrybentów w tej wiadomości.

{ user, comment, emotes:[url,…] }
TE.on('like')

Widz wysłał lajki. count = ta seria; total = suma bieżąca.

{ user, count, total }
TE.on('join')

Widz wszedł do pokoju. isTop = dołączył top darczyńca.

{ user, isTop }
TE.on('sticker')

Emotka subskrybenta lub naklejka na ekranie wysłana na żywo. url = obrazek emotki; id = jej id na TikToku.

{ user|null, id|null, url, source }
TE.on('metrics')

Najnowsza migawka streamu. TE.metrics ją przechowuje; zdarzenie odpala się przy każdej aktualizacji. hostNick i hostAvatar to nick i zdjęcie profilowe samego streamera.

{ live, viewers, likes, followers, coins, hostNick, hostAvatar, topGifters:[…], … }
TE.on('milestone')

Przekroczono okrągły kamień milowy (lajki, monety, obserwujący, suby).

{ metric, value, label }
TE.on('poll')

Natywna ankieta TikTok LIVE – prawdziwe głosy, od początku do końca.

{ state:'start'|'update'|'end', title, options:[{text,votes}], endsAt }
TE.on('battle')

Aktualizacje bitew LinkMic: karty bitwy, fan tickets, wielkość armii.

{ card|null, tickets|null, armies|null, battleId|null }
TE.on('rank')

Pozycja w rankingu godzinowym i momenty awansu.

{ rank|null, from|null, to|null, countdown|null }
TE.on('envelope')

Czerwona koperta / skrzynia skarbów zrzucona na LIVE.

{ }
TE.on('pinned')

Host przypiął komentarz.

{ text }
TE.on('deleted')

Moderator usunął wiadomość z czatu (id pasuje do wcześniejszego zdarzenia chat).

{ id }
TE.on('streamState')

Stream wystartował lub się zakończył.

{ live }
TE.on('apiEvent')

Własne zdarzenie wysłane przez Event API. Nasłuchuj apiEvent albo bezpośrednio jego własnej nazwy.

{ name, data }
TE.on('nowPlaying')

Aktualny utwór streamera na Spotify ORAZ kolejka następnych. Odpala się po połączeniu i przy każdej zmianie utworu, stanu odtwarzania, postępu lub kolejki. playback to null, gdy nic nie gra; queue to nadchodzące utwory.

{ connected, playback: { isPlaying, progressMs, durationMs, title, artists:[…], album, artwork|null } | null, queue:[{ title, artist, artwork|null, durationMs }] }
Tylko jeden konkretny prezent lub emotka
TE.onGift('Rose', function (ev) { /* … */ });
TE.onSticker('<sticker id>', function (ev) { /* … */ });

Emotki Twojego kanału są w kreatorze widgetów: otwórz Symuluj zdarzenia, wybierz Naklejka i wskaż jedną. Zdarzenie testowe ma jej prawdziwe id i URL obrazka, więc handler oparty na którymkolwiek z nich jest testowany na prawdziwym przykładzie.

05

Akcje

Co Twój widget może zrobić w odpowiedzi – zwykłe API przeglądarki, gotowe do wklejenia.

Pokaż obrazek

Wyświetl obrazek na ekranie – grafikę prezentu/emotki, wgrany plik albo dowolny URL. Znika sam po kilku sekundach.

// Show an image, then fade it out
TE.on('gift', function (ev) {
  var img = document.createElement('img');
  img.src = ev.gift.image;                 // ← any image URL works
  img.style.cssText = 'position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);max-width:60%';
  document.body.appendChild(img);
  setTimeout(function () { img.remove(); }, 4000);   // ← how long it stays
});
Odtwórz dźwięk

Odtwórz dźwięk przy zdarzeniu. Udostępnij ustawienie dźwięku, żeby streamer wybrał własny plik – bez edycji kodu.

// Play a sound the streamer chose in settings
var s = TE.defineSettings([{ key: 'sfx', label: 'Alert sound', type: 'sound', default: '' }]);
TE.on('settings', function (ns) { s = ns; });
TE.on('gift', function (ev) {
  if (s.sfx) { var a = new Audio(s.sfx); a.volume = 0.8; a.play(); }
});
Pokaż / animuj tekst

Wypisz dynamiczny tekst i ponownie odpal animację CSS, przełączając klasę.

// Announce the event with a CSS pop animation
var box = document.getElementById('box');   // your element in the HTML tab
TE.on('follow', function (ev) {
  box.textContent = ev.user.name + ' followed!';
  box.classList.remove('pop'); void box.offsetWidth; box.classList.add('pop');
});
Pokaż statystykę na żywo

Powiąż liczbę na ekranie z metryką na żywo – widzowie, lajki, monety, obserwujący – aktualizowaną automatycznie.

// Keep a number in sync with the live stream
TE.on('metrics', function (m) {
  document.getElementById('count').textContent = m.viewers.toLocaleString();
});
Przeczytaj na głos (TTS)

Wypowiedz zdarzenie przez syntezator mowy przeglądarki. Świetne do podziękowań za prezenty i obserwacje.

// Text-to-speech shout-out
TE.on('gift', function (ev) {
  var u = new SpeechSynthesisUtterance(ev.user.name + ' sent ' + ev.gift.name);
  speechSynthesis.speak(u);
});
Podgląd (TE.demo)

Każdy widget pokazuje się w galerii i kreatorze: przykłady wyglądające jak prawdziwe zdarzenia, z prawdziwymi grafikami prezentów, nickami i awatarami. Na żywo TE.demo ma wartość false i pojawiają się tylko prawdziwe zdarzenia.

// Preview samples through the real code path. Never on a live overlay.
if (TE.demo) {
  var GIFTS = [
    { name: 'Rose', coins: 1, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/eba3a9bb85c33e017f3648eaf88d7189~tplv-obj.png' },
    { name: 'Perfume', coins: 20, image: 'https://p16-webcast.tiktokcdn.com/img/maliva/webcast-va/20b8f61246c7b6032777bb81bf4ee055~tplv-obj.png' }
  ];
  var NAMES = ['lunaa', 'nightowl_gaming_official'], n = 0;
  function sample() {
    n++;
    onGift({ user: { name: NAMES[n % 2], avatar: window.__TE_AVATAR }, gift: GIFTS[n % 2] });
  }
  sample();
  setInterval(sample, 6000);
}
Zapamiętuj rzeczy (TE.store)

Prosta synchroniczna trwałość dla stanu wizualnego. Do uczestników, zakupów i wspólnych wyników używaj transakcyjnego runtime’u.

// Persistent state: survives OBS reloads
var total = TE.store.get('total', 0);          // read (with default)
TE.on('gift', function (ev) {
  total += ev.gift.coins;
  TE.store.set('total', total);                 // write (auto-saved)
  render();
});
Reaguj na komendy z czatu

Dokładnie dopasowane komendy z czatu plus atomowe kolekcje umożliwiają bezpieczne zapisy, głosowania i losowania.

// One server-authoritative entry per viewer.
// A function keyword reads s.command on every message, so the
// streamer can change the command in settings without a reload.
TE.onCommand(function () { return s.command || '!join'; }, function (c) {
  TE.collection.join('entrants', c.user.id, c.user).then(function (result) {
    if (result.joined) renderCount(result.count);
  });
});
Wywołuj akcje streamu (TE.act)

Wywołuj alerty overlayu, dźwięki, TTS, liczniki, czas subathonu albo przełączanie scen OBS bezpośrednio z widgetu.

// One call, one stream action
TE.act({ id: 'tts', message: 'New high score!' });
TE.act({ id: 'points', user: ev.user, amount: 50 });
Przyznawaj punkty widzom

Transakcyjnie sprawdzaj, wydawaj lub przyznawaj punkty lojalnościowe i otrzymuj wynikowe saldo.

// Only spin after the points were really debited
TE.onCommand(function () { return s.command || '!spin'; }, function (ev) {
  TE.points.trySpend(ev.user, 50, 'Wheel spin').then(function (result) {
    if (result.ok) spin();
    else showMissing(result.missing);
  });
});
Twórz uczciwe gry

Atomowy stan, kolekcje, bezpieczne losowania, cooldowny i trwałe timery – bezpieczne przy zduplikowanych źródłach przeglądarki.

// Fair draw from a server-owned entrant collection
TE.random.draw('entrants', { count: 1 }).then(function (result) {
  if (result.ok) reveal(result.winners[0].value);
});
Dodaj kontrolki dla streamera

Udostępnij tekst, kolory, liczby, przełączniki – streamer zmienia je na żywo w edytorze overlayu, bez kodu.

// No-code controls for whoever uses the widget
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',  default: 'Goal' },
  { key: 'accent', label: 'Color', type: 'color', default: '#FF2E4D' },
]);
document.body.style.setProperty('--accent', s.accent);
TE.on('settings', function (ns) { s = ns; /* re-render with new values */ });
06

Ustawienia streamera

Zadeklaruj kontrolki przez TE.defineSettings([...]), a każdy, kto używa widgetu, zmienia je na żywo w edytorze overlayu – bez kodu. Każde pole:

Umieszczony widget z jednym lub kilkoma polami 'button' udostępnia te akcje w swoich ustawieniach w Edytorze overlayu. Kreator widgetów pokazuje te same przyciski do bezpiecznego testowania podczas kodowania.

typwyświetlawartość
'text'Jednoliniowe pole tekstowestring
'number'Pole liczbowenumber
'color'Próbnik kolorustring hex, np. "#FF2E4D"
'select'Lista rozwijana (wymaga options: [...])jedna z opcji
'toggle'Przełącznik wł./wył.boolean
'range'Suwak (min / max / step)number
'button'Deklaratywny przycisk akcjiincrement, set lub toggle – walidowany JSON, nigdy kod panelu
'sound'Wybór spośród wgranych dźwięków streamera + UploadURL wybranego pliku ("" = brak)
'image'Wybór spośród wgranych obrazków streamera + UploadURL wybranego pliku ("" = brak)
var s = TE.defineSettings([
  { key: 'title',  label: 'Title', type: 'text',   default: 'Follower goal' },
  { key: 'target', label: 'Goal',  type: 'number', default: 100 },
  { key: 'accent', label: 'Color', type: 'color',  default: '#FF2E4D' },
  { key: 'reset',  label: 'Reset', type: 'button', default: 0,
    action: { type: 'increment', step: 1 }, confirm: true, tone: 'danger' },
]);
// re-render when the streamer changes something live
var lastReset = s.reset;
TE.on('settings', function (ns) {
  s = ns;
  if (ns.reset !== lastReset) { lastReset = ns.reset; resetCounter(); }
  render();
});

Definicje kontrolek to walidowane dane. Przycisk action przyjmuje tylko increment, set lub toggle. HTML, kod callbacków i dowolne URL-e są odrzucane lub ignorowane przez renderer panelu.

07

Piaskownica i limity

Widgety działają w odizolowanym iframe ze ścisłą polityką bezpieczeństwa treści (CSP), więc zepsuty lub złośliwy widget nigdy nie dotknie Twojego konta ani reszty strony.

Dozwolone
  • Zdalne obrazki i GIF-y (grafiki prezentów TikTok, awatary, dowolny URL)
  • Google Fonts + własne @font-face
  • Dźwięk przez new Audio(url)
  • Zapytania do własnych API TokElements (np. /api/files/…)
  • Animacje CSS, SVG, canvas, Web Audio
Zablokowane
  • Zewnętrzne tagi <script> / CDN-y
  • fetch / XHR / WebSocket do innych hostów
  • Ciasteczka, localStorage, dostęp do strony nadrzędnej
  • Ładowanie pakietów npm
08

Przykłady

Kompletne widgety do skopiowania i wklejenia. Każdy podgląd działa na żywo – zdarzenia demo się odpalają, więc widzisz, jak reaguje.

Reakcja na emotkę· wyświetla emotkę, gdy ktoś wyśle ją na czacie
var img = document.getElementById('emote');
// fires for every emote — use TE.onSticker('<id>', …) for one specific one
TE.on('sticker', function (ev) {
  img.src = ev.url;
  img.classList.remove('show'); void img.offsetWidth; img.classList.add('show');
});
Podgląd na żywo
Pasek celu obserwacji· wiąże pasek z metryką na żywo
var target = 200;                       // ← your goal
TE.on('metrics', function (m) {
  var pct = Math.min(100, m.follows / target * 100);
  document.getElementById('fill').style.width = pct + '%';
  document.getElementById('txt').textContent = m.follows + ' / ' + target;
});
Podgląd na żywo