- TypingUsers state split by sender_type: AI vs human typing
- AI typing shows "{Name} is thinking..." with accent color
- Human typing shows "{Name} is typing..." with muted style
- AI typing relies on backend 60s TTL stop (no client-side 4s fallback)
- Add Page Visibility API to reconnect WS on tab become visible
- Add debug logs for typing flow tracing
- Pass sender_type through WS room.typing event routing
- MessageInput: ignore empty text in handleEditorUpdate to avoid
TipTap's onUpdate("") on init clearing the typing state
- DiscordChatPanel: show typing indicator when other users are typing
- room-context: wire onTypingStart/Stop into ws callbacks
- Remove duplicate smooth scroll effect from DiscordChatPanel; handle
all scroll logic in MessageList instead
- MessageList: track isInitialLoadRef to instant-jump to bottom on
first load (no animation), and only auto-scroll for new messages
when user is already near the bottom
- sender.ts: getSenderDisplayName rejects UUID values and falls back
to 'AI' for AI messages; getSenderModelId uses display_name
Frontend:
- Add Discord-style 3-column layout (server icons / channel sidebar / chat)
- AI Studio design system: new CSS token palette (--room-* vars)
- Replace all hardcoded Discord colors with CSS variable tokens
- Add RoomSettingsPanel (name, visibility, AI model management)
- Settings + Member list panels mutually exclusive (don't overlap)
- AI models shown at top of member list with green accent
- Fix TS errors: TipTap SuggestionOptions, unused imports, StarterKit options
- Remove MentionInput, MentionPopover, old room components (废弃代码清理)
Backend:
- RoomAiResponse returns model_name from agents.model JOIN
- room_ai_list and room_ai_upsert fetch model name for each config
- AiConfigData ws-protocol interface updated with model_name
Note: RoomSettingsPanel UI still uses shadcn defaults (未完全迁移到AI Studio)
Root cause: publish_reaction_event sends a RoomMessageEvent (not
reaction_added) with reactions field set. The onRoomMessage handler
previously returned prev immediately when a duplicate ID was found,
skipping the reaction update entirely.
Fix:
- Add reactions field to RoomMessagePayload so TypeScript knows it's there
- When a duplicate message ID is found AND payload carries reactions,
update the existing message's reactions field instead of ignoring
- ReactionItem and ReactionGroup have identical shapes, so assignment works