Skip to main content

Crate heatsync_core

Crate heatsync_core 

Source
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§

block
per-emote blocks: hide one exact emote, never a name.
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. same order as the extension: a channel’s set is ffz > 7tv > bttv (the server’s /api/channel/:c/emotes list puts the winner LAST, so from_list is last-wins), globals are 7tv > bttv > ffz > twitch (from_global_list ranks them), and a twitch/kick token always beats a same-name set emote. message text is word-matched against the result. modifiers (BTTV w!-style prefixes / FFZ ffzX-style effect words) are parsed here into per-layer Effect lists; 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 Look to its toolkit.
hsbadge
the heatsync badge catalogue: the one mark a chatter picked to show.
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 /ws relay 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 are seq - 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.js sessionWentLiveSeen, the 60s text dedupe). pure: the clock is passed in as now_ms, nothing here sleeps or reads a timer. feed it every parsed ChatLine; 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. heat is the channel’s heat snapshotted at arrival, so a message sent during a spike stays warm in scrollback. color is the user’s chat color (hex) if the platform sent one.
Note
a non-chat event shown inline in the chat flow. what is the human headline (“resubscribed for 14 months”, “raiding with 500 viewers”) — the message’s user is the actor and its text carries 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§

BadgeKind
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.
NoteKind
what kind of non-chat event a Note marks. one vocabulary across platforms — a kick gift bomb and a twitch submysterygift are both Gift.
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.