[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [16bf5ed](https://github.com/morethanwords/tweb/commit/16bf5ed154ba6af95809467c6c33844e6d0e5cf8) • _6 files, +244/-74_
Warn before opening a link that fakes a familiar domain

Auto-detected links were checked for a latin/non-latin mix only, so a
host built from two non-latin scripts opened without a word: `пօчта.рф`
is cyrillic with an armenian `օ` standing in for `о`. The same check
cried wolf on `例え.jp`, where the only latin in sight is the TLD.

Scripts are now resolved per label following UTS #39 — a label mixing
scripts is a spoof unless it is one of the japanese/korean/chinese
combinations, a letter from a script no registry sells is a spoof on its
own, and a cyrillic/greek/armenian label next to a latin one still is
one, which is what keeps `аррӏе.com` caught.

The check moved into safeWindowOpen as well, so the urls the app opens
without an anchor of its own — inline keyboard buttons, story links, web
app events — no longer skip it, and a host-shaped login such as
`почта.рф@evil.com` is now caught too. Hence the rename: it is no longer
only about scripts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[morethanwords/tweb/master](https://github.com/morethanwords/tweb/tree/master) • [b2df097](https://github.com/morethanwords/tweb/commit/b2df09771bef61b463d795621d242d4138ff59b6) • _4 files, +54/-10_
Recolor chat list spoilers when a row becomes active

The bluff spoiler a dialog preview draws over spoilered text picks its
particle color once, from `getComputedStyle(element).color`, and only
recomputes it on a resize, a theme change or a new chat background.
Opening a chat repaints the row's subtitle to white through CSS alone, so
the particles kept the grey of an inactive row — and stayed white once the
row lost the highlight.

The color is already known at that point: renderDialogSubtitleParts threads
`getTextColor(isActive)` into wrapRichText for the custom emoji sitting next
to the spoiler. Pass it down to the renderer too, resolve it through
customProperties, and recolor it from setDialogActiveStatus exactly where
`setTextColor` recolors those custom emoji. Sampling the computed color
stays as the fallback for callers with no property to name — a reply preview
inside a bubble takes its color from the peer, not from a fixed variable.

The color lives in a WeakMap rather than beside the update callback because
it has to outlive the render target: the element can be detached and
reconnected (BluffSpoilerController), and that re-attach must repaint with
the color the row has now, not with the one captured when it first rendered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

[#webk](?q=%23webk)