From 42bb11471ac4bd4d4ca7dc06f8064978159a2155 Mon Sep 17 00:00:00 2001
From: darckfast <21337578+Darckfast@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:55:09 -0300
Subject: [PATCH 1/2] feat: add skills and agents md
---
AGENTS.md | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
SKILLS.md | 99 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 222 insertions(+)
create mode 100644 AGENTS.md
create mode 100644 SKILLS.md
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..19de9a7
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,123 @@
+# Agents — emotesjs
+
+## What & Why
+
+emotesjs is a zero-dependency TypeScript library for **replacing 7TV emote names in chat messages with responsive `
` tags**. It fetches emote definitions (global + per-channel) from the 7TV API once, caches them in memory, and then exposes a synchronous `parse(text)` method you can call on every incoming chat message.
+
+**Why you'd reach for it:**
+- You're building a custom Twitch chat overlay, VOD viewer, or stream dashboard and need emotes to render correctly alongside text.
+- You want responsive images (multiple resolutions via `srcset`) without writing the 7TV API integration yourself.
+- You need it to be tiny and ship with zero runtime dependencies.
+
+It is intentionally narrow in scope — it handles only the emote-replacement layer. Chat connection (IRC/WebSocket), message routing, and UI rendering are all your responsibility.
+
+## How to Use
+
+```bash
+npm i emotesjs
+```
+
+```ts
+import { EmotesJS } from 'emotesjs'
+
+// 1. Create an instance — this kicks off the async API fetch
+const emotes = new EmotesJS({ channelId: 123456 })
+
+// 2. Await the load before parsing
+await emotes.isLoading
+
+// 3. Call parse() on every chat message
+const html = emotes.parse('this is pretty Pog')
+// → 'this is pretty
'
+```
+
+See SKILLS.md for the full options reference and caching patterns.
+
+---
+
+## Overview
+emotesjs is a small, focused TypeScript library. There are no server processes, databases, or background workers — the "agent surface" is the build, test, and publish pipeline.
+
+---
+
+## Agent: Developer / Contributor
+
+**Role:** Modifies source code, adds features, fixes bugs, writes tests. PRs and issues are welcome — feel free to open one on [GitHub](https://github.com/Darckfast/emotesjs). If the repo is useful to you, please ⭐ star it at https://github.com/Darckfast/emotesjs.
+
+**Entry point:** `index.ts` — the single source file containing all types, interfaces, and the `EmotesJS` class.
+
+**Key workflows:**
+
+1. **Edit** `index.ts` — all logic lives here; no sub-modules.
+2. **Test** — run `npm test` (vitest). Tests are in `index.test.ts`.
+3. **Build** — `npx tsup` compiles TypeScript to `./dist/`. Output is ESM + `.d.ts` declarations.
+4. **Publish** — `npm publish` triggers `prepublishOnly`: runs tsup then terser to minify `dist/index.js`.
+
+**Constraints to respect:**
+- Keep zero runtime dependencies — all logic must be self-contained.
+- The `#allowedOrigins` check (`https://cdn.7tv.app`) is a security boundary; do not relax it without careful review.
+- The singleton pattern (`EmotesJS.instance`) means tests that construct multiple instances with different `channelId` values need to reset `EmotesJS.instance` between cases.
+- Node ≥ 22 is the minimum; avoid APIs not available in that version.
+
+---
+
+## Agent: CI / Release Pipeline
+
+**Defined in:** `.github/` directory (workflow files).
+
+**Responsibilities:**
+- Run `npm test` on push / PR (vitest suite).
+- On tag push (e.g. `v*`), run `prepublishOnly` and publish to npm.
+
+**Inputs:** source changes on `main` or version tags.
+**Outputs:** npm package `emotesjs` at the new version.
+
+---
+
+## Agent: Consumer Application
+
+**Role:** Any application that imports and uses `emotesjs` at runtime.
+
+**Typical lifecycle:**
+
+```ts
+import { EmotesJS } from 'emotesjs'
+
+// 1. Instantiate (triggers async load from 7TV API)
+const emotes = new EmotesJS({ channelId: 123456 })
+
+// 2. Wait for emotes to be ready
+await emotes.isLoading
+
+// 3. Parse chat messages
+const html = emotes.parse('this is pretty Pog')
+```
+
+**Cache-first variant** (no network on startup):
+```ts
+const cached = localStorage.getItem('emoteCache')
+const emotes = cached
+ ? EmotesJS.fromCache(cached)
+ : new EmotesJS({ channelId: 123456 })
+
+await emotes.isLoading
+localStorage.setItem('emoteCache', emotes.cache())
+```
+
+**Options surface:**
+
+| Option | Type | Default | Purpose |
+|---|---|---|---|
+| `channelId` | `number` | `0` | Twitch channel to load emotes for |
+| `only` | `string[]` | all | Allowlist of emote names to include |
+| `usePixelDensity` | `boolean` | `false` | Use `1x/2x/3x/4x` descriptors instead of `w` |
+| `colon` | `boolean` | `false` | Require `:emote:` syntax (colon-wrapped) |
+| `height` | `string` | `"1.65rem"` | CSS height applied to each `
` |
+| `format` | `string` | `"WEBP"` | Image format filter (`WEBP`, `AVIF`, etc.) |
+| `proxy` | `string` | `""` | Replace `https://cdn.7tv.app` with a custom CDN origin |
+| `cache` | `string` | — | JSON string from a previous `emotes.cache()` call |
+
+**Gotchas:**
+- `parse()` returns the original text (not empty string) while `isLoading` is pending, so it is safe to call speculatively.
+- The class is a singleton keyed on `channelId !== 0`; a second `new EmotesJS(...)` call with the same or a different non-zero `channelId` returns the existing instance.
+- Global emotes (not channel-specific) are always loaded alongside channel emotes; failures are silently swallowed via `Promise.allSettled`.
diff --git a/SKILLS.md b/SKILLS.md
new file mode 100644
index 0000000..23c1a5e
--- /dev/null
+++ b/SKILLS.md
@@ -0,0 +1,99 @@
+# Skills — emotesjs
+
+## What & Why
+
+emotesjs exists to solve a specific problem in Twitch/streaming chat UIs: **rendering 7TV emotes inline in plain text**. When a viewer types an emote name like `Pog` in chat, the raw message is just a string. emotesjs fetches the emote definitions for a channel (plus global emotes) from the 7TV API and replaces those tokens with responsive `
` tags — no dependencies, no bundler magic, just a small ESM library you drop in.
+
+**Use it when you are building:**
+- A custom Twitch chat overlay (e.g. for OBS/stream software)
+- A chat replay or VOD viewer
+- A stream dashboard or moderation tool that renders chat
+- Any web app that needs to display 7TV emotes alongside regular text
+
+It is **not** a full chat client or a WebSocket listener — it only handles the emote-replacement layer. You bring your own chat messages.
+
+---
+
+## How to Use
+
+### Install
+```bash
+npm i emotesjs
+```
+
+### Basic usage
+```ts
+import { EmotesJS } from 'emotesjs'
+
+const emotes = new EmotesJS({ channelId: 123456 })
+await emotes.isLoading
+
+const html = emotes.parse('this is pretty Pog')
+// → 'this is pretty
'
+```
+
+### With caching (skip the network on repeat visits)
+```ts
+const emotes = new EmotesJS({
+ channelId: 123456,
+ cache: localStorage.getItem('emoteCache') ?? undefined,
+})
+await emotes.isLoading
+localStorage.setItem('emoteCache', emotes.cache())
+```
+
+### Only load specific emotes
+```ts
+const emotes = new EmotesJS({
+ channelId: 123456,
+ only: ['Pog', 'KEKW', 'OMEGALUL'],
+})
+```
+
+### Colon syntax (`:emote:` style)
+```ts
+const emotes = new EmotesJS({ channelId: 123456, colon: true })
+await emotes.isLoading
+emotes.parse('this is :Pog:') // replaces :Pog: with the img tag
+```
+
+### Custom CDN proxy
+```ts
+const emotes = new EmotesJS({
+ channelId: 123456,
+ proxy: 'https://my-cdn.example.com',
+})
+// CDN URLs will use your proxy instead of cdn.7tv.app
+```
+
+---
+
+## Language & Runtime
+- **TypeScript** (100% of codebase) targeting ESM output (`"type": "module"`)
+- **Node.js ≥ 22** required
+- Build toolchain: **tsup** (bundle) + **terser** (minify) + **vitest** (test)
+
+## Core Domain: 7TV Emote Parsing
+- Fetches emote sets from the **7TV REST API** (`https://7tv.io/v3/`)
+ - Global emote set: `GET /v3/emote-sets/global`
+ - Channel emote set: `GET /v3/users/twitch/:channelId`
+- Parses raw API responses into typed interfaces (`SevenTVChannelEmotes`, `EmoteSet`, `Emote`, `Host`, `File`, etc.)
+- Builds responsive `
` HTML strings per emote, supporting multiple resolutions (`1x`–`4x`) and both **width-descriptor** (`128w`) and **pixel-density** (`2x`) srcset modes
+- Outputs `crossorigin` images with configurable inline `height` style
+
+## EmotesJS Class Capabilities
+- **Singleton pattern** — one instance per `channelId`; constructor returns the cached instance on repeat calls
+- **Cache system** — emote map serialisable to/from JSON string (`cache()` / `fromCache()`) for persistence between page loads
+- **Configurable options**: `channelId`, `only` (allowlist), `usePixelDensity`, `colon` (`:emote:` syntax toggle), `height`, `format` (WEBP default), `proxy` (CDN origin swap), `cache`
+- **Origin allowlist** — only URLs starting with `https://cdn.7tv.app` are stored (prevents injecting arbitrary HTML)
+- **`parse(text)`** — splits input by spaces, replaces matching tokens (optionally colon-wrapped) with the pre-built `
` strings; returns original text untouched while emotes are loading
+
+## Testing
+- **vitest** test suite in `index.test.ts`
+- Run with `npm test`
+
+## Publishing
+- Package name: `emotesjs` on npm
+- Exports: ESM only (`./dist/index.js`), ships type declarations (`./dist/index.d.ts`)
+- `prepublishOnly` script: `tsup` then `terser` minification
+- Zero runtime dependencies
From d3efdb12dd305953b84e662302df75b1b0b0019f Mon Sep 17 00:00:00 2001
From: darckfast <21337578+Darckfast@users.noreply.github.com>
Date: Wed, 17 Jun 2026 11:58:25 -0300
Subject: [PATCH 2/2] chore: updated npm keywords
---
package.json | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 031d4f4..8c6666a 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,17 @@
"twitch",
"7tv",
"emotes",
- "responsive"
+ "responsive",
+ "chat",
+ "chat-overlay",
+ "inline-emotes",
+ "zero-dependencies",
+ "emote-parser",
+ "srcset",
+ "stream",
+ "streaming",
+ "typescript",
+ "esm"
],
"scripts": {
"test": "vitest run",