atagen

malice

public
Star0
Watch0

Commit 8c81366a752f

tarball

atagen <boss@atagen.co>2026-10-26 04:43 UTC

4 files changed+10-13
web: let quick reactions grow past eight and wrap the toolbar row

The eight-reaction cap was enforced on load, save and in the picker, so extra choices were silently dropped. The hover toolbar now lays quick reactions out eight per row and wraps.
Modified file: web/src/lib/MessageRow.svelte+6 additions−2 deletions
@@ -14,7 +14,7 @@
1414 import { mediaUrl, messageMediaUrl } from './api';
1515 import { keywordMatcher } from './keywords';
1616 import type { ImagePack, LinkPreview, Person, QuickReaction, ReplyReference, SendState, TimelineContent, TimelineEvent } from './types';
17- import { senderLabel } from './types';
17+ import { QUICK_REACTIONS_PER_ROW, senderLabel } from './types';
1818 import { formatClock, formatDay, initialsOf, senderHue } from './format';
1919 import { findUrls } from './urls';
2020 import { mentionsFromHtml, resolveMentions, stripMarkdownPills } from './mentions';
@@ -368,6 +368,7 @@
368368 >
369369 {#if showActions && event.event_id}
370370 <div class="actions" role="toolbar" aria-label={`Actions for message from ${displayName}`}>
371+ <div class="quick" style={`--quick-cols:${Math.min(quickReactions.length, QUICK_REACTIONS_PER_ROW)}`}>
371372 {#each quickReactions as r (r.key)}
372373 {@const label = r.shortcode ? `:${r.shortcode}:` : r.key}
373374 <button
@@ -384,6 +385,7 @@
384385 {/if}
385386 </button>
386387 {/each}
388+ </div>
387389 <button
388390 type="button"
389391 title="Add reaction"
@@ -717,13 +719,15 @@
717719 .readby .more { margin-left: 3px; font-size: 10.5px; color: var(--muted); }
718720 .actions {
719721 position: absolute; top: -12px; right: 10px; z-index: 2;
720- display: flex; gap: 2px;
722+ display: flex; align-items: flex-start; gap: 2px;
721723 padding: 2px;
722724 background: var(--bg-soft);
723725 border: 1px solid var(--border);
724726 border-radius: var(--r-md);
725727 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
726728 }
729+ .quick { display: grid; grid-template-columns: repeat(var(--quick-cols, 8), auto); gap: 2px; }
730+ .quick:empty { display: none; }
727731 .actions button {
728732 background: transparent;
729733 border: 1px solid transparent;
Modified file: web/src/lib/ReactionPicker.svelte+1 additions−6 deletions
@@ -5,7 +5,6 @@
55 import Emoji from './Emoji.svelte';
66 import { loadEmoji, searchEmoji } from './emoji';
77 import type { EmojiGroup } from './emoji';
8- import { MAX_QUICK_REACTIONS } from './types';
98 import type { ImagePack, PackImage, QuickReaction } from './types';
109 import { hasUsage } from './packs';
1110
@@ -36,7 +35,6 @@
3635 .map((pack) => ({ pack, images: pack.images.filter((i) => imageMatches(i, q)) }))
3736 .filter((p) => p.images.length > 0);
3837 $: filteredGroups = searchEmoji(groups ?? [], q);
39- $: full = quick.length >= MAX_QUICK_REACTIONS;
4038 $: nothingFound = q !== '' && groups !== null && filteredPacks.length === 0 && filteredGroups.length === 0;
4139
4240 function imageMatches(image: PackImage, needle: string): boolean {
@@ -59,7 +57,7 @@
5957 return;
6058 }
6159 if (isQuick(key)) onQuickChange(quick.filter((r) => r.key !== key));
62- else if (!full) onQuickChange([...quick, { key, shortcode }]);
60+ else onQuickChange([...quick, { key, shortcode }]);
6361 }
6462
6563 function quickLabel(r: QuickReaction): string {
@@ -102,7 +100,6 @@
102100 <div class="quick" role="group" aria-label="Quick reactions">
103101 <div class="quick-head">
104102 <span>Quick reactions</span>
105- <span class="muted">{quick.length}/{MAX_QUICK_REACTIONS}</span>
106103 </div>
107104 <div class="quick-row">
108105 {#each quick as r (r.key)}
@@ -141,7 +138,6 @@
141138 title={`:${image.shortcode}:`}
142139 aria-label={`:${image.shortcode}:`}
143140 aria-pressed={customising ? isQuick(image.reaction_key) : undefined}
144- disabled={customising && full && !isQuick(image.reaction_key)}
145141 on:click={() => choose(image.reaction_key, image.shortcode)}
146142 >
147143 <img src={messageMediaUrl(image.media_id, 'Thumbnail')} alt="" loading="lazy" />
@@ -168,7 +164,6 @@
168164 title={e.label}
169165 aria-label={e.label}
170166 aria-pressed={customising ? isQuick(e.emoji) : undefined}
171- disabled={customising && full && !isQuick(e.emoji)}
172167 on:click={() => choose(e.emoji, e.shortcodes[0] ?? null)}
173168 >
174169 <Emoji emoji={e.emoji} size={24} lazy />
Modified file: web/src/lib/quick-reactions.ts+2 additions−4 deletions
@@ -1,6 +1,5 @@
11 import { get, writable } from 'svelte/store';
22 import { getAccountData, setAccountData } from './api';
3-import { MAX_QUICK_REACTIONS } from './types';
43 import type { QuickReaction } from './types';
54
65 const EVENT_TYPE = 'dev.malice.quick_reactions';
@@ -29,7 +28,7 @@ function parse(raw: unknown): QuickReaction[] | null {
2928 seen.add(key);
3029 out.push({ key, shortcode: typeof shortcode === 'string' ? shortcode : null });
3130 }
32- return out.slice(0, MAX_QUICK_REACTIONS);
31+ return out;
3332 }
3433
3534 export async function loadQuickReactions(): Promise<void> {
@@ -40,8 +39,7 @@ export async function loadQuickReactions(): Promise<void> {
4039 }
4140 }
4241
43-export async function saveQuickReactions(list: QuickReaction[]): Promise<void> {
44- const reactions = list.slice(0, MAX_QUICK_REACTIONS);
42+export async function saveQuickReactions(reactions: QuickReaction[]): Promise<void> {
4543 const previous = get(quickReactions);
4644 quickReactions.set(reactions);
4745 try {
Modified file: web/src/lib/types.ts+1 additions−1 deletions
@@ -931,7 +931,7 @@ export function parseImagePacks(raw: unknown): ImagePacks {
931931
932932 export { packsForRoom } from './packs';
933933
934-export const MAX_QUICK_REACTIONS = 8;
934+export const QUICK_REACTIONS_PER_ROW = 8;
935935 export interface QuickReaction {
936936 key: string;
937937 shortcode: string | null;