From 0aab2ad717f38671f60355c8d5d4b6ca22f717e9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 02:11:57 +0000 Subject: [PATCH 1/3] Add dump viewer search and larger panes Agent-Logs-Url: https://github.com/EssentialsX/website-next/sessions/cfedfe7a-7d08-4556-85d1-31435870cb26 Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- components/dump/dump-file-card.tsx | 5 +- components/dump/virtualized-text.tsx | 237 ++++++++++++++++++++++----- 2 files changed, 203 insertions(+), 39 deletions(-) diff --git a/components/dump/dump-file-card.tsx b/components/dump/dump-file-card.tsx index ed2f705..888f280 100644 --- a/components/dump/dump-file-card.tsx +++ b/components/dump/dump-file-card.tsx @@ -23,6 +23,7 @@ export default function DumpFileCard({ language, }: DumpFileCardProps) { const [sectionOpen, setSectionOpen] = useState(false); + const viewerHeight = 600; // Use virtualized text for large files that don't need syntax highlighting const shouldVirtualize = @@ -60,8 +61,8 @@ export default function DumpFileCard({ {shouldVirtualize ? - - : + + : } diff --git a/components/dump/virtualized-text.tsx b/components/dump/virtualized-text.tsx index 05ea38c..e7a3b80 100644 --- a/components/dump/virtualized-text.tsx +++ b/components/dump/virtualized-text.tsx @@ -1,7 +1,9 @@ 'use client'; +import { Kbd, Text, TextInput } from '@mantine/core'; +import { IconSearch } from '@tabler/icons-react'; import { useVirtualizer } from '@tanstack/react-virtual'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; interface VirtualizedTextProps { content: string; @@ -11,12 +13,35 @@ interface VirtualizedTextProps { export function VirtualizedText({ content, - maxHeight = 400, + maxHeight = 600, lineHeight = 18, }: VirtualizedTextProps) { + const containerRef = useRef(null); const parentRef = useRef(null); + const searchInputRef = useRef(null); + const [searchQuery, setSearchQuery] = useState(''); + const [activeMatchIndex, setActiveMatchIndex] = useState(0); + const [isHovered, setIsHovered] = useState(false); const lines = useMemo(() => content.split('\n'), [content]); + const normalizedSearchQuery = searchQuery.trim().toLowerCase(); + const matchingLineIndexes = useMemo(() => { + if (!normalizedSearchQuery) { + return []; + } + + return lines.reduce((indexes, line, index) => { + if (line.toLowerCase().includes(normalizedSearchQuery)) { + indexes.push(index); + } + + return indexes; + }, []); + }, [lines, normalizedSearchQuery]); + const activeLineIndex = + matchingLineIndexes.length > 0 ? + matchingLineIndexes[activeMatchIndex] ?? matchingLineIndexes[0] + : null; const virtualizer = useVirtualizer({ count: lines.length, @@ -25,48 +50,186 @@ export function VirtualizedText({ overscan: 10, }); + useEffect(() => { + setActiveMatchIndex(0); + }, [normalizedSearchQuery]); + + useEffect(() => { + if (activeLineIndex === null) { + return; + } + + virtualizer.scrollToIndex(activeLineIndex, { align: 'center' }); + }, [activeLineIndex, virtualizer]); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'f') { + return; + } + + const activeElement = document.activeElement; + const isViewerFocused = !!( + activeElement && containerRef.current?.contains(activeElement) + ); + + if (!isHovered && !isViewerFocused) { + return; + } + + event.preventDefault(); + searchInputRef.current?.focus(); + searchInputRef.current?.select(); + }; + + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [isHovered]); + + const goToMatch = (direction: 1 | -1) => { + if (matchingLineIndexes.length === 0) { + return; + } + + setActiveMatchIndex(currentIndex => { + const nextIndex = + (currentIndex + direction + matchingLineIndexes.length) % + matchingLineIndexes.length; + return nextIndex; + }); + }; + + const renderLine = (line: string, isActiveMatch: boolean) => { + if (!normalizedSearchQuery) { + return line; + } + + const renderedParts: React.ReactNode[] = []; + const lowerCaseLine = line.toLowerCase(); + let searchIndex = 0; + let key = 0; + + while (searchIndex < line.length) { + const matchIndex = lowerCaseLine.indexOf(normalizedSearchQuery, searchIndex); + + if (matchIndex === -1) { + renderedParts.push( + {line.slice(searchIndex)}, + ); + break; + } + + if (matchIndex > searchIndex) { + renderedParts.push( + {line.slice(searchIndex, matchIndex)}, + ); + } + + renderedParts.push( + + {line.slice(matchIndex, matchIndex + normalizedSearchQuery.length)} + , + ); + + searchIndex = matchIndex + normalizedSearchQuery.length; + } + + return renderedParts; + }; + return (
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} >
- {virtualizer.getVirtualItems().map(virtualItem => ( -
- {lines[virtualItem.index] || ''} -
- ))} + } + value={searchQuery} + onChange={event => setSearchQuery(event.currentTarget.value)} + onKeyDown={event => { + if (event.key === 'Enter') { + event.preventDefault(); + goToMatch(event.shiftKey ? -1 : 1); + } + }} + style={{ flex: 1 }} + /> + + {matchingLineIndexes.length > 0 ? + `${activeMatchIndex + 1} / ${matchingLineIndexes.length} matches` + : normalizedSearchQuery ? + 'No matches' + : Ctrl/Cmd + F} + +
+ +
+
+ {virtualizer.getVirtualItems().map(virtualItem => { + const isActiveMatch = activeLineIndex === virtualItem.index; + + return ( +
+ {renderLine(lines[virtualItem.index] || '', isActiveMatch)} +
+ ); + })} +
); From f17261021ed4803ee63415910298eb71f9245d4d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 02:12:49 +0000 Subject: [PATCH 2/3] Verify dump viewer issue fixes Agent-Logs-Url: https://github.com/EssentialsX/website-next/sessions/cfedfe7a-7d08-4556-85d1-31435870cb26 Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- components/dump/virtualized-text.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/components/dump/virtualized-text.tsx b/components/dump/virtualized-text.tsx index e7a3b80..e2c6f24 100644 --- a/components/dump/virtualized-text.tsx +++ b/components/dump/virtualized-text.tsx @@ -40,7 +40,7 @@ export function VirtualizedText({ }, [lines, normalizedSearchQuery]); const activeLineIndex = matchingLineIndexes.length > 0 ? - matchingLineIndexes[activeMatchIndex] ?? matchingLineIndexes[0] + (matchingLineIndexes[activeMatchIndex] ?? matchingLineIndexes[0]) : null; const virtualizer = useVirtualizer({ @@ -64,7 +64,10 @@ export function VirtualizedText({ useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { - if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== 'f') { + if ( + !(event.ctrlKey || event.metaKey) || + event.key.toLowerCase() !== 'f' + ) { return; } @@ -110,7 +113,10 @@ export function VirtualizedText({ let key = 0; while (searchIndex < line.length) { - const matchIndex = lowerCaseLine.indexOf(normalizedSearchQuery, searchIndex); + const matchIndex = lowerCaseLine.indexOf( + normalizedSearchQuery, + searchIndex, + ); if (matchIndex === -1) { renderedParts.push( @@ -121,7 +127,9 @@ export function VirtualizedText({ if (matchIndex > searchIndex) { renderedParts.push( - {line.slice(searchIndex, matchIndex)}, + + {line.slice(searchIndex, matchIndex)} + , ); } From baa3f8d832e04804f442a5fd206dc2f0ccd2e372 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 May 2026 02:13:44 +0000 Subject: [PATCH 3/3] Polish dump viewer shortcut hint Agent-Logs-Url: https://github.com/EssentialsX/website-next/sessions/cfedfe7a-7d08-4556-85d1-31435870cb26 Co-authored-by: JRoy <10731363+JRoy@users.noreply.github.com> --- components/dump/virtualized-text.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/components/dump/virtualized-text.tsx b/components/dump/virtualized-text.tsx index e2c6f24..54e605d 100644 --- a/components/dump/virtualized-text.tsx +++ b/components/dump/virtualized-text.tsx @@ -22,6 +22,9 @@ export function VirtualizedText({ const [searchQuery, setSearchQuery] = useState(''); const [activeMatchIndex, setActiveMatchIndex] = useState(0); const [isHovered, setIsHovered] = useState(false); + const [shortcutModifier, setShortcutModifier] = useState<'Cmd' | 'Ctrl'>( + 'Ctrl', + ); const lines = useMemo(() => content.split('\n'), [content]); const normalizedSearchQuery = searchQuery.trim().toLowerCase(); @@ -54,6 +57,13 @@ export function VirtualizedText({ setActiveMatchIndex(0); }, [normalizedSearchQuery]); + useEffect(() => { + const platform = `${window.navigator.platform} ${window.navigator.userAgent}`; + if (/Mac|iPhone|iPad|iPod/i.test(platform)) { + setShortcutModifier('Cmd'); + } + }, []); + useEffect(() => { if (activeLineIndex === null) { return; @@ -95,10 +105,10 @@ export function VirtualizedText({ } setActiveMatchIndex(currentIndex => { - const nextIndex = + return ( (currentIndex + direction + matchingLineIndexes.length) % - matchingLineIndexes.length; - return nextIndex; + matchingLineIndexes.length + ); }); }; @@ -185,7 +195,7 @@ export function VirtualizedText({ `${activeMatchIndex + 1} / ${matchingLineIndexes.length} matches` : normalizedSearchQuery ? 'No matches' - : Ctrl/Cmd + F} + : {shortcutModifier} + F}