Skip to content

feat: Implemented log search functionality with highlighted results #647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

harshit078
Copy link

@harshit078 harshit078 commented Apr 21, 2025

Linked Issue(s)

#556

Acceptance Criteria fulfillment

  • Added Search in Logs
  • Improved Search UI and functionality

Proposed changes

  • Added Search functionality in logs
  • Added highlighted logs with ease to scroll through the list of selected logs
Screen.Recording.2025-04-21.at.4.27.38.PM.mov
Screenshot 2025-04-21 at 4 27 24 PM

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added a search bar for system logs with dynamic text highlighting of matching terms in log entries.
    • Introduced navigation controls to cycle through search results, showing current and total matches.
    • Log entries now highlight all occurrences of the search query for better readability.
  • Improvements

    • Improved scrolling behavior with smooth scroll-to-bottom functionality.
    • Enhanced highlight visuals for active search matches within logs.
    • Unified search state management and improved performance with memoized components and refined event handling.
    • Updated log components to support search query highlighting and current match indication.
    • Refined search input with debounced updates and clear functionality for better user experience.

Copy link

coderabbitai bot commented Apr 21, 2025

Walkthrough

The changes introduce a comprehensive search and navigation feature to the system logs interface. A new LogSearch component provides a search input with navigation controls, allowing users to find and cycle through matching log entries. Both PlainLog and FormattedLog components are updated to highlight text matching the search query. The main SystemLogs component is enhanced with state and logic to manage search queries, highlight matches, and handle smooth scrolling to matched entries. Scroll detection is refactored from using an IntersectionObserver to a scroll event listener and a smooth scroll-to-bottom function, simplifying the logic for displaying the scroll down button. Additionally, the useSystemLogs hook now requires a serviceName parameter.

Changes

File(s) Change Summary
web-server/src/components/Service/SystemLog/FormattedLog.tsx
web-server/src/components/Service/SystemLog/PlainLog.tsx
Added a SearchHighlightText helper component to highlight search matches; updated main components to accept optional searchQuery and isCurrentMatch props and render highlighted text accordingly.
web-server/src/components/Service/SystemLog/LogSearch.tsx Introduced a new LogSearch component with search input, debounced query updates, navigation controls for previous/next matches, and callbacks for searching and navigating between results.
web-server/src/content/Service/SystemLogs.tsx Enhanced the SystemLogs component with unified search state management via useReducer, integrated LogSearch, added debounced search matching and highlight tracking, replaced IntersectionObserver with scroll event listener and smooth scroll-to-bottom function, updated scroll button logic, and memoized log rendering with search highlighting and current match indication. Removed early return for missing serviceName and wrapped component with memo.
web-server/src/hooks/useSystemLogs.tsx Updated useSystemLogs hook to require the serviceName parameter instead of making it optional.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LogSearch
    participant SystemLogs
    participant LogEntry (PlainLog/FormattedLog)
    
    User->>LogSearch: Types search query / uses navigation controls
    LogSearch->>SystemLogs: onSearch(query) / onNavigate(direction)
    SystemLogs->>SystemLogs: Update search state, find matches, update highlights
    SystemLogs->>LogEntry: Pass searchQuery and isCurrentMatch props
    LogEntry->>LogEntry: Highlight matching text
    SystemLogs->>SystemLogs: Scroll to highlighted match (if navigating)
Loading

Possibly related PRs

  • feat(system_logs): Fixed auto scroll #630: Refactors and replaces the IntersectionObserver-based scroll detection and scroll down button logic in SystemLogs.tsx, directly modifying the same scrolling behavior and UI elements as this PR.

Poem

In the warren of logs where the data flows,
A bunny now searches, and every match glows.
With highlights of yellow, and scrolls oh-so-smooth,
Hopping through entries, each click on the move.
Search, skip, and find—what a clever new trick!
Logs now respond, and the answers come quick.
🐇✨

[!ANNOUNCEMENT]

⚡️ AI Code Reviews for VS Code, Cursor, Windsurf

CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
Learn more here.


[!ANNOUNCEMENT]

⚡️ Faster reviews with caching

CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.
Enjoy the performance boost—your workflow just got faster.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (1)
web-server/src/components/Service/SystemLog/FormattedLog.tsx (1)

13-30: 🛠️ Refactor suggestion

Factor‑out HighlightedText to avoid code duplication

See earlier comment in PlainLog.tsx.

🧹 Nitpick comments (4)
web-server/src/components/Service/SystemLog/PlainLog.tsx (1)

3-7: Unused prop: index

index is accepted in PlainLogProps but never used in the component. Either remove it from the prop list or use it (e.g. as key when mapping). Keeping unused props increases cognitive load.

web-server/src/components/Service/SystemLog/LogSearch.tsx (2)

7-16: gap has no effect without display:flex (or grid)

SearchContainer sets gap: 5 but the default display is block, so the property is ignored. Add display: flex (or grid) to get the intended spacing:

-const SearchContainer = styled('div')(() => ({
+const SearchContainer = styled('div')(() => ({
   position: 'sticky',
   top: 0,
   zIndex: 1,
   gap: 5,
+  display: 'flex',
   paddingBottom: 8,
   alignItems: 'center',
   backdropFilter: 'blur(10px)',
   borderRadius: 5,
 }));

64-71: Prevent the default “Enter” behaviour in the text field

Pressing in a single‑line TextField can submit the surrounding form (if any) or trigger an unintended click on the first <button> in the DOM. Add event.preventDefault() after handling navigation.

-      if (event.key === 'Enter' && event.shiftKey) {
+      if (event.key === 'Enter' && event.shiftKey) {
+        event.preventDefault();
         onNavigate('prev');
       } else if (event.key === 'Enter') {
+        event.preventDefault();
         onNavigate('next');
       }
web-server/src/content/Service/SystemLogs.tsx (1)

75-78: Use a data‑attribute instead of inline style to mark matches

Selecting by span[style*="background-color: yellow"] can yield false positives and is brittle if the style changes. When rendering highlights, add data-log-match and query via [data-log-match] to decouple logic from styling.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a545c0 and 3a3abb0.

📒 Files selected for processing (4)
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx (2 hunks)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx (1 hunks)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
web-server/src/components/Service/SystemLog/FormattedLog.tsx (1)
web-server/src/types/resources.ts (1)
  • ParsedLog (1056-1062)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

75-87: ⚠️ Potential issue

Critical issue: useEffect inside a callback function

React hooks can only be called at the top level of your component or other hooks, not inside regular JavaScript functions, conditions, or loops. The useEffect hook inside your handleSearch callback will cause runtime errors.

Move this effect to the component's top level:

- const handleSearch = useCallback((query: string) => {
-   setSearchQuery(query);
-   setCurrentMatch(0);
-   setTotalMatches(0);
-   setHighlightedElements([]);
-   updateHighlight(null);
-
-   if (!query) return;
-
-   useEffect(() => {
-     const elements = Array.from(
-       containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
-     ) as HTMLElement[];
-     setHighlightedElements(elements);
-     setTotalMatches(elements.length);
-     setCurrentMatch(elements.length ? 1 : 0);
-
-     if (elements.length) {
-       updateHighlight(elements[0]);
-       elements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
-     }
-   }, [searchQuery, logs, updateHighlight]);
-
- }, []);
+ const handleSearch = useCallback((query: string) => {
+   setSearchQuery(query);
+   setCurrentMatch(0);
+   setTotalMatches(0);
+   setHighlightedElements([]);
+   updateHighlight(null);
+ }, [updateHighlight]);
+
+ useEffect(() => {
+   if (!searchQuery) return;
+   
+   const elements = Array.from(
+     containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
+   ) as HTMLElement[];
+   setHighlightedElements(elements);
+   setTotalMatches(elements.length);
+   setCurrentMatch(elements.length ? 1 : 0);
+
+   if (elements.length) {
+     updateHighlight(elements[0]);
+     elements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
+   }
+ }, [searchQuery, logs, updateHighlight]);
🧹 Nitpick comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

51-64: Consider using CSS classes for highlights instead of inline styles

Using inline styles for highlights works, but it can be harder to maintain. Consider using CSS classes with defined styles in your stylesheet.

- if (currentHighlightRef.current) {
-   currentHighlightRef.current.style.backgroundColor = 'yellow';
-   currentHighlightRef.current.style.color = 'black';
- }
-
- if (element) {
-   element.style.backgroundColor = 'rgb(255, 161, 10)';
-   element.style.color = 'white';
-   currentHighlightRef.current = element;
- } else {
-   currentHighlightRef.current = null;
- }
+ if (currentHighlightRef.current) {
+   currentHighlightRef.current.classList.remove('active-highlight');
+   currentHighlightRef.current.classList.add('search-highlight');
+ }
+
+ if (element) {
+   element.classList.remove('search-highlight');
+   element.classList.add('active-highlight');
+   currentHighlightRef.current = element;
+ } else {
+   currentHighlightRef.current = null;
+ }

And add these CSS classes to your stylesheet:

.search-highlight {
  background-color: yellow;
  color: black;
}

.active-highlight {
  background-color: rgb(255, 161, 10);
  color: white;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a3abb0 and 940f740.

📒 Files selected for processing (1)
  • web-server/src/content/Service/SystemLogs.tsx (4 hunks)
🔇 Additional comments (7)
web-server/src/content/Service/SystemLogs.tsx (7)

128-137: Properly implemented container ref on scrollable element

Great job fixing the container ref issue from the previous review! The containerRef is now correctly attached to the scrollable FlexBox, which will allow the scroll logic to work properly.


91-107: The navigation logic works well

The navigation function properly handles cycling through matches in both directions. It updates the current match index, scrolls to the selected element, and updates the highlight styling.


33-36: Simplified scroll detection logic

Good simplification of the scroll detection logic! Using a direct scroll event listener is more straightforward than an IntersectionObserver for this use case.


133-134: Properly integrated search query with log components

The search query is correctly passed down to both PlainLog and FormattedLog components, which will enable highlighting of matched text in the logs.

Also applies to: 135-136


116-121: Well-integrated search UI component

The LogSearch component is properly integrated with all the necessary props for search functionality and navigation.


42-49: Smooth scrolling implementation

The scrollToBottom function properly implements smooth scrolling behavior to enhance the user experience.


77-77: 🛠️ Refactor suggestion

Potential null reference issue

The optional chaining on containerRef.current?.querySelectorAll protects against null, but if the component hasn't rendered yet, there won't be any elements to query.

Add a check to ensure the search is only performed when the container is available:

- const elements = Array.from(
-   containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
- ) as HTMLElement[];
+ const elements = containerRef.current
+   ? Array.from(containerRef.current.querySelectorAll('span[style*="background-color: yellow"]')) as HTMLElement[]
+   : [];

Likely an incorrect or invalid review comment.

@samad-yar-khan
Copy link
Contributor

samad-yar-khan commented Apr 23, 2025

from the looks of it is just highlighting the logs, not actually searching through them or filtering the search results. Seems satisfactory from the issue. But can we make this more usable ?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

154-167: 🛠️ Refactor suggestion

Remove direct reference to DOM elements in JSX calculation.

Accessing DOM properties directly in render calculations can cause issues during SSR and is generally not a React best practice. Move this calculation to a useEffect or memoized value.

+ const [buttonMarginLeft, setButtonMarginLeft] = useState(0);
+
+ useEffect(() => {
+   if (containerRef.current) {
+     setButtonMarginLeft(containerRef.current.clientWidth / 2 - 67);
+   }
+ }, []);

  // In the JSX
  <Button
    onClick={scrollToBottom}
    component={MotionBox}
    initial={{ opacity: 0, scale: 0.7 }}
    animate={{ opacity: 1, scale: 1 }}
    transition={{
      ease: 'easeOut'
    }}
    bottom={20}
    sx={{
      position: 'fixed',
-     marginLeft: `calc(${containerRef.current
-         ? containerRef.current.clientWidth / 2 - 67
-         : 0
-       }px)`
+     marginLeft: `${buttonMarginLeft}px`
    }}
  >
♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

69-84: DOM query for highlighted elements is brittle and should be improved.

The current approach of querying elements by their inline style attribute is fragile. React might reformat or reorder style properties, causing the selector to fail. This was noted in a previous review comment as well.

As suggested in the previous review, use a dedicated class or data attribute for highlighted elements and update your query selector:

- const elements = Array.from(
-   containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
- ) as HTMLElement[];
+ const elements = Array.from(
+   containerRef.current?.querySelectorAll('span.log-highlight') ?? []
+ ) as HTMLElement[];

Make sure to add the corresponding class to the highlighted spans in both PlainLog and FormattedLog components.

🧹 Nitpick comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

54-67: Consider debouncing the search input to improve performance.

For an improved user experience, consider adding debounce to prevent excessive processing during rapid typing. This is especially important since you're performing DOM operations in response to search input.

+import { debounce } from 'lodash';
+
 const SystemLogs = memo(({ serviceName }: { serviceName?: ServiceNames }) => {
   // ... existing code
 
-  const handleSearch = useCallback((query: string) => {
+  const debouncedSearch = useCallback(
+    debounce((query: string) => {
+      // Reset states only when query is empty or below a threshold
+      if (!query || query.length < 3) {
+        setCurrentMatch(0);
+        setTotalMatches(0);
+        setHighlightedElements([]);
+        updateHighlight(null);
+        return;
+      }
+      // Let the effect handle DOM updates
+    }, 300),
+    [updateHighlight]
+  );
+
+  const handleSearch = useCallback((query: string) => {
     setSearchQuery(query);
-  
-    // Reset states only when query is empty or below a threshold
-    if (!query || query.length < 3) {
-      setCurrentMatch(0);
-      setTotalMatches(0);
-      setHighlightedElements([]);
-      updateHighlight(null);
-      return;
-    }
-  
-    // Let the effect handle DOM updates
+    debouncedSearch(query);
   }, [updateHighlight]);

87-103: Add error handling for navigation edge cases.

The navigation function works well, but lacks error handling for edge cases. Consider adding checks to ensure the element still exists in the DOM before attempting to scroll to it.

 const handleNavigate = useCallback((direction: 'prev' | 'next') => {
   if (highlightedElements.length === 0) return;

   let newIndex = currentMatch;
   if (direction === 'next') {
     newIndex = currentMatch < totalMatches ? currentMatch + 1 : 1;
   } else {
     newIndex = currentMatch > 1 ? currentMatch - 1 : totalMatches;
   }

   setCurrentMatch(newIndex);
   const element = highlightedElements[newIndex - 1];
-  if (element) {
+  if (element && document.body.contains(element)) {
     element.scrollIntoView({ behavior: 'smooth', block: 'center' });
     updateHighlight(element);
+  } else {
+    // Element no longer in DOM, re-query highlighted elements
+    const freshElements = Array.from(
+      containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
+    ) as HTMLElement[];
+    setHighlightedElements(freshElements);
+    if (freshElements.length) {
+      setTotalMatches(freshElements.length);
+      setCurrentMatch(1);
+      updateHighlight(freshElements[0]);
+      freshElements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
+    }
   }
 }, [currentMatch, totalMatches, highlightedElements, updateHighlight]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 901cd26 and e2e5018.

📒 Files selected for processing (2)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx
🔇 Additional comments (8)
web-server/src/content/Service/SystemLogs.tsx (8)

3-3: Component optimization with React.memo and new imports look good.

The component is properly wrapped with memo for performance optimization, and all necessary hooks and components are imported. This will prevent unnecessary re-renders when the serviceName prop doesn't change.

Also applies to: 8-8, 19-19, 175-175


22-28: State management for search functionality is well-structured.

The state variables are appropriately separated for different aspects of the search functionality. Good use of the ref for tracking the currently highlighted element across renders.


30-37: Clean implementation of scrollToBottom function.

The scroll functionality is simplified with direct scrollTo calls, which is more straightforward than the previous approach.


39-52: The highlight management callback looks good.

The updateHighlight callback effectively toggles between the default yellow background and the distinct orange highlight for the active match.


105-115: Initial scroll implementation looks good.

The initial scroll implementation now correctly uses a ref to track the initial load and requestAnimationFrame to ensure the DOM is ready before scrolling.


117-125: Memoized renderLogs function is a good optimization.

Extracting the logs rendering logic into a memoized callback is a good optimization that prevents unnecessary re-renders.


134-139: Good integration of the LogSearch component.

The LogSearch component is properly integrated with all the necessary props. This cleanly separates the search UI from the main component.


146-148: Container ref placement is now correct.

The containerRef is now properly attached to the scrollable container, fixing the previous issue where it was attached to an empty element.

@harshit078
Copy link
Author

Screenshot 2025-04-27 at 1 12 38 PM

Improved pointer and key input interactions from 150 ms to 80ms

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

88-90: Replace fragile style selector with semantic class / data-attribute

The DOM lookup relies on matching the literal string background-color: yellow, which is brittle (style order, minification, different color formats, etc.).
A robust approach is to mark the highlighted <span> with a class (log-highlight) or data- attribute and query that instead.

This point was raised in a previous review but is still unresolved.


22-23: showScrollDownButton is never toggled – add scroll listener

showScrollDownButton is initialised but never updated when the user scrolls away from the bottom, so the floating button will never appear.

A simple scroll handler on the containerRef will fix this:

+  /* show / hide “scroll-to-bottom” button */
+  useEffect(() => {
+    const el = containerRef.current;
+    if (!el) return;
+
+    const handleScroll = () => {
+      const { scrollTop, scrollHeight, clientHeight } = el;
+      const atBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10;
+      showScrollDownButton.set(!atBottom);
+    };
+
+    el.addEventListener('scroll', handleScroll);
+    handleScroll(); // initialise
+    return () => el.removeEventListener('scroll', handleScroll);
+  }, [showScrollDownButton]);

Also applies to: 179-185

🧹 Nitpick comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

191-195: Hard-coded marginLeft breaks on resize – use CSS centering

The floating button’s horizontal position is calculated once during render and won’t adjust if the log pane resizes.
Using left: 50% with transform: translateX(-50%) is simpler and fully responsive:

-            bottom={20}
-            sx={{
-              position: 'fixed',
-              marginLeft: `calc(${containerRef.current
-                  ? containerRef.current.clientWidth / 2 - 67
-                  : 0
-                }px)`
-            }}
+            sx={{
+              position: 'fixed',
+              bottom: 20,
+              left: '50%',
+              transform: 'translateX(-50%)'
+            }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2e5018 and 65c860e.

📒 Files selected for processing (1)
  • web-server/src/content/Service/SystemLogs.tsx (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
web-server/src/content/Service/SystemLogs.tsx (5)
web-server/src/hooks/useSystemLogs.tsx (1)
  • useSystemLogs (5-22)
web-server/src/utils/logFormatter.ts (1)
  • parseLogLine (12-95)
web-server/src/components/Service/SystemLog/PlainLog.tsx (1)
  • PlainLog (34-40)
web-server/src/components/Service/SystemLog/FormattedLog.tsx (1)
  • FormattedLog (37-82)
web-server/src/components/Service/SystemLog/SystemLogsErrorFllback.tsx (1)
  • SystemLogsErrorFallback (12-60)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
web-server/src/content/Service/SystemLogs.tsx (3)

87-89: Use a stable marker for highlighted spans instead of relying on inline style

The selector 'span[style*="background-color: yellow"]' is fragile as React may normalize styles or reorder properties.

- const elements = Array.from(
-   containerRef.current?.querySelectorAll('span[style*="background-color: yellow"]') ?? []
- ) as HTMLElement[];
+ const elements = Array.from(
+   containerRef.current?.querySelectorAll('.log-highlight') ?? []
+ ) as HTMLElement[];

This means you'll need to add the className="log-highlight" to the highlighted spans in PlainLog and FormattedLog components.


84-102: Re-run highlight scan when new logs arrive

The useLayoutEffect only depends on debouncedSearchQuery. If new logs load while a search query is active, matches in those logs won't be highlighted or included in navigation.

- }, [debouncedSearchQuery]);
+ }, [debouncedSearchQuery, logs]);

22-22: Missing implementation for showScrollDownButton toggle

You've removed the IntersectionObserver but there's no mechanism to detect when the user scrolls up to show the scroll-down button.

 const SystemLogs = memo(({ serviceName }: { serviceName?: ServiceNames }) => {
   // ... existing code
+  useEffect(() => {
+    const handleScroll = () => {
+      if (!containerRef.current) return;
+      
+      const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
+      const isScrolledToBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10;
+      showScrollDownButton.set(!isScrolledToBottom);
+    };
+    
+    const scrollContainer = containerRef.current;
+    if (scrollContainer) {
+      scrollContainer.addEventListener('scroll', handleScroll);
+    }
+    
+    return () => {
+      if (scrollContainer) {
+        scrollContainer.removeEventListener('scroll', handleScroll);
+      }
+    };
+  }, [showScrollDownButton]);
🧹 Nitpick comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

53-66: Direct DOM manipulation may conflict with React's rendering

You're directly manipulating DOM element styles instead of using React's state-based rendering. This can lead to issues if React re-renders the elements.

Consider modifying the HighlightedText component to accept an isActive prop, so React can handle the highlighting:

// In PlainLog and FormattedLog components:
<HighlightedText
  text={text}
  searchQuery={searchQuery}
+ isActiveMatch={matchIndex === currentActiveMatchIndex}
/>

// In HighlightedText component:
const getStyle = (isMatch, isActive) => {
  if (isMatch) {
    return isActive 
      ? { backgroundColor: 'rgb(255, 161, 10)', color: 'white' }
      : { backgroundColor: 'yellow', color: 'black' };
  }
  return {};
};

This would eliminate the need for direct DOM manipulation.


72-81: Consider extracting magic numbers as constants

The minimum search query length (3 characters) is hardcoded in multiple places.

+const MIN_SEARCH_LENGTH = 3;

const handleSearch = useCallback((query: string) => {
  setSearchQuery(query);
  isSearchingRef.current = !!query;

-  if (!query || query.length < 3) {
+  if (!query || query.length < MIN_SEARCH_LENGTH) {
    // ...

Also update line 85 to use the constant.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65c860e and 113e483.

📒 Files selected for processing (1)
  • web-server/src/content/Service/SystemLogs.tsx (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
web-server/src/content/Service/SystemLogs.tsx (6)
web-server/src/hooks/useSystemLogs.tsx (1)
  • useSystemLogs (5-22)
web-server/src/utils/logFormatter.ts (1)
  • parseLogLine (12-95)
web-server/src/components/Service/SystemLog/PlainLog.tsx (1)
  • PlainLog (34-40)
web-server/src/components/Service/SystemLog/FormattedLog.tsx (1)
  • FormattedLog (37-82)
web-server/src/components/Service/SystemLog/SystemLogsErrorFllback.tsx (1)
  • SystemLogsErrorFallback (12-60)
web-server/src/components/Service/SystemLog/LogSearch.tsx (1)
  • LogSearch (178-178)
🔇 Additional comments (5)
web-server/src/content/Service/SystemLogs.tsx (5)

176-178: Proper implementation of container with ref

This is a good improvement that addresses a previous critical issue - you've correctly attached the containerRef to the scrollable container.


164-169: Good integration of the search component

The LogSearch component is well-integrated with proper props being passed for search functionality and navigation.


142-155: Good extraction of renderLogs as a memoized callback

The extraction of the log rendering logic into a memoized callback with the correct dependencies is a good performance optimization.


27-34: 🛠️ Refactor suggestion

Redundant state and ref duplications

You have duplicate state and refs for highlightedElements, currentMatch, and totalMatches. This creates potential synchronization issues.

Remove the redundant state variables and use either the state or the refs consistently:

- const [highlightedElements, setHighlightedElements] = useState<HTMLElement[]>([]);
- const [currentMatch, setCurrentMatch] = useState(0);
- const [totalMatches, setTotalMatches] = useState(0);
  const currentHighlightRef = useRef<HTMLElement | null>(null);
  const isInitialLoad = useRef(true);
  const isSearchingRef = useRef(false);
  const currentMatchRef = useRef(0);
  const totalMatchesRef = useRef(0);
  const highlightedElementsRef = useRef<HTMLElement[]>([]);
  const isNavigatingRef = useRef(false);

Then update your component to only use the refs for DOM manipulation and state for rendering:

// When updating for UI rendering
- setHighlightedElements(elements);
  highlightedElementsRef.current = elements;
- setTotalMatches(newTotalMatches);
  totalMatchesRef.current = newTotalMatches;
- setCurrentMatch(newTotalMatches ? 1 : 0);
  currentMatchRef.current = newTotalMatches ? 1 : 0;

// In LogSearch component props
  <LogSearch
    onSearch={handleSearch}
    onNavigate={handleNavigate}
-   currentMatch={currentMatch}
-   totalMatches={totalMatches}
+   currentMatch={currentMatchRef.current}
+   totalMatches={totalMatchesRef.current}
  />

Likely an incorrect or invalid review comment.


68-82:

✅ Verification successful

Consider enhancing search with filtering capability

According to the PR comments, the current implementation only highlights matches but doesn't filter the logs to show only matching entries.

Consider enhancing the feature by adding an option to filter logs to show only entries containing the search query:

+ const [shouldFilterLogs, setShouldFilterLogs] = useState(false);

// In handleSearch:
  const handleSearch = useCallback((query: string) => {
    // ...existing code...
  }, [updateHighlight]);

+ const filteredLogs = useMemo(() => {
+   if (!debouncedSearchQuery || !shouldFilterLogs) return logs;
+   return logs.filter(log => log.toLowerCase().includes(debouncedSearchQuery.toLowerCase()));
+ }, [logs, debouncedSearchQuery, shouldFilterLogs]);

// In renderLogs:
- return logs.map((log, index) => {
+ return (shouldFilterLogs ? filteredLogs : logs).map((log, index) => {

// Update LogSearch component:
<LogSearch
  onSearch={handleSearch}
  onNavigate={handleNavigate}
  currentMatch={currentMatch}
  totalMatches={totalMatches}
+ onToggleFilter={setShouldFilterLogs}
+ isFilterEnabled={shouldFilterLogs}
/>

Then update your LogSearch component to include a filter toggle button.


🏁 Script executed:

#!/bin/bash
# Check if this enhancement aligns with the PR requirements

echo "PR objectives mention:"
cat << EOF
The pull request titled "feat: Implemented log search functionality with highlighted results"
aims to enhance the log viewing experience by adding search capabilities within logs.

The comment on the PR by user samad-yar-khan notes that the current implementation
appears to highlight the logs but does not actually perform searching through or filtering
of the search results. The commenter suggests considering enhancements to make the feature
more usable.
EOF

echo -e "\nChecking the current LogSearch component implementation:"
fd -t f LogSearch.tsx | xargs cat

Length of output: 5083


🏁 Script executed:

#!/bin/bash
# Locate SystemLogs.tsx and show relevant sections around log rendering
file=$(fd -t f SystemLogs.tsx)
echo "----- File: $file -----"
sed -n '1,200p' "$file"
echo "----- Rendering logs in SystemLogs.tsx -----"
grep -R "renderLogs" -n "$file"
grep -R "logs" -n "$file" | sed -n '1,50p'

Length of output: 1131


🏁 Script executed:

#!/bin/bash
set -e

# Find SystemLogs.tsx files
files=$(fd -t f SystemLogs.tsx)

for f in $files; do
  echo "===== File: $f ====="
  
  # Show first 200 lines for context
  sed -n '1,200p' "$f"
  
  echo -e "\n--- Occurrences of 'logs', 'render', and 'renderLogs' in $f ---"
  grep -E -n "(logs|renderLogs|render)" "$f" || true
  
  echo -e "\n\n"
done

Length of output: 9077


No changes needed: filtering logs is a future enhancement
The current PR implements in-line match highlighting per its scope. Introducing entry‐level filtering would be a valuable follow-up but isn’t required to fulfill the “highlight search results” objective here.

• The existing handleSearch + renderLogs flow correctly highlights matches without altering the log list.
• If you’d like to expose a “show only matching entries” toggle, consider capturing it in a separate issue or PR to avoid expanding the current feature scope.

Copy link
Contributor

@jayantbh jayantbh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, first things first, my review might be overwhelming.
Sorry about that, should have done this in parts perhaps.

But given that I rarely get time to review PRs in our OSS repo, I decided doing it all in one go might save you more time.

That being said, great first attempt, because it functions alright. :)

However, the code quality leaves a lot to be desired.
Take this as a challenge, I'm hoping you'll come away having learnt a lot of serious stuff from this.

Some learning material I've linked in one of the comments:
https://kentcdodds.com/blog/make-impossible-states-impossible
https://www.youtube.com/watch?v=IcgmSRJHu_8 (link is about Elm lang, but focus on the concept - it's beautifully presented)

}

const HighlightedText = ({ text, searchQuery }: { text: string; searchQuery?: string }) => {
if (!searchQuery) return <>{text}</>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A component can directly return text. Text nodes are valid react nodes.

searchQuery?: string;
}

const HighlightedText = ({ text, searchQuery }: { text: string; searchQuery?: string }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the component HighlightedText is misleading because this is used to render logs regardless of whether it's highlighted or not.

FormattedLog was perhaps a better name.

Comment on lines 107 to 126
requestAnimationFrame(() => {
let newIndex = currentMatchRef.current;
const total = totalMatchesRef.current;

if (direction === 'next') {
newIndex = newIndex < total ? newIndex + 1 : 1;
} else {
newIndex = newIndex > 1 ? newIndex - 1 : total;
}
};
}, [showScrollDownButton]);

setCurrentMatch(newIndex);
currentMatchRef.current = newIndex;
const element = highlightedElementsRef.current[newIndex - 1];
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
updateHighlight(element);
}
isNavigatingRef.current = false;
});
}, [updateHighlight]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an example of adding hacky code to make existing hacky code work.

Using a proper state machine architecture will make this SO MUCH EASIER to handle.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

76-96: 🛠️ Refactor suggestion

Search effect needs to watch for new logs

The search effect only depends on searchQuery, but it should also re-run when new logs are loaded. If new logs arrive while a search is active, the new content won't be searched or highlighted.

-  }, [searchState.query]);
+  }, [searchState.query, logs]);
🧹 Nitpick comments (3)
web-server/src/content/Service/SystemLogs.tsx (3)

166-177: Good implementation of scroll detection

The scroll event listener correctly implements the scroll detection for showing/hiding the scroll-down button, fixing the previously identified issue. Consider debouncing the scroll handler for better performance, especially with large log volumes.

+ import { debounce } from '@mui/material/utils';

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;
    
-   const handleScroll = () => {
+   const handleScroll = debounce(() => {
      const isScrolledUp = container.scrollTop < container.scrollHeight - container.clientHeight - 100;
      showScrollDownButton.set(isScrolledUp);
-   };
+   }, 100);
    
    container.addEventListener('scroll', handleScroll);
-   return () => container.removeEventListener('scroll', handleScroll);
+   return () => {
+     container.removeEventListener('scroll', handleScroll);
+     handleScroll.clear();
+   };
  }, [showScrollDownButton]);

98-105: Consider optimizing the scrollToBottom function

The scrollToBottom function works correctly, but could be optimized to avoid unnecessary scrolling when already at the bottom.

  const scrollToBottom = useCallback(() => {
    if (containerRef.current) {
+     const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
+     const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 10;
+     
+     if (isAtBottom) return;
+     
      containerRef.current.scrollTo({
        top: containerRef.current.scrollHeight,
        behavior: 'smooth'
      });
    }
  }, []);

246-249: Consider refactoring button position calculation

The inline calculation for the button position is complex and could be extracted for better readability. Also, there's potential for unnecessary re-renders since the calculation references the DOM directly in the JSX.

+ const getButtonPosition = useCallback(() => {
+   if (!containerRef.current) return 0;
+   return containerRef.current.clientWidth / 2 - 67;
+ }, []);
+
+ useEffect(() => {
+   // Force re-calculation when container size changes
+   const resizeObserver = new ResizeObserver(() => {
+     // Trigger re-render if button is visible
+     if (showScrollDownButton.value) {
+       showScrollDownButton.set(true);
+     }
+   });
+   
+   if (containerRef.current) {
+     resizeObserver.observe(containerRef.current);
+   }
+   
+   return () => {
+     if (containerRef.current) {
+       resizeObserver.unobserve(containerRef.current);
+     }
+     resizeObserver.disconnect();
+   };
+ }, [showScrollDownButton]);

  // In the JSX:
  sx={{
    position: 'fixed',
-   marginLeft: `calc(${containerRef.current
-       ? containerRef.current.clientWidth / 2 - 67
-       : 0
-     }px)`
+   marginLeft: `${getButtonPosition()}px`
  }}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d41efb and ebdb7bf.

📒 Files selected for processing (3)
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx (2 hunks)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx
🔇 Additional comments (8)
web-server/src/content/Service/SystemLogs.tsx (8)

19-66: Excellent refactoring to a reducer pattern! 👍

The implementation of a unified search state with a reducer pattern is a significant improvement over managing multiple separate state variables. This addresses the previous feedback about making "impossible states impossible" and reduces the potential for bugs from inconsistent state.

The state structure clearly represents all possible search states, and the reducer actions are well-defined with appropriate state transitions.


87-89: Great improvement using data attributes instead of style selectors

Using data-highlighted="true" instead of the previous style-based selector (span[style*="background-color: yellow"]) is a significant improvement. This addressing previous feedback and provides a more reliable way to identify highlighted elements.


111-122: Solid navigation logic with proper boundary handling

The navigation implementation correctly handles edge cases:

  • Skips navigation when there are no matches or search is in progress
  • Properly cycles through matches (wrapping from last to first and vice versa)
  • Updates the current index in the state

179-204: Well-structured log rendering with memoization

The memoized rendering logic is well-implemented and correctly passes the search query and match status to child components. The use of data-log-index attribute is a good practice for DOM traversal.


225-231: Properly addressed previous container ref issue

The container ref is now correctly attached to the scrollable FlexBox, resolving the previous issue where the ref was attached to an empty element. This ensures the scroll functionality works as expected.


68-68: Good use of memoization and clean export

Wrapping the component with memo is good for performance optimization, and the explicit export is cleaner than using default export.

Also applies to: 258-260


152-164: Smooth initial scroll implementation

The implementation for initial scrolling to the bottom is well done, with proper checks for loading state and log availability. Using behavior: 'auto' ensures the initial scroll is immediate rather than animated.


124-150:

✅ Verification successful

Verify highlighted element selection works in all browsers

The DOM traversal logic to find the parent log element is well-implemented, but there's a potential edge case: if the DOM structure changes, the data-log-index attribute might not be found.


🏁 Script executed:

#!/bin/bash
# Verify that PlainLog and FormattedLog components properly set data-highlighted attribute

echo "Checking PlainLog component implementation:"
rg -A 10 -B 5 "data-highlighted" --glob "*.tsx" src/components/Service/SystemLog/PlainLog.tsx

echo -e "\nChecking FormattedLog component implementation:"
rg -A 10 -B 5 "data-highlighted" --glob "*.tsx" src/components/Service/SystemLog/FormattedLog.tsx

Length of output: 575


I want to locate the actual component files and see how they annotate each log entry. Let’s search for PlainLog, FormattedLog, and any use of data-log-index or data-highlighted in the repo:

This will tell us the correct paths and whether those components set the necessary attributes.


🏁 Script executed:

#!/bin/bash
# Find where PlainLog and FormattedLog are defined
echo "=== PlainLog definitions ==="
rg -n "PlainLog" --glob "*.tsx"

echo -e "\n=== FormattedLog definitions ==="
rg -n "FormattedLog" --glob "*.tsx"

# Find uses of data-log-index
echo -e "\n=== data-log-index usage ==="
rg -n "data-log-index" --glob "*.tsx"

# Find uses of data-highlighted (highlight attr)
echo -e "\n=== data-highlighted usage ==="
rg -n "data-highlighted" --glob "*.tsx"

Length of output: 2304


Let’s confirm how PlainLog leverages HighlightedText (from FormattedLog) to set the data-highlighted attribute:


🏁 Script executed:

#!/bin/bash
# Check where PlainLog wraps matches in HighlightedText
echo "=== HighlightedText import & usage in PlainLog.tsx ==="
rg -n "HighlightedText" --glob "web-server/src/components/Service/SystemLog/PlainLog.tsx"

echo -e "\n=== Context around HighlightedText in PlainLog.tsx ==="
rg -A 5 -B 5 "HighlightedText" --glob "web-server/src/components/Service/SystemLog/PlainLog.tsx"

Length of output: 2083


We should inspect the HighlightedText implementation in FormattedLog.tsx to confirm it applies data-highlighted correctly. Let’s pull that definition:


🏁 Script executed:

#!/bin/bash
# Show HighlightedText implementation including data-highlighted spans
rg -A5 -B20 "const HighlightedText" --glob "web-server/src/components/Service/SystemLog/FormattedLog.tsx"

Length of output: 2413


Confirmed data-highlighted on all highlighted spans
We’ve verified that:

  • The HighlightedText component in
    web-server/src/components/Service/SystemLog/FormattedLog.tsx renders each match wrapped in
    <span data-highlighted="true">…</span>.
  • PlainLog imports and uses HighlightedText directly.
  • SystemLogs.tsx’s lookup
    containerRef.current?.querySelectorAll('span[data-highlighted="true"]') (line 88) will therefore find every highlighted element.

No changes are required—your DOM traversal and attribute usage will work consistently across browsers.

@harshit078 harshit078 requested a review from jayantbh May 1, 2025 12:50
@samad-yar-khan
Copy link
Contributor

samad-yar-khan commented May 6, 2025

@harshit078 one of the linter checks is failing.

Comment on lines 79 to 91
const handleSearchChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const query = event.target.value;
setSearchQuery(query);
debouncedSearch(query);
},
[debouncedSearch]
);

const handleClear = useCallback(() => {
setSearchQuery('');
onSearch('');
}, [onSearch]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think about it, when resetting, you're doing both - setting the search query to something, and also triggering search function - separately - with that query.

Could there be a better way to do this?

);

const showSearchControls = useMemo(
() => searchQuery && totalMatches > 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if search query is just a bunch of spaces?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this really doesn't need to be memoized.

Comment on lines 93 to 98
const handleNavigate = useCallback(
(direction: 'prev' | 'next') => {
onNavigate(direction);
},
[onNavigate]
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If handleNavigate just calls onNavigate and nothing else, and it re-triggers when onNavigate changes, why even wrap it in a useCallback?

<SearchIcon color="action" />
</InputAdornment>
),
endAdornment: searchQuery && (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps using Boolean(searchQuery.length) (or !!searchQuery.length) will be a more obvious way of doing this.

dispatch({ type: 'SET_SEARCHING', payload: true });

const timer = setTimeout(() => {
requestAnimationFrame(() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why requestAnimationFrame here?

if (total === 0 || isSearching) return;

const newIndex = direction === 'next'
? (currentIndex < total ? currentIndex + 1 : 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This indexing starts at 1?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For next, you can just use modulus.
curr + 1 % total or something.
prev is fine.

Comment on lines 135 to 140
while (parentElement && !parentElement.hasAttribute('data-log-index')) {
parentElement = parentElement.parentElement;
}

if (parentElement) {
const lineIndex = parseInt(parentElement.getAttribute('data-log-index') || '-1', 10);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way than reading to/writing from HTML attributes?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use React's createRef and then alter our approach using react ref system. Shall we implement this way, What do you think ?

if (containerRef.current && !showScrollDownButton.value) {
scrollDown();
const { currentIndex, elements } = searchState;
if (currentIndex === 0 || !elements.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think index being 0 as a "null" state will confused folks.
In almost all languages indices start at 0.

If you MUST start from 1, maybe call it rank, or order, etc. But I really think with very little refactoring you can make it start from 0.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I made the refactoring 0 based indexing

@jayantbh
Copy link
Contributor

jayantbh commented May 6, 2025

Much better. But still has lint failures, and some feedback remaining.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

97-99: 🛠️ Refactor suggestion

Use a more specific selector for highlighted elements

Using span[data-highlighted="true"] as a selector could potentially conflict with third-party extensions or other components. Consider using a more specific and namespaced attribute or class name.

-          containerRef.current?.querySelectorAll(
-            'span[data-highlighted="true"]'
-          ) ?? []
+          containerRef.current?.querySelectorAll(
+            'span[data-mhq-log-highlighted="true"]'
+          ) ?? []

Also make sure to update the corresponding attributes in your PlainLog and FormattedLog components.

🧹 Nitpick comments (4)
web-server/src/content/Service/SystemLogs.tsx (4)

122-136: Simplify navigation logic with modulo operator

The navigation logic can be simplified using the modulo operator for the "next" case, making it more concise and easier to understand.

-    const newIndex = direction === 'next'
-      ? (currentIndex < total ? currentIndex + 1 : 1)
-      : (currentIndex > 1 ? currentIndex - 1 : total);
+    const newIndex = direction === 'next'
+      ? ((currentIndex % total) + 1)
+      : (currentIndex > 1 ? currentIndex - 1 : total);

130-130: Consider zero-based indexing for consistency

Starting the indexing at 1 instead of 0 is inconsistent with JavaScript conventions and could lead to confusion. Consider refactoring to use zero-based indexing throughout the codebase.

If changing to zero-based indexing isn't possible at this stage, consider renaming variables to make it clear that they're one-based (e.g., matchPosition instead of currentIndex).


140-166: Extract element navigation logic to a separate function

The element navigation logic is complex and could benefit from being extracted into a separate named function for better readability and maintainability.

Consider extracting this logic to a function like scrollToMatchedElement(element: HTMLElement) that handles finding the parent with data attributes and scrolling to the element.


232-236: Type assertion may not be necessary

The type assertion serviceName as ServiceNames may be unnecessary if the component typing is correctly set up. Consider reviewing if this can be handled through proper typing of the component props.

- serviceName={serviceName as ServiceNames} 
+ serviceName={serviceName}

If type assertion is truly needed, add a comment explaining why.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebdb7bf and 89d9329.

📒 Files selected for processing (4)
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx (2 hunks)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx (1 hunks)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx
  • web-server/src/components/Service/SystemLog/LogSearch.tsx
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx
🔇 Additional comments (9)
web-server/src/content/Service/SystemLogs.tsx (9)

26-73: Well-implemented reducer pattern for search state management! 👍

Great job implementing a reducer pattern for the search state. This is a significant improvement that addresses previous feedback about having too many separate states. The SearchState type and reducer implementation are clean and well-organized, making the component's state logic more maintainable.


75-84: Good component organization with memo optimization

Using memo to wrap the component and properly organizing the state declarations shows thoughtful implementation. The initial state setup is clear and the reducer integration is well done.


95-95: Consider if requestAnimationFrame is necessary here

The use of requestAnimationFrame may be unnecessary in this callback that's already wrapped in a setTimeout. Both functions defer execution, and the double deferral could potentially cause a slight delay in updating the UI.

Is there a specific reason for using both setTimeout and requestAnimationFrame? In most cases, just one of these timing mechanisms would be sufficient.


160-165: Ensure smooth scrolling works consistently

Using scrollIntoView with behavior: 'smooth' can be inconsistent across browsers. Consider adding a fallback or polyfill for browsers that don't support smooth scrolling.

Test this functionality in different browsers to ensure consistent behavior.


169-186: Good attention to initial load UX

The effect to scroll to the bottom on initial load is a nice touch for user experience. Using 'auto' for the behavior ensures it happens immediately without animation, which is appropriate for initial load.


188-201: Well-implemented scroll listener for button visibility

This is a good implementation of the scroll listener to toggle the scroll button visibility. The cleanup in the effect's return function properly removes the event listener when the component unmounts.


203-228: Well-structured log rendering with memoization

The memoized renderLogs function is a good performance optimization. It correctly passes the search query and current match status to the log components.


240-245: Great integration of search UI component

The LogSearch component integration looks clean and well-implemented, with all necessary props passed correctly.


252-258: Properly structured container with ref

The container structure with ref attachment is correctly implemented. This addresses previous issues where the ref was attached to an empty element.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

85-107: Add logs dependency to search effect

The search effect only depends on searchState.query, but if new logs arrive while the same search query is active, those new logs won't be highlighted until the query changes.

-  }, [searchState.query]);
+  }, [searchState.query, logs]);
🧹 Nitpick comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

96-100: Consider using a more specific class name for highlighted logs

The .mhq--highlighted-log class is better than querying by style attributes, but consider using an even more specific namespace like .mhq-systemlogs-highlight to avoid potential conflicts with third-party extensions or other components.

         const elements = Array.from(
           containerRef.current?.querySelectorAll(
-            '.mhq--highlighted-log'
+            '.mhq-systemlogs-highlight'
           ) ?? []
         ) as HTMLElement[];

140-169: Consider using a ref-based approach for highlight scrolling

While the DOM traversal approach works, consider using React refs for managing highlighted elements instead of direct DOM manipulation. This would better align with React's declarative paradigm.

You could maintain a map of refs for highlighted elements and use those for scrolling instead of querying the DOM directly.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89d9329 and 25c1dee.

📒 Files selected for processing (5)
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx (2 hunks)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx (1 hunks)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (3 hunks)
  • web-server/src/hooks/useSystemLogs.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx
  • web-server/src/components/Service/SystemLog/LogSearch.tsx
🔇 Additional comments (9)
web-server/src/hooks/useSystemLogs.tsx (1)

9-9: LGTM: Type signature update correctly makes serviceName required

Making serviceName a required parameter is a good improvement that aligns with the search functionality implementation, ensuring logs are always associated with a specific service.

web-server/src/content/Service/SystemLogs.tsx (8)

26-73: Nice implementation of reducer pattern for search state

The use of a reducer pattern with well-defined types for managing complex search state is a good design choice. It makes state transitions explicit and easier to reason about, which will help maintain the code as this feature evolves.


95-95: Consider if requestAnimationFrame is necessary here

The requestAnimationFrame call wraps a DOM query operation, which is already synchronous. While this won't cause issues, it may not be necessary in this specific case and adds complexity.

Do you have specific performance reasons for using requestAnimationFrame here instead of directly querying the DOM?


122-138: Good implementation of navigation with modulus for wrapping

The navigation logic is well-implemented, especially the use of modulus for wrapping around to the first match when navigating past the last one. The code also correctly handles edge cases (no matches, search in progress).


171-188: Good implementation of initial scroll behavior

The initial scroll behavior is well-implemented, using a ref to track the initial load state and only scrolling on the first load. Using requestAnimationFrame here is appropriate to ensure scrolling happens after rendering.


190-203: Good addition of scroll event listener

The scroll event listener correctly replaces the previous IntersectionObserver approach and properly cleans up the listener in the effect cleanup function to prevent memory leaks.


205-230: Well-structured memoized log rendering function

The memoized rendering function is well-structured and correctly passes the search query and current match status to the log components. Using useCallback here is good for performance.


242-247: Good integration of LogSearch component

The LogSearch component is well integrated with the necessary props for search and navigation functionality.


254-260: Good implementation of logs container with proper ref

The logs container is correctly implemented with a ref and uses the memoized rendering function. The styling for overflow and max height is appropriate for a scrollable container.

@harshit078 harshit078 requested a review from jayantbh May 12, 2025 13:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
web-server/src/content/Service/SystemLogs.tsx (1)

85-105: ⚠️ Potential issue

Add logs dependency to search effect

The search effect only depends on searchState.query but not on logs. This means that if new logs arrive while the same search query is active, these logs won't be highlighted until the query changes.

-  }, [searchState.query]);
+  }, [searchState.query, logs]);
🧹 Nitpick comments (3)
web-server/src/content/Service/SystemLogs.tsx (3)

95-102: Consider removing nested requestAnimationFrame

Using requestAnimationFrame inside a setTimeout appears redundant. Since you're already debouncing with a 300ms timeout, the additional frame scheduling is likely unnecessary and adds complexity.

    const timer = setTimeout(() => {
-      requestAnimationFrame(() => {
-        const elements = Array.from(
-          containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
-        ) as HTMLElement[];
-
-        dispatch({ type: 'SET_MATCHES', payload: elements });
-      });
+      const elements = Array.from(
+        containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
+      ) as HTMLElement[];
+
+      dispatch({ type: 'SET_MATCHES', payload: elements });
    }, 300);

97-97: Use a more specific class name for highlighted logs

The class .mhq--highlighted-log is more specific than the previous style-based selector, which is good. However, as mentioned in previous feedback, consider using an even more specific namespace to avoid potential conflicts with third-party code.

-          containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
+          containerRef.current?.querySelectorAll('.missionhq-highlighted-log') ?? []

160-166: Consider removing requestAnimationFrame for scrolling

Using requestAnimationFrame for the scrollIntoView call is likely unnecessary since the scrolling behavior is already set to 'smooth'.

-      requestAnimationFrame(() => {
        element.scrollIntoView({
          behavior: 'smooth',
          block: 'center'
        });
-      });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25c1dee and 9c2f64f.

📒 Files selected for processing (4)
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx (2 hunks)
  • web-server/src/components/Service/SystemLog/LogSearch.tsx (1 hunks)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx (1 hunks)
  • web-server/src/content/Service/SystemLogs.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web-server/src/components/Service/SystemLog/PlainLog.tsx
  • web-server/src/components/Service/SystemLog/LogSearch.tsx
  • web-server/src/components/Service/SystemLog/FormattedLog.tsx
🔇 Additional comments (10)
web-server/src/content/Service/SystemLogs.tsx (10)

3-10: Well-organized imports section with clean grouping

The imports section is now organized in a clean, logical pattern with React hooks grouped together and related components grouped below.


26-73: Good use of typed reducer for search state management

Implementing a reducer pattern with TypeScript types provides a more structured approach to managing complex search state. This addresses previous feedback about having "too many states trying to track a singular thing" and helps prevent bugs from unintentional state changes.


75-84: Component memoization improves performance

Wrapping the component with memo is a good optimization to prevent unnecessary re-renders when parent components change.


169-186: Good implementation of initial scroll to bottom

The effect properly handles scrolling to the bottom on initial load, with appropriate cleanup of the flag to prevent repeated scrolling. Use of requestAnimationFrame is appropriate here as it ensures the DOM has been painted before attempting to scroll.


188-201: Proper scroll listener implementation

This effect correctly adds and removes a scroll event listener to toggle the scroll down button visibility based on scroll position. The cleanup function properly removes the event listener on unmount.


203-228: Well-structured log rendering with memoization

The memoized renderLogs function efficiently renders logs and passes the necessary search context to child components. The addition of data-log-index attributes to each log div provides a clean way to reference specific logs.


237-242: Clean integration of LogSearch component

The LogSearch component is properly integrated with appropriate props for search functionality, navigation, and displaying match information.


249-255: Proper container ref implementation

The ref is now correctly attached to the scrollable container, fixing the previous issue where scroll logic would never fire. The container has appropriate styling for overflow and max height.


260-260: Updated scroll to bottom function

The click handler for the scroll button now uses the unified scrollToBottom function, which provides smooth scrolling behavior.


283-285: Clean export using memo pattern

The component is properly memoized and exported with a named export, which is a clean approach.

Comment on lines 138 to 167
useEffect(() => {
if (containerRef.current && !showScrollDownButton.value) {
scrollDown();
const { selectedIndex, elements } = searchState;
if (selectedIndex === null || !elements.length) {
setCurrentMatchLineIndex(null);
return;
}
}, [logs, showScrollDownButton.value]);

if (!serviceName)
return (
<SomethingWentWrong desc="Unspecified Service. This might be a product bug. Please contact us to let us know.;" />
);
const element = elements[selectedIndex];
if (!element) return;

let parentElement: HTMLElement | null = element;
while (parentElement && !parentElement.hasAttribute('data-log-index')) {
parentElement = parentElement.parentElement;
}

if (parentElement) {
const lineIndex = parseInt(
parentElement.getAttribute('data-log-index') || '-1',
10
);
setCurrentMatchLineIndex(lineIndex);

requestAnimationFrame(() => {
element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
});
}
}, [searchState.selectedIndex, searchState.elements, searchState]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove redundant dependency and optimize DOM traversal

The effect has redundant dependencies and uses DOM traversal to find parent elements.

  1. Remove the redundant searchState dependency:
-  }, [searchState.selectedIndex, searchState.elements, searchState]);
+  }, [searchState.selectedIndex, searchState.elements]);
  1. Consider storing log indices with the highlighted elements to avoid DOM traversal:
// In the search effect where you collect elements
const elements = Array.from(
  containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
) as HTMLElement[];

+ // Store log indices along with elements
+ const elementsWithIndices = elements.map(element => {
+   let parentElement: HTMLElement | null = element;
+   while (parentElement && !parentElement.hasAttribute('data-log-index')) {
+     parentElement = parentElement.parentElement;
+   }
+   const logIndex = parentElement ? 
+     parseInt(parentElement.getAttribute('data-log-index') || '-1', 10) : -1;
+   
+   return { element, logIndex };
+ });

- dispatch({ type: 'SET_MATCHES', payload: elements });
+ dispatch({ type: 'SET_MATCHES', payload: elementsWithIndices.map(e => e.element) });
+ // You'd need to modify your state to also store the indices

Then simplify your scroll effect to use the stored indices.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 148 to 157
let parentElement: HTMLElement | null = element;
while (parentElement && !parentElement.hasAttribute('data-log-index')) {
parentElement = parentElement.parentElement;
}

if (parentElement) {
const lineIndex = parseInt(
parentElement.getAttribute('data-log-index') || '-1',
10
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

DOM traversal for finding parent elements is brittle

Traversing the DOM to find parent elements with data attributes is not the most React-idiomatic approach and can be brittle if the DOM structure changes.

Consider refactoring to a more React-friendly approach by storing necessary information in your state rather than reading it from the DOM. Run this check to verify the DOM structure pattern:


🏁 Script executed:

#!/bin/bash
# Check the actual DOM structure of log entries and highlighted spans
echo "Looking for data-log-index attributes:"
rg -A 3 'data-log-index' web-server/src

echo -e "\nChecking where highlighted spans are created:"
rg -A 3 'mhq--highlighted' web-server/src

Length of output: 1569


Avoid brittle DOM traversal by passing the log index directly to your handlers

Rather than climbing the DOM tree and reading data-log-index, capture the index in your event handler signature. This decouples your React logic from the DOM structure and is both more robust and idiomatic.

• Location: web-server/src/content/Service/SystemLogs.tsx (around lines 148–157)
• Remove the while (parentElement …) loop and parseInt block
• Introduce a handler that accepts lineIndex: number and calls setCurrentMatchLineIndex(lineIndex)

Suggested refactor:

--- a/web-server/src/content/Service/SystemLogs.tsx
+++ b/web-server/src/content/Service/SystemLogs.tsx
@@ -140,16 +140,14 @@ export const SystemLogs: React.FC<Props> = ({ logs }) => {
     // Remove this DOM traversal:
-    let parentElement: HTMLElement | null = element;
-    while (parentElement && !parentElement.hasAttribute('data-log-index')) {
-      parentElement = parentElement.parentElement;
-    }
-
-    if (parentElement) {
-      const lineIndex = parseInt(
-        parentElement.getAttribute('data-log-index') || '-1',
-        10
-      );
-      setCurrentMatchLineIndex(lineIndex);
-    }
+    // Instead, receive the index directly:
+    const handleLogClick = (lineIndex: number) => {
+      setCurrentMatchLineIndex(lineIndex);
+    };
 
     // …
 
     return (
@@ -158,7 +156,7 @@ export const SystemLogs: React.FC<Props> = ({ logs }) => {
         {logs.map((log, index) => (
-          <div key={index} data-log-index={index}>
+          <div key={index} onClick={() => handleLogClick(index)}>
             {!parsedLog ? (
               <PlainLog log={log} />
             ) : (

This change:

  • Eliminates reliance on data-log-index and manual DOM traversal
  • Ensures React’s state drives UI behavior, making future structure changes less error-prone

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

85-105: 🛠️ Refactor suggestion

Add logs dependency to search effect

The search effect only depends on searchQuery, but if new logs arrive while searching, they won't be highlighted until the query changes.

-  }, [searchState.query]);
+  }, [searchState.query, logs]);

155-173: 🛠️ Refactor suggestion

Use a more React-friendly approach for finding log indices

The current approach of traversing the DOM to find parent elements with data attributes is brittle and not idiomatic React.

A better approach would be to store the log indices with the elements when you first collect them:

// In the search effect where you collect elements
const elements = Array.from(
  containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
) as HTMLElement[];

+ // Map elements to include their log indices
+ const elementsWithMetadata = elements.map(element => {
+   const logElement = element.closest('[data-log-index]');
+   const logIndex = logElement ? 
+     parseInt(logElement.getAttribute('data-log-index') || '-1', 10) : -1;
+   return { element, logIndex };
+ });

- dispatch({ type: 'SET_MATCHES', payload: elements });
+ // You'd need to modify your state to also store the indices
+ dispatch({ 
+   type: 'SET_MATCHES', 
+   payload: elementsWithMetadata.map(item => item.element)
+ });
+ // Store the indices separately or extend your state type

This would simplify your selection effect and make it more robust against DOM changes.

🧹 Nitpick comments (2)
web-server/src/content/Service/SystemLogs.tsx (2)

95-102: Remove unnecessary requestAnimationFrame

The requestAnimationFrame is redundant here because it's already wrapped in a setTimeout. The animation frame request doesn't add value since the timeout already defers execution.

    const timer = setTimeout(() => {
-      requestAnimationFrame(() => {
        const elements = Array.from(
          containerRef.current?.querySelectorAll('.mhq--highlighted-log') ?? []
        ) as HTMLElement[];

        dispatch({ type: 'SET_MATCHES', payload: elements });
-      });
    }, 300);

120-136: Simplify dependencies in handleNavigate

The searchState dependency is too broad and can cause unnecessary re-renders. You're only using specific properties from it.

  }, 
-    [searchState]
+    [searchState.elements, searchState.selectedIndex, searchState.isSearching]
  );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 9c2f64f and 70d1cd0.

📒 Files selected for processing (1)
  • web-server/src/content/Service/SystemLogs.tsx (3 hunks)
🔇 Additional comments (12)
web-server/src/content/Service/SystemLogs.tsx (12)

26-45: Good use of TypeScript for search state management

I like the clear type definitions for the search state and reducer actions. This makes the code more maintainable and type-safe.


47-73: Well-structured search reducer implementation

The reducer implementation cleanly handles different search actions with appropriate state updates. The approach of using a reducer for complex state management is appropriate here.


75-84: Modified component with memo and proper state setup

The use of memo for performance optimization is good. The new state structure with useReducer and useState provides a clear separation of concerns.


107-114: Smooth scroll implementation looks good

The scrollToBottom function is well implemented with smooth scrolling behavior.


138-153: Avoid DOM manipulation with requestAnimationFrame

The requestAnimationFrame here is appropriate for scrolling operations, but consider moving the element access logic into the effect where the selectedIndex changes.


175-192: Initial scroll behavior implementation looks good

The effect for handling initial load and scrolling to the bottom is well implemented.


194-207: Well-implemented scroll event listener

The scroll event listener is properly set up with cleanup on unmount and appropriate threshold detection for showing/hiding the scroll button.


209-238: Well-memoized log rendering function

The memoized rendering function with proper dependencies helps prevent unnecessary re-renders. The data attributes and event handlers are well organized.


247-252: Clean integration of LogSearch component

The LogSearch component is integrated well with appropriate props for search functionality and navigation.


259-266: Proper container ref placement

The container ref is now correctly placed on the scrollable element with appropriate styling.


270-270: Smooth scrollToBottom on button click

The scroll down button now correctly uses the smooth scrollToBottom function.


293-295: Proper component export with memo

Exporting the memoized component explicitly is a good practice for clarity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants