- RoomRow: ⋮ button (hover) for pin/unpin context menu
- MessageBubble: right-click context menu with emoji reactions + edit/delete
- Typing indicator: debounced setTyping, animated TypingDots, polling getTyping every 2s
- Online presence: heartbeat setPresence every 30s, green dot on avatars/header
- Read receipts: ✓/✓✓ for own messages in direct rooms via otherUserLastRead
- Migration 075: chat_presence + chat_typing tables
- Context menus clamped to viewport bounds
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace hover toolbar with right-click context menu (no overflow issues)
Messages: emoji reactions row + Edit + Delete (own only)
Rooms: Pin / Unpin — pinned rooms always appear at top with 📌 icon
- Context menu clamped to viewport bounds, closes on any outside click
- Sound notifications: Web Audio API beep (880Hz, 0.25s) on new messages
and on new unreads when widget is closed; respects soundEnabled setting
- Settings: added px-4 to sections so toggle is never clipped
Added hint text about right-click shortcuts
- Pin state stored in localStorage (hotelsync-chat-pins)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration 073: attachment_url column in chat_messages
- upload.ts: add 'chat' folder to ALLOWED_FOLDERS
- chat.ts: accept attachment_url in send message, return it in GET messages
- api.ts: attachmentUrl in ChatMessage type, uploadImage() with raw FormData fetch
- ChatWidget: paperclip button, file preview thumbnail with remove, image display
inline in message bubbles (clickable, opens original), send enabled with image only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration 072: widen type column to VARCHAR(20), add partial unique index for general rooms
- Backend: replace brittle ON CONFLICT with explicit SELECT-then-INSERT ensureRoom helper
- Frontend: show room skeletons (Общий чат + Уведомления) during load instead of spinner,
hide '0 чатов' subtitle when list is empty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>