From 9766b5c41286026acc26fe5357150847a2693e02 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:00:13 -0600 Subject: [PATCH] readd copy handler --- web/src/pages/Logs.tsx | 104 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 6 deletions(-) diff --git a/web/src/pages/Logs.tsx b/web/src/pages/Logs.tsx index 80a0bcb5b..a7c137cea 100644 --- a/web/src/pages/Logs.tsx +++ b/web/src/pages/Logs.tsx @@ -19,6 +19,11 @@ import { LazyLog } from "@melloware/react-logviewer"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; import EnhancedScrollFollow from "@/components/dynamic/EnhancedScrollFollow"; import { MdCircle } from "react-icons/md"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; function Logs() { const [logService, setLogService] = useState("frigate"); @@ -93,11 +98,9 @@ function Logs() { const filterLines = useCallback( (lines: string[]) => { - // console.log(lines); if (!filterSeverity?.length) return lines; return lines.filter((line) => { - // console.log(line); const parsedLine = parseLogLines(logService, [line])[0]; return filterSeverity.includes(parsedLine.severity); }); @@ -266,6 +269,84 @@ function Logs() { [logService, setFilterSeverity, setSelectedLog], ); + useEffect(() => { + const handleCopy = (e: ClipboardEvent) => { + e.preventDefault(); + if (!lazyLogWrapperRef.current) return; + + const selection = window.getSelection(); + if (!selection) return; + + const range = selection.getRangeAt(0); + const fragment = range.cloneContents(); + + const extractLogData = (element: Element) => { + const severity = + element.querySelector(".log-severity")?.textContent?.trim() || ""; + const dateStamp = + element.querySelector(".log-timestamp")?.textContent?.trim() || ""; + const section = + element.querySelector(".log-section")?.textContent?.trim() || ""; + const content = + element.querySelector(".log-content")?.textContent?.trim() || ""; + + return { severity, dateStamp, section, content }; + }; + + let copyData: { + severity: string; + dateStamp: string; + section: string; + content: string; + }[] = []; + + if (fragment.querySelectorAll(".grid").length > 0) { + // Multiple grid elements + copyData = Array.from(fragment.querySelectorAll(".grid")).map( + extractLogData, + ); + } else { + // Try to find the closest grid element or use the first child element + const gridElement = + fragment.querySelector(".grid") || (fragment.firstChild as Element); + + if (gridElement) { + const data = extractLogData(gridElement); + if (data.severity || data.dateStamp || data.section || data.content) { + copyData.push(data); + } + } + } + + if (copyData.length === 0) return; // No valid data to copy + + // Calculate maximum widths for each column + const maxWidths = { + severity: Math.max(...copyData.map((d) => d.severity.length)), + dateStamp: Math.max(...copyData.map((d) => d.dateStamp.length)), + section: Math.max(...copyData.map((d) => d.section.length)), + }; + + const pad = (str: string, length: number) => str.padEnd(length, " "); + + // Create the formatted copy text + const copyText = copyData + .map( + (d) => + `${pad(d.severity, maxWidths.severity)} | ${pad(d.dateStamp, maxWidths.dateStamp)} | ${pad(d.section, maxWidths.section)} | ${d.content}`, + ) + .join("\n"); + + e.clipboardData?.setData("text/plain", copyText); + }; + + const content = lazyLogWrapperRef.current; + content?.addEventListener("copy", handleCopy); + return () => { + content?.removeEventListener("copy", handleCopy); + }; + }, []); + return (
@@ -347,13 +428,22 @@ function Logs() {
Message
{isStreaming && (
- + + + + + + Logs are streaming from the server + +
)}
@@ -411,7 +501,7 @@ function LogLineData({
-
+
{line.dateStamp}