Expand description
heatsync-core — the brain. protocol types, heat ramp, emote resolution, ws parsing, mock feed, and the editing model. no terminal, no async, no i/o (clocks and keystrokes are passed in). every client is a thin face over this.
The editing half (key, edit, vi, slash, clip) lived in the tui
until a second face needed it. None of it ever touched the terminal except
for crossterm’s key type, which key now replaces — a face maps its own
keystrokes into key::KeyEvent and gets the same editor.
Modules§
- clip
- yank target. tries the system clipboard first, then falls back to OSC 52 so a yank still lands over ssh, where there is no local clipboard daemon to talk to. returns a short label naming which path worked, for the status line.
- complete
- tab completion for the composer — emote names and usernames, twitch-style.
- dupe
- twitch refuses a message identical to your previous one in the same channel (msg_duplicate). chatterino’s bypass: append an invisible tag char, so the wire text differs. it toggles — a third identical send goes out plain.
- edit
- a vi line editor for the composer. insert and normal sub-modes, motions, operators, registers and history — the same muscle memory as everywhere else, rather than a handful of emacs control chords.
- emote
- emote resolution. mirrors the server: /api/channel/:c/emotes returns a
precedence-ordered (7tv > bttv > ffz > twitch > kick) deduped list, so we
insert first-wins into a name→emote map and word-match message text against
it. modifiers (BTTV
w!-style prefixes / FFZffzX-style effect words) are parsed here into per-layerEffectlists; the pixel work happens at render. - heat
- the heat ramp — one decaying scalar → a stepped temperature color.
terminal-native: a tier is an ANSI color name plus vt320 attributes, so it
follows the user’s terminal palette. each frontend maps
Lookto its toolkit. - iso
- the server’s timestamps (
2026-10-04T20:00:00.000Z) as unix seconds. - key
- Face-independent key events.
- layout
- the heatsync.org
/m/<layout>share-link grammar, parse + build. - mock
- deterministic mock feed — proves the aesthetic before the relay WS exists. no rand crate, no real clock: a seeded xorshift + a synthetic ms clock advanced each tick. heat emerges from the same decaying-counter model the live feed uses, so the mock and the real thing behave identically.
- paint
- name paints: heatsync’s own paint spec and 7TV’s paint object, reduced to one gradient model a terminal can sample per character. pure data in, rgb out — fetching and caching live in the tui.
- proto
- HeatSync
/wsrelay protocol — parse inbound frames, build outbound ones. anon read is allowed, so no auth is required to consume chat. content and usernames are sanitized HERE, at the trust boundary, before anything else touches them (terminal-injection defense). - room
- room-mode changes → system notes. twitch re-sends ROOMSTATE on join and on
every mode change; the first state per channel is only remembered, later
ones say what changed (
slow mode 30s,emote-only on, …). - sanitize
- terminal-injection defense. chat text is UNTRUSTED — a raw escape sequence in a message could retitle the terminal, poke the clipboard, or worse on some emulators. we render into a terminal, so we strip every control byte before a message is ever handed to the tui. this is the one risk chatterino never has.
- scroll
- message-cursor math. a cursor names a line by its absolute sequence number
(how many lines the channel had appended before it), which stays put while
the ring evicts old lines, grows at the live end, or gets history
backfilled in front — so the view stays anchored without any bookkeeping on
arrival.
seq= lines ever appended,len= lines held now; the held lines areseq - len .. seq. - slash
- slash commands typed in the composer, the way every chat client does it.
- social
- the account side of the virtual tabs: dms, whispers and mention-rule hits
as the
/api/cli/*routes return them, and the request bodies for sending. pure — no network; the tui does the calls and feeds the answers here. - streamev
- stream-event anti-wall state machine — port of the extension’s go-live
pairing + guards (stream-event-text.js
createGoLivePairing, main.jssessionWentLiveSeen, the 60s text dedupe). pure: the clock is passed in asnow_ms, nothing here sleeps or reads a timer. feed it every parsedChatLine; lines without stream facts pass straight through. - vi
- vi navigation over the chat ring: a cursor, a linewise visual selection, counts, and search. no rendering and no I/O — main.rs owns the keymap and the drawing, this owns the model, which is what makes the motions testable.
Structs§
- Badge
- a badge plus its version — for a subscriber, the months tier twitch sent.
- Channel
- live state for a single channel. bounded ring buffer — a raid can flood
forever and we never grow past
cap(mele is 8GB; resource-conscious). heat is a decaying counter driven by message arrivals. - Extra
- per-line extras that change how a chat line looks, grouped so a new face only has to carry one field.
- Message
- one chat line.
heatis the channel’s heat snapshotted at arrival, so a message sent during a spike stays warm in scrollback.coloris the user’s chat color (hex) if the platform sent one. - Note
- a non-chat event shown inline in the chat flow.
whatis the human headline (“resubscribed for 14 months”, “raiding with 500 viewers”) — the message’suseris the actor and itstextcarries any attached user message (a resub message, a redemption input), which renders like chat. - Pin
- a channel’s pinned message — one line in a bar above chat.
- Tag
- a leading label on a line: where it came from, or what kind of row it is.
Enums§
- Badge
Kind - a user role badge, normalized across platforms. twitch sends
what a badge is, normalized across platforms. twitch sends
"broadcaster/1,moderator/1", kick sends[{name, version}]— both parse into this one vocabulary (the extension’s BADGE_SHORT set). a badge we don’t know keeps its first letter instead of vanishing. - Note
Kind - what kind of non-chat event a
Notemarks. one vocabulary across platforms — a kick gift bomb and a twitch submysterygift are bothGift. - Platform
- where a channel’s chat comes from.
- TabKind
- what a tab shows. special tabs are virtual — fed by the app, not by joining a channel — and pin left of the channel tabs in this order.
- TagKind
- Tint
- an announcement’s color, mapped onto the ansi palette: twitch’s PRIMARY is the plain one, ORANGE reads as yellow, PURPLE as magenta.
- Unread
- what a tab has waiting, for its color: nothing, chat, a stream event, or a ping.