diff --git a/frontend/components/chat/ChatWindow.jsx b/frontend/components/chat/ChatWindow.jsx index c6c275b..3d79993 100644 --- a/frontend/components/chat/ChatWindow.jsx +++ b/frontend/components/chat/ChatWindow.jsx @@ -27,6 +27,32 @@ import { getStatusMoodLabel } from "../../src/lib/statusMoods" const formatRecordingTime = (s) => `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, "0")}` +/** + * Highlights occurrences of `query` inside `text` by wrapping them in spans. + * Used to visually emphasize matched text in search results (Fix #569). + * @param {string} text - The full message text + * @param {string} query - The search query string + * @returns {JSX.Element} - Span with highlighted matches + */ +const highlightText = (text, query) => { + if (!query.trim() || !text) return {text}; + const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const parts = text.split(new RegExp(`(${escaped})`, "gi")); + return ( + + {parts.map((part, i) => + part.toLowerCase() === query.toLowerCase() ? ( + + {part} + + ) : ( + {part} + ) + )} + + ); +}; + const formatLastSeen = (dateString) => { if (!dateString) return "Offline"; const date = new Date(dateString); @@ -120,6 +146,33 @@ export default function ChatWindow({ selectedUser, onBack, isMobileHidden }) { setRecentSearches(savedSearches); }, []); + /** + * FIX (#569): Keyboard shortcut to open the in-chat search bar. + * Ctrl+F (Windows/Linux) and Cmd+F (Mac) are intercepted when a chat + * is active, preventing the browser's native find dialog from opening + * and instead focusing the in-chat search field. + */ + useEffect(() => { + const handleKeyDown = (e) => { + if ((e.ctrlKey || e.metaKey) && e.key === "f" && selectedUser) { + e.preventDefault(); + setSearchOpen(true); + // Short timeout to allow the search input to render before focusing + setTimeout(() => { + const input = document.querySelector(".chat-search-input"); + if (input) input.focus(); + }, 50); + } + if (e.key === "Escape" && searchOpen) { + setSearchOpen(false); + setSearchResults([]); + setSearchQuery(""); + } + }; + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [selectedUser, searchOpen]); + const scrollToMessage = (msgId) => { const msgElement = document.getElementById(`msg-${msgId}`); if (msgElement) { @@ -558,9 +611,14 @@ const mediaMessages = messages.filter( {searchResults.length > 0 && ( -
-

{searchResults.length} results found:

+
+

+ {searchResults.length} message{searchResults.length !== 1 ? "s" : ""} found — click to jump +

{searchResults.map((res) => ( ))}