diff --git a/packages/frontend/src/LogEntry.tsx b/packages/frontend/src/LogEntry.tsx index 5b69d07..1129702 100644 --- a/packages/frontend/src/LogEntry.tsx +++ b/packages/frontend/src/LogEntry.tsx @@ -88,6 +88,8 @@ export function LogEntry({ .map((part: anser.AnserJsonEntry, index: number) => ( {new Date(log.timestamp).toISOString().split("T")[1]} -
+
{renderLogMessage(log.message)}
(null); + const [openDropdownIndex, setOpenDropdownIndex] = useState( + null + ); + const listRef = useRef(null); + const [listHeight, setListHeight] = useState(0); + const containerRef = useRef(null); + + useEffect(() => { + const updateHeight = () => { + if (containerRef.current) { + const { top } = containerRef.current.getBoundingClientRect(); + setListHeight(window.innerHeight - top); + } + }; + + updateHeight(); + window.addEventListener("resize", updateHeight); + return () => window.removeEventListener("resize", updateHeight); + }, []); + + const getItemSize = (index: number) => { + const lineCount = logs[index].message.split("\n").length; + return lineCount * LINE_HEIGHT; + }; + + useHotkeys( + "j,k", + ({ key }) => { + setSelectedLogIndex((prevIndex) => { + if (prevIndex === null) return 0; + const newIndex = + key === "j" || key === "ArrowDown" + ? Math.min(prevIndex + 1, logs.length - 1) + : Math.max(prevIndex - 1, 0); + listRef.current?.scrollToItem(newIndex, "smart"); + return newIndex; + }); + }, + { + enabled: openDropdownIndex === null, + } + ); + + const Row = ({ + index, + style, + }: { + index: number; + style: React.CSSProperties; + }) => { + const log = logs[index]; + const isSelected = index === selectedLogIndex; + const isDropdownOpen = index === openDropdownIndex; + + return ( +
+ setSelectedLogIndex(index)} + onDropdownOpenChange={(isOpen) => { + if (isOpen) { + setOpenDropdownIndex(index); + } else { + setOpenDropdownIndex(null); + } + }} + /> +
+ ); + }; + + return ( +
+ {logs.length > 0 ? ( + + {Row} + + ) : ( + + Waiting for logs + + + )} +
+ ); +}