From ddeb72101e392ae580e6a2a1fea32878a75a360d Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 2 Mar 2026 02:09:52 +0100 Subject: [PATCH] feat: dynamic PiP collision avoidance, reaction pill redesign, channel dedup fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PiP positioning: - Replace hardcoded layout constants with dynamic DOM measurement system - Obstacle elements declare themselves via data-pip-obstacle="left|bottom" - getPipBounds() queries actual element rects at runtime via getBoundingClientRect - MutationObserver + ResizeObserver re-clamp PiP when obstacles appear/resize - PiP reappears after close when navigating away from voice channel Reactions: - Align reaction pills with Aether Drift prototype - Own-reaction accent: purple → mint (bg, border, count color) - Default pills: subtle white overlay bg + border-soft border - Count text: 12px/semibold/txt-secondary per prototype spec Bug fix: - Deduplicate channel insertion in createChannel store action - Prevents double-add race between REST response and WS broadcast --- packages/web/src/components/chat/Message.tsx | 8 +- .../web/src/components/chat/MessageInput.tsx | 16 ++- .../src/components/layout/ChannelSidebar.tsx | 2 +- .../src/components/layout/ServerSidebar.tsx | 2 +- .../src/components/voice/PictureInPicture.tsx | 118 +++++++++++++++--- packages/web/src/stores/serverStore.ts | 7 +- 6 files changed, 124 insertions(+), 29 deletions(-) diff --git a/packages/web/src/components/chat/Message.tsx b/packages/web/src/components/chat/Message.tsx index 70ab0811..d34fe8e4 100644 --- a/packages/web/src/components/chat/Message.tsx +++ b/packages/web/src/components/chat/Message.tsx @@ -248,14 +248,14 @@ export function Message({ message, isCompact, isFirstInGroup }: MessageProps) { ))} diff --git a/packages/web/src/components/chat/MessageInput.tsx b/packages/web/src/components/chat/MessageInput.tsx index 77fcf459..a34d5b8d 100644 --- a/packages/web/src/components/chat/MessageInput.tsx +++ b/packages/web/src/components/chat/MessageInput.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef, useCallback, useMemo } from 'react'; +import React, { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { useChatStore } from '../../stores/chatStore'; import { isDmChannel, useServerStore } from '../../stores/serverStore'; import { wsSend } from '../../hooks/useWebSocket'; @@ -30,6 +30,18 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { const members = useServerStore((s) => s.members); const typingTimeoutRef = useRef>(); + // Auto-focus textarea on channel navigation + useEffect(() => { + textareaRef.current?.focus(); + }, [channelId]); + + // Auto-focus textarea when replying + useEffect(() => { + if (replyTo) { + textareaRef.current?.focus(); + } + }, [replyTo]); + // Filter members for the mention popover const filteredMembers = useMemo(() => { if (!mentionState) return []; @@ -216,7 +228,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) { }; return ( -
+
{replyTo && (
diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index 029f75c7..bc80adc5 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -97,7 +97,7 @@ export function ChannelSidebar() { // Floating bottom panel — shared between DM view and server view const floatingPanel = user ? ( -
+
{/* Voice controls (expands when connected) */} {(currentVoiceChannelId || activeDmCall) && } diff --git a/packages/web/src/components/layout/ServerSidebar.tsx b/packages/web/src/components/layout/ServerSidebar.tsx index 36ea4a62..1a8ad02d 100644 --- a/packages/web/src/components/layout/ServerSidebar.tsx +++ b/packages/web/src/components/layout/ServerSidebar.tsx @@ -157,7 +157,7 @@ export function ServerSidebar() { }; return ( -