diff --git a/LOTUS_TODO.md b/LOTUS_TODO.md
index 024291013..1ff7a57d0 100644
--- a/LOTUS_TODO.md
+++ b/LOTUS_TODO.md
@@ -1008,20 +1008,24 @@ Themes:
---
-### [ ] P5-7 · In-App Notification Toast Redesign (TDS mode only)
+### [x] P5-7 · In-App Notification Toast Redesign (TDS mode only)
**What:** Slim dark card sliding in from bottom-right: user avatar, display name (orange), truncated message preview, room name (dim), × dismiss. 4-second auto-dismiss. TDS variables only — non-TDS keeps existing behavior.
**[AUDIT REQUIRED]** Find where in-app notification toasts are currently rendered in `src/app/`. May be in `ClientNonUIFeatures.tsx`.
**Complexity:** Medium.
+**COMPLETED June 2026.** `src/app/state/toast.ts` — `ToastNotif` type + `toastQueueAtom` (unbounded append) + `dismissToastAtom` (filter by id). `src/app/features/toast/LotusToastContainer.tsx` — fixed `position:fixed; bottom:1.5rem; right:1.5rem; z-index:9997` stack of toast cards. Each card: 24px circular avatar (img or initials fallback), display name in `--lt-accent-orange`, body text (80-char truncated), room name in `--lt-text-secondary`, × dismiss, 4 s `setTimeout` auto-dismiss. Slide-in via `@keyframes lotusToastIn` (translateX 120%→0, 0.2 s ease-out; opacity-only fade under `prefers-reduced-motion`). `` added in `App.tsx` as sibling to `` inside `JotaiProvider`. `InviteNotifications` and `MessageNotifications` in `ClientNonUIFeatures.tsx` call `setToast` and return early when `document.hasFocus()` — OS notification path unchanged when window is blurred. Message toast uses `mDirectAtom` to pick `getDirectRoomPath` vs `getHomeRoomPath` for correct `hashPath` navigation. Invite toast uses `hashPath: getInboxInvitesPath()`.
+
---
-### [ ] P5-8 · Mention Highlight Animation
+### [x] P5-8 · Mention Highlight Animation
**What:** Brief pulse/glow animation (0.4–0.6s ease-out) on incoming @mention messages. CSS keyframe: scale 1.0 → 1.005 → 1.0 + background glow pulse. Only fires on new incoming messages, not on page load. Respects `prefers-reduced-motion`.
**[AUDIT REQUIRED]** Find where mentioned messages receive their highlight class in the timeline. Verify animation doesn't affect scroll position.
**Complexity:** Low.
+**COMPLETED — already upstream (discovered June 2026).** Full audit confirmed: `MentionHighlightPulse` style in `src/app/components/message/layout/layout.css.ts:113-129` — 0.6 s `ease-out` `scale(1.003)` + `Warning.Main` box-shadow glow. Wired in `Message.tsx:975` via `isMentioned && isNewRef.current` — one-shot via `isNewRef` flipped in `onAnimationEnd`. Detects `m.mentions.user_ids` and `m.mentions.room`. Scoped to `(prefers-reduced-motion: no-preference)`. No implementation needed.
+
---
### [ ] P5-9 · LFG (Looking for Group) Slash Command
@@ -1094,12 +1098,14 @@ Themes:
---
-### [ ] P5-17 · Quick Emoji Reaction Bar (Hover Shortcut)
+### [x] P5-17 · Quick Emoji Reaction Bar (Hover Shortcut)
**What:** Floating mini-bar of 5 most recent reactions above the hover toolbar. One-click react. 6th button opens full emoji board.
**[AUDIT REQUIRED]** Find the message hover toolbar in `Message.tsx` and confirm how to inject an additional row without breaking layout. Confirm recent emoji tracking mechanism in EmojiBoard.
**Complexity:** Medium.
+**COMPLETED June 2026.** `MessageQuickReactions` component (already existed) moved from the 3-dots PopOut menu into the main hover toolbar in `Message.tsx`. Now renders directly on hover between the "Add Reaction" emoji-board button and the "Reply" button, guarded by `canSendReaction`. Limit increased from 4 to 5 recent emojis (`useRecentEmoji(mx, 5)`). The `` separator (appropriate in menu context only) removed from the component. `setEmojiBoardAnchor(undefined)` added to the toolbar callback so clicking a quick reaction also closes any open emoji picker. Removed from 3-dots menu entirely (was redundant). Emoji source: Matrix account data `ElementRecentEmoji`, sorted by usage count.
+
---
### [ ] P5-18 · Status-Based Avatar Border Color
diff --git a/README.md b/README.md
index e800fa0ab..b31892ab5 100644
--- a/README.md
+++ b/README.md
@@ -150,6 +150,8 @@ Emoji reaction buttons styled for terminal mode via `button[data-reaction-key]`
### UX & Composer
- **Message length counter**: A muted character counter appears just left of the send button while typing, disappearing when the composer is empty. Resets on room switch.
+- **Quick emoji reactions on hover**: The 5 most-recently-used emoji reactions appear directly in the message hover toolbar (between the emoji-board button and Reply), so reacting requires a single click rather than opening the 3-dots menu first. Clicking a quick-reaction also closes any open emoji picker. Powered by `useRecentEmoji` sourced from Matrix account data.
+- **In-app notification toasts**: When a message or invite notification fires and the browser window is focused, a slim TDS-styled toast card slides in from the bottom-right instead of triggering an OS notification. Card shows: 24px avatar (initials fallback), sender name in orange, truncated message body, room name, × dismiss, 4 s auto-dismiss. Clicking navigates directly to the correct room (DM or home path) or the invites inbox. OS notifications are unchanged when the window is not focused. Implemented in `src/app/features/toast/LotusToastContainer.tsx` + `src/app/state/toast.ts`.
- **Collapsible long messages**: Messages exceeding ~20 lines are auto-collapsed with a "Read more ↓" button. Click to expand inline; a "Collapse ↑" button re-folds. Threshold (in lines) configurable in Settings → Appearance. Uses CSS `max-height` + `overflow: hidden` — works correctly with code blocks and embedded media. Respects `prefers-reduced-motion`.
- **Message send animation**: Own sent messages fade and scale into the timeline (0.15 s ease-out: `scale(0.97)→scale(1)`, `opacity 0.4→1`). Incoming messages are unaffected. Respects `prefers-reduced-motion`.
- **Right-click room context menu**: Expanded sidebar room context menu — **Mute** now opens a duration submenu (15 min / 1 hr / 8 hr / 24 hr / Indefinite) with auto-restore after the selected window; **Copy Room Link** copies the `matrix.to` URL with a "Copied!" flash; **Mark as Read** marks the room read to the latest event; plus Leave Room and Room Settings shortcuts.
diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx
index 8649f0521..568c3a067 100644
--- a/src/app/features/room/message/Message.tsx
+++ b/src/app/features/room/message/Message.tsx
@@ -152,7 +152,7 @@ type MessageQuickReactionsProps = {
export const MessageQuickReactions = as<'div', MessageQuickReactionsProps>(
({ onReaction, ...props }, ref) => {
const mx = useMatrixClient();
- const recentEmojis = useRecentEmoji(mx, 4);
+ const recentEmojis = useRecentEmoji(mx, 5);
if (recentEmojis.length === 0) return ;
return (
@@ -180,7 +180,6 @@ export const MessageQuickReactions = as<'div', MessageQuickReactionsProps>(
))}
-
>
);
},
@@ -1035,6 +1034,14 @@ export const Message = React.memo(
)}
+ {canSendReaction && (
+ {
+ onReactionToggle(mEvent.getId()!, key, shortcode);
+ setEmojiBoardAnchor(undefined);
+ }}
+ />
+ )}