From 11d3fb686aaca127573764a9ad21ed4d75b9f5d2 Mon Sep 17 00:00:00 2001 From: KhalilSelyan Date: Tue, 19 Mar 2024 12:05:03 +0300 Subject: [PATCH 1/3] Add @tanstack/react-virtual package --- package.json | 1 + pnpm-lock.yaml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/package.json b/package.json index a9f0c42..c26e11e 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@radix-ui/react-tooltip": "^1.0.6", "@tailwindcss/line-clamp": "^0.4.4", "@tanstack/react-table": "^8.9.3", + "@tanstack/react-virtual": "^3.1.3", "@tauri-apps/api": "2.0.0-beta.0", "@tauri-apps/plugin-app": "2.0.0-alpha.1", "@tauri-apps/plugin-autostart": "2.0.0-beta.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 148ea10..048c395 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -107,6 +107,9 @@ dependencies: '@tanstack/react-table': specifier: ^8.9.3 version: 8.9.3(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-virtual': + specifier: ^3.1.3 + version: 3.1.3(react-dom@18.2.0)(react@18.2.0) '@tauri-apps/api': specifier: 2.0.0-beta.0 version: 2.0.0-beta.0 @@ -2294,11 +2297,26 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@tanstack/react-virtual@3.1.3(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-YCzcbF/Ws/uZ0q3Z6fagH+JVhx4JLvbSflgldMgLsuvB8aXjZLLb3HvrEVxY480F9wFlBiXlvQxOyXb5ENPrNA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@tanstack/virtual-core': 3.1.3 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@tanstack/table-core@8.9.3: resolution: {integrity: sha512-NpHZBoHTfqyJk0m/s/+CSuAiwtebhYK90mDuf5eylTvgViNOujiaOaxNDxJkQQAsVvHWZftUGAx1EfO1rkKtLg==} engines: {node: '>=12'} dev: false + /@tanstack/virtual-core@3.1.3: + resolution: {integrity: sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g==} + dev: false + /@tauri-apps/api@2.0.0-alpha.6: resolution: {integrity: sha512-ZMOc3eu9amwvkC6M69h3hWt4/EsFaAXmtkiw4xd2LN59/lTb4ZQiVfq2QKlRcu1rj3n/Tcr7U30ZopvHwXBGIg==} engines: {node: '>= 14.6.0', npm: '>= 6.6.0', yarn: '>= 1.19.1'} From 4410231568dfd941614692ec46ff713579ac08fd Mon Sep 17 00:00:00 2001 From: KhalilSelyan Date: Tue, 19 Mar 2024 12:05:10 +0300 Subject: [PATCH 2/3] Fix file validation logic in Launch component --- src/components/tabComponents/Launch.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/tabComponents/Launch.tsx b/src/components/tabComponents/Launch.tsx index 78a4395..7cf9c11 100644 --- a/src/components/tabComponents/Launch.tsx +++ b/src/components/tabComponents/Launch.tsx @@ -472,13 +472,17 @@ const Launch = () => { } return; } else if (mapFileAttributesValuesFound.length === 1) { - if (!mapPathContentFiles.some((file) => file.endsWith(".pcd"))) { + if ( + !mapPathContentFiles.some((file) => file && file.endsWith(".pcd")) + ) { toast({ variant: "destructive", title: "Error", description: `The "pointcloud_map" file was not found in the map_path directory`, }); - } else if (!mapPathContentFiles.some((file) => file.endsWith(".osm"))) { + } else if ( + !mapPathContentFiles.some((file) => file && file.endsWith(".osm")) + ) { toast({ variant: "destructive", title: "Error", @@ -493,8 +497,10 @@ const Launch = () => { } } else { if ( - mapFileAttributesValues.some((file) => file.endsWith(".osm")) || - mapFileAttributesValues.some((file) => file.endsWith(".pcd")) + mapFileAttributesValues.some( + (file) => file && file.endsWith(".osm") + ) || + mapFileAttributesValues.some((file) => file && file.endsWith(".pcd")) ) { toast({ variant: "destructive", From 3afb54dad7ffc38c7b9b06a9dc51a63a49cc6c38 Mon Sep 17 00:00:00 2001 From: KhalilSelyan Date: Tue, 19 Mar 2024 12:05:51 +0300 Subject: [PATCH 3/3] refactor + use virtualizer for better performance and now can take up to 10k lines of logs before dropping 1k --- src/components/AutowareLaunchDialog.tsx | 172 ++++++++++++------------ 1 file changed, 89 insertions(+), 83 deletions(-) diff --git a/src/components/AutowareLaunchDialog.tsx b/src/components/AutowareLaunchDialog.tsx index 94f0fa2..1d5043b 100644 --- a/src/components/AutowareLaunchDialog.tsx +++ b/src/components/AutowareLaunchDialog.tsx @@ -1,10 +1,12 @@ "use client"; import { memo, useCallback, useEffect, useRef, useState } from "react"; +import { useVirtualizer } from "@tanstack/react-virtual"; import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; -import { useAtom } from "jotai"; +import { SetStateAction, useAtom } from "jotai"; +import { cn } from "@/lib/utils"; import { Dialog, DialogContent, @@ -36,6 +38,8 @@ import { } from "./ui/select"; import { Tabs, TabsList, TabsTrigger } from "./ui/tabs"; +type SetAtom = (...args: Args) => Result; + interface AutowareLaunchDialog extends React.HTMLAttributes {} const tabTitles = ["ALL", "INFO", "WARN", "ERROR", "DEBUG", "COMPONENT"]; @@ -77,15 +81,17 @@ export function AutowareLaunchDialog(props: AutowareLaunchDialog) { const logs = data.payload as string; // Helper function to handle log updates using Sets - const handleLogUpdate = (setLogState: any) => { - setLogState((prevArray: string[]) => { + const handleLogUpdate = ( + setLogState: SetAtom<[SetStateAction], void> + ) => { + setLogState((prevArray) => { // Deep clone the previous array const clonedPrevArray = JSON.parse(JSON.stringify(prevArray)); - const newSet = new Set(clonedPrevArray); + const newSet = new Set(clonedPrevArray); newSet.add(logs); - if (newSet.size > 100) { - return [...newSet].slice(-50); + if (newSet.size > 10000) { + return [...newSet].slice(-1000); } return [...newSet]; }); @@ -93,28 +99,28 @@ export function AutowareLaunchDialog(props: AutowareLaunchDialog) { // Check for "ERROR" logs if (logs.includes("ERROR")) { - console.log("WE HAVE AN ERROR"); + // console.log("WE HAVE AN ERROR"); handleLogUpdate(setLaunchLogsAll); handleLogUpdate(setLaunchLogsError); } // Check for "WARN" logs if (logs.includes("WARN")) { - console.log("WE HAVE A WARNING"); + // console.log("WE HAVE A WARNING"); handleLogUpdate(setLaunchLogsAll); handleLogUpdate(setLaunchLogsWarn); } // Check for "DEBUG" logs if (logs.includes("DEBUG")) { - console.log("WE HAVE A DEBUG LOG"); + // console.log("WE HAVE A DEBUG LOG"); handleLogUpdate(setLaunchLogsAll); handleLogUpdate(setLaunchLogsDebug); } // Check for "INFO" logs if (logs.includes("INFO")) { - console.log("WE HAVE AN INFO LOG"); + // console.log("WE HAVE AN INFO LOG"); handleLogUpdate(setLaunchLogsAll); handleLogUpdate(setLaunchLogsInfo); } @@ -130,8 +136,8 @@ export function AutowareLaunchDialog(props: AutowareLaunchDialog) { if (index !== -1) { const updatedLogs = new Set(newLogs[index].logs); updatedLogs.add(logs); - if (updatedLogs.size > 100) { - newLogs[index].logs = [...updatedLogs].slice(-50); + if (updatedLogs.size > 10000) { + newLogs[index].logs = [...updatedLogs].slice(-1000); } else { newLogs[index].logs = [...updatedLogs]; } @@ -177,6 +183,47 @@ export function AutowareLaunchDialog(props: AutowareLaunchDialog) { ); } + function getLogsForCurrentTab() { + switch (currentTab) { + case "all": + return filterLogs(launchLogsAll); + case "info": + return filterLogs(launchLogsInfo); + case "warn": + return filterLogs(launchLogsWarn); + case "error": + return filterLogs(launchLogsError); + case "debug": + return filterLogs(launchLogsDebug); + case "component": + const componentLogs = launchLogsComponent.find( + (logObj) => logObj.name === selectedPackage + ); + return componentLogs ? filterLogs(componentLogs.logs) : []; + default: + return []; + } + } + const logsToDisplay = getLogsForCurrentTab(); + useEffect(() => { + console.log(logsToDisplay.length); + }, [logsToDisplay.length]); + + const rowVirtualizer = useVirtualizer({ + count: logsToDisplay.length, + getScrollElement: () => logDivRef.current!, + estimateSize: useCallback(() => 30, []), + overscan: 5, + }); + + useEffect(() => { + // scroll to the bottom of the logs + logDivRef.current?.scrollTo({ + top: logDivRef.current?.scrollHeight, + behavior: "smooth", + }); + }, [logsToDisplay]); + return ( @@ -194,10 +241,7 @@ export function AutowareLaunchDialog(props: AutowareLaunchDialog) { Autoware Launch Logs -
+
setSearchQuery(e.target.value)} /> )} -
- {currentTab === "all" && - filterLogs(launchLogsAll).map((log, index) => ( -
+ {rowVirtualizer.getVirtualItems().map((log) => ( +
- {highlightSearchQuery(log, searchQuery)} -
- ))} - {currentTab === "info" && - filterLogs(launchLogsInfo).map((log, index) => ( -
- {highlightSearchQuery(log, searchQuery)} -
- ))} - {currentTab === "warn" && - filterLogs(launchLogsWarn).map((log, index) => ( -
- {highlightSearchQuery(log, searchQuery)} -
- ))} - {currentTab === "error" && - filterLogs(launchLogsError).map((log, index) => ( -
- {highlightSearchQuery(log, searchQuery)} -
- ))} - {currentTab === "debug" && - filterLogs(launchLogsDebug).map((log, index) => ( -
- {highlightSearchQuery(log, searchQuery)} -
- ))} - {currentTab === "component" && ( -
- {/* Display logs for the selected package */} - {launchLogsComponent - .filter((logObj) => logObj.name === selectedPackage) - .map((logObj, index) => ( -
- {logObj.logs.map((log, logIndex) => { - let logClass = ""; - if (log.includes("ERROR")) { - logClass = "text-red-500"; - } else if (log.includes("WARN")) { - logClass = "text-yellow-500"; - } else if (log.includes("DEBUG")) { - logClass = "text-emerald-500"; - } else if (log.includes("INFO")) { - logClass = ""; - } - - return ( -
- {log} -
- ); - })} -
- ))} + }` + )} + > + {/* Render your log entry here */} + {highlightSearchQuery(logsToDisplay[log.index], searchQuery)}
- )} + ))}