atagen

malice

public
Star0
Watch0

Commit 787cdbbf0721

tarball

atagen <boss@atagen.co>2026-10-24 06:51 UTC

3 files changed+8-6
web: pill raw matrix ids and fall back to event sender avatars

Some clients send a bare `@user:server` in the plain body and only link the display name in formatted_body, so those pings rendered as plain text. Timeline avatars came only from the members roster, which left anyone missing from a stale roster without a picture even though every event carries its sender avatar.
Modified file: web/src/App.svelte+1 additions−1 deletions
@@ -1459,7 +1459,7 @@
14591459 itemId={g.key}
14601460 event={g.item.event}
14611461 showSender={g.showSender}
1462- senderAvatar={senderAvatarFor(g.item.event.sender.user_id)}
1462+ senderAvatar={senderAvatarFor(g.item.event.sender.user_id) ?? g.item.event.sender.avatar}
14631463 {reacting}
14641464 onReact={(eventId, key, shortcode) => void react(eventId, key, shortcode)}
14651465 quickReactions={$quickReactions}
Modified file: web/src/lib/MessageRow.svelte+5 additions−4 deletions
@@ -11,7 +11,7 @@
1111 import { senderLabel } from './types';
1212 import { formatClock, formatDay, initialsOf, senderHue } from './format';
1313 import { findUrls } from './urls';
14- import { mentionsFromHtml, stripMarkdownPills } from './mentions';
14+ import { mentionsFromHtml, resolveMentions, stripMarkdownPills } from './mentions';
1515
1616 export let itemId: string;
1717 export let event: TimelineEvent;
@@ -66,8 +66,9 @@
6666 $: interactive = event.content.kind === 'Message' || event.content.kind === 'Sticker' || event.content.kind === 'Poll';
6767 $: message = event.content.kind === 'Message' ? event.content.message : null;
6868 $: stripped = stripMarkdownPills(message ? (message.media ? message.media.caption : message.body) ?? '' : '');
69- $: mentions = message ? [...mentionsFromHtml(message.formatted_body), ...stripped.mentions] : [];
70- $: mentionsMe = self !== null && mentions.some((m) => m.user_id === self);
69+ $: linked = message ? [...mentionsFromHtml(message.formatted_body), ...stripped.mentions] : [];
70+ $: mentions = resolveMentions(stripped.text, linked);
71+ $: mentionsMe = self !== null && [...linked, ...mentions].some((m) => m.user_id === self);
7172 $: previews = findUrls(stripped.text);
7273 $: bundled = message ? message.link_previews : [];
7374 $: cards = previews
@@ -346,7 +347,7 @@
346347 {@const m = event.content.message}
347348 {#if stripped.text}
348349 <div class="text" class:emote={m.kind === 'Emote'} class:notice={m.kind === 'Notice' || m.kind === 'ServerNotice'}>
349- {#if m.kind === 'Emote'}* {displayName} {/if}<MessageText text={stripped.text} jumbo={m.kind === 'Text'} {mentions} {self} />
350+ {#if m.kind === 'Emote'}* {displayName} {/if}<MessageText text={stripped.text} jumbo={m.kind === 'Text'} {mentions} {self} nameOf={(id) => people[id]?.name ?? null} />
350351 {#if m.edited && !showSender}<span class="edited" title="Edited">(edited)</span>{/if}
351352 </div>
352353 {/if}
Modified file: web/src/lib/MessageText.svelte+2 additions−1 deletions
@@ -8,13 +8,14 @@
88 export let jumbo = false;
99 export let mentions: Mention[] = [];
1010 export let self: string | null = null;
11+ export let nameOf: (userId: string) => string | null = () => null;
1112
1213 $: segments = parseCode(text);
1314 $: pieces = segments.map((s) => (s.kind === 'text' ? segmentInline(s.text, mentions) : []));
1415 $: single = segments.length === 1 && pieces[0].length === 1;
1516 </script>
1617
17-{#each segments as s, i}{#if s.kind === 'inline'}<code class="inline">{s.text}</code>{:else if s.kind === 'block'}<pre class="block"><code>{s.text}</code></pre>{:else}{#each pieces[i] as p}{#if p.kind === 'mention'}<span class="mention" class:me={p.user_id === self} title={p.user_id}><EmojiText text={p.text} /></span>{:else if p.kind === 'link'}<a class="link" href={p.href} target="_blank" rel="noopener noreferrer">{p.text}</a>{:else}<EmojiText text={p.text} jumbo={jumbo && single} />{/if}{/each}{/if}{/each}
18+{#each segments as s, i}{#if s.kind === 'inline'}<code class="inline">{s.text}</code>{:else if s.kind === 'block'}<pre class="block"><code>{s.text}</code></pre>{:else}{#each pieces[i] as p}{#if p.kind === 'mention'}<span class="mention" class:me={p.user_id === self} title={p.user_id}><EmojiText text={p.text === p.user_id ? (nameOf(p.user_id) ?? p.text) : p.text} /></span>{:else if p.kind === 'link'}<a class="link" href={p.href} target="_blank" rel="noopener noreferrer">{p.text}</a>{:else}<EmojiText text={p.text} jumbo={jumbo && single} />{/if}{/each}{/if}{/each}
1819
1920 <style>
2021 code {