Skip to content

Conversation

hoophalab
Copy link
Contributor

@hoophalab hoophalab commented Aug 22, 2025

Description

This PR adds a filter box in the search tab panel. The user can filter the logs by entering a KQL query.
This PR adds a URL hash parameter for handling filtering.
image

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  1. Open an irv2 file
  2. Apply a KQL query in the search tab panel -- the filter is applied
  3. Change the log level in the status bar -- both the kql and level filtering are applied
  4. Change the url hash parameter -- the ui is correctly updated
    (The hash parameter needs to be in a uri encoded format)

Add an irv2 file to the public folder

  1. Access the file with the url hash parameter -- the parameter is correctly applied
    http://localhost:3010/?filePath=/test/example.clp.zst#filterString=message%3A+*zookeeper*

Summary by CodeRabbit

  • New Features

    • KQL filtering in the Search tab for supported structured logs: dedicated filter input, Filter button, Shift+Enter shortcut, contextual help and grouped inputs.
    • Sharing links now include both filter and subquery parameters.
  • UX/Style

    • Monospace input text, refined placeholders, full-width input containers and layout improvements.
  • Bug Fixes

    • Fixed textarea overflow/rounding issues.
  • Chores

    • Updated dependency clp-ffi-js to ^0.6.1.
    • Filtering now runs on file load and refreshes the current page and queries for up-to-date results.

@hoophalab hoophalab requested a review from a team as a code owner August 22, 2025 09:34
Copy link

coderabbitai bot commented Aug 22, 2025

Walkthrough

Adds KQL-aware filtering across UI, stores, URL hash, and decoder layers. Introduces a dedicated KQL filter input, updates hash params (query/subquery), aligns page reload/query restart after filtering, extends decoder APIs to accept kqlFilter, and updates state/store types to track fileTypeInfo and KQL filter values.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Bumps clp-ffi-js from ^0.6.0 to ^0.6.1.
App init and URL/hash flow
src/components/AppController.tsx, src/utils/url/urlHash.ts, src/utils/url/index.ts, src/typings/url.ts
Simplifies initial load to delegate to handleHashChange. Hash parsing now supports FILTER_STRING (query) and renames QUERY_STRING to subquery. getCursorFromHashParams defaults to LAST_EVENT. updateViewHashParams synchronizes KQL filter and triggers filtering + page load; updateQueryHashParams uses subquery. URL defaults include FILTER_STRING.
Search UI and inputs
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx, .../QueryInputBox.tsx, .../FilterInputBox.tsx, .../index.css, .../InputBox.css, .../QueryInputBox.css, .../FilterInputBox.css
Adds FilterInputBox component, groups query inputs when KQL enabled (CLP IR structured), changes QueryInputBox to use subquery hash param and dynamic placeholder, CSS refactors input container/decorator and overflow. FilterInputBox supports Shift+Enter, disabled states and end-button behavior.
Status bar filter control
src/components/StatusBar/LogLevelSelect/index.tsx
Refactors to read stores on-demand; on level change sets logLevelFilter, calls filterLogs(), reloads page by EVENT_NUM cursor and restarts query; errors routed to notifications; resets filter on file load.
Stores: view and log file
src/stores/viewStore/createViewFilterSlice.ts, src/stores/viewStore/types.ts, src/stores/logFileStore.ts
Adds ViewFilterValues (logLevelFilter, kqlFilter, kqlFilterInput) and setters; filterLogs() is now parameterless and calls logFileManagerProxy.setFilter(logLevelFilter, kqlFilter) then updates URL hash. Adds fileTypeInfo to log file store state.
Services and decoders
src/services/LogFileManager/index.ts, src/services/LogFileManagerProxy.ts, src/services/decoders/ClpIrDecoder/index.ts, src/services/decoders/JsonlDecoder/index.ts, src/typings/decoders.ts
setLogLevelFilter signature extended to accept (logLevelFilter, kqlFilter) across typings and implementations. LogFileManagerProxy.setFilter drops cursor param and no longer returns/loads a page. LogFileManager maps pages by chunk offset. ClpIrDecoder forwards kqlFilter and wraps errors; JsonlDecoder accepts kqlFilter and warns when non-empty.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant SearchTab as SearchTabPanel
  participant ViewStore as ViewStore (filter slice)
  participant Proxy as LogFileManagerProxy
  participant Manager as LogFileManager
  participant Decoder as Decoder
  participant UI as QueryStore

  rect #EEF5FF
    note right of SearchTab: Apply KQL filter (button or Shift+Enter)
    User->>SearchTab: Enter KQL + trigger
    SearchTab->>ViewStore: setKqlFilterInput()/setKqlFilter()
    SearchTab->>ViewStore: filterLogs()
  end

  ViewStore->>Proxy: setFilter(logLevelFilter, kqlFilter)
  Proxy->>Manager: setLogLevelFilter(logLevelFilter, kqlFilter)
  Manager->>Decoder: setLogLevelFilter(logLevelFilter, kqlFilter)
  Decoder-->>Manager: success / error
  Manager-->>Proxy: void
  ViewStore->>ViewStore: updateWindowUrlHashParams({query: kqlFilter})

  rect #F0FFF0
    note right of UI: Post-filter refresh
    ViewStore->>Manager: loadPageByCursor({EVENT_NUM, eventNum})
    ViewStore->>UI: startQuery()
  end

  alt Decoder error
    Decoder-->>ViewStore: throw
    ViewStore->>UI: handleErrorWithNotification
  end
Loading
sequenceDiagram
  autonumber
  participant App as AppController
  participant URL as urlHash utils
  participant ViewStore as ViewStore
  participant Manager as LogFileManager
  participant UI as QueryStore

  rect #FFF6EB
    App->>URL: getWindowUrlHashParams()
    App->>URL: handleHashChange()/updateViewHashParams()
    URL->>ViewStore: setKqlFilter(query) if changed
    URL->>ViewStore: filterLogs()
  end

  URL->>URL: getCursorFromHashParams()
  alt valid cursor
    URL->>Manager: loadPageByCursor(cursor)
  else no cursor
    URL-->>App: use LAST_EVENT cursor
  end

  opt subquery present
    URL->>ViewStore: setQueryString(subquery)
    URL->>UI: startQuery()
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@hoophalab hoophalab changed the title feat: Add KQL filtering support. feat: Add KQL query support for filtering structured IR log events. Aug 22, 2025
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: 27

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/services/decoders/utils.ts (1)

30-41: Update JSDoc to reflect new utcOffsetKey contract

The doc still says “log level key and timestamp key”, but the function now returns utcOffsetKey too. Please update the comment to prevent drift.

- * Parses the log level key and timestamp key from the decoder options.
+ * Parses the log level, timestamp, and UTC offset keys from the decoder options.
src/stores/logFileStore.ts (2)

35-39: Reset fileTypeInfo and numEvents during load to prevent stale UI state

When starting a new load, we don’t reset fileTypeInfo and numEvents. This can incorrectly keep KQL UI enabled (based on previous file) and display stale counts until the new file finishes loading.

Apply this diff to reset both fields when initiating a load:

         set({
           fileName: "Loading...",
           fileSrc: fileSrc,
           metadata: LOG_FILE_STORE_DEFAULT.metadata,
           onDiskFileSizeInBytes: LOG_FILE_STORE_DEFAULT.onDiskFileSizeInBytes,
+          fileTypeInfo: LOG_FILE_STORE_DEFAULT.fileTypeInfo,
+          numEvents: LOG_FILE_STORE_DEFAULT.numEvents,
         });

Also applies to: 47-54, 118-124


152-152: Possible null-dereference of fileInfo.fileTypeInfo

fileTypeInfo is nullable in state, but we unconditionally access fileInfo.fileTypeInfo.isStructured. If the loader ever returns null (e.g., unknown/unsupported type), this will throw.

Guard the dereference:

-        if (0 === decoderOptions.formatString.length && fileInfo.fileTypeInfo.isStructured) {
+        if (
+            0 === decoderOptions.formatString.length &&
+            null !== fileInfo.fileTypeInfo &&
+            true === fileInfo.fileTypeInfo.isStructured
+        ) {
             const {postPopUp} = useNotificationStore.getState();
             postPopUp(FORMAT_POP_UP_MESSAGE);
         }

Also applies to: 157-161

src/components/AppController.tsx (1)

46-53: Potential duplicate query triggers

filterLogs() already calls startQuery() (per the store slice). Here we call startQuery() again when updateQueryHashParams() is true, and we call handleHashChange() immediately after loadPageByCursor() on initial load. Depending on timing, the same query may start twice.

  • Option A: Let filterLogs() own query starting for filter-driven changes; restrict updateQueryHashParams() branch to non-filter query params.
  • Option B: Make filterLogs() not call startQuery() and centralize query starting here.

If you want, I can prepare a focused refactor patch after you confirm the preferred ownership.

Also applies to: 122-123

src/services/LogFileManager/index.ts (1)

61-63: Update UI call to setLogLevelFilter with the new kqlFilter parameter

The decoder interface, all implementations (JsonlDecoder, ClpIrDecoder) and backend call sites (LogFileManager, LogFileManagerProxy) already use the two-argument signature setLogLevelFilter(logLevelFilter, kqlFilter). However, in your status-bar component you’re still invoking it with a single argument, which will pass undefined for kqlFilter and break the new arity.

Please update:

  • File: src/components/StatusBar/LogLevelSelect/index.tsx
    Lines: ~180–183
    Before:
    // only passing the LogLevelFilter array (or null)
    setLogLevelFilter(
      0 === logLevels.length ? null : logLevels
    );
    filterLogs();
    After (example):
    // now passing both filters; use empty string or stored KQL
    setLogLevelFilter(
      0 === logLevels.length ? null : logLevels,
      ''  // or your current KQL filter
    );
    filterLogs();

Remaining decoder implementations and call sites already conform to the new signature. Once the UI invocation is updated, all calls will have the correct arity and runtime errors will be avoided.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 61bd41f and c2c4c24.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • package.json (1 hunks)
  • src/components/AppController.tsx (2 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (5 hunks)
  • src/components/StatusBar/LogLevelSelect/index.tsx (1 hunks)
  • src/services/LogFileManager/index.ts (1 hunks)
  • src/services/LogFileManagerProxy.ts (1 hunks)
  • src/services/decoders/ClpIrDecoder/index.ts (1 hunks)
  • src/services/decoders/JsonlDecoder/index.ts (2 hunks)
  • src/services/decoders/utils.ts (2 hunks)
  • src/stores/logFileStore.ts (3 hunks)
  • src/stores/viewStore/createViewFilterSlice.ts (1 hunks)
  • src/stores/viewStore/types.ts (1 hunks)
  • src/typings/decoders.ts (1 hunks)
  • src/typings/tab.ts (1 hunks)
  • src/typings/url.ts (2 hunks)
  • src/utils/url/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • src/typings/tab.ts
  • src/services/decoders/ClpIrDecoder/index.ts
  • src/components/AppController.tsx
  • src/components/StatusBar/LogLevelSelect/index.tsx
  • src/services/decoders/utils.ts
  • src/typings/url.ts
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
  • src/utils/url/index.ts
  • src/services/decoders/JsonlDecoder/index.ts
  • src/stores/logFileStore.ts
  • src/typings/decoders.ts
  • src/services/LogFileManagerProxy.ts
  • src/services/LogFileManager/index.ts
  • src/stores/viewStore/types.ts
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx
  • src/stores/viewStore/createViewFilterSlice.ts
🧠 Learnings (8)
📚 Learning: 2024-11-14T04:35:00.985Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css:14-18
Timestamp: 2024-11-14T04:35:00.985Z
Learning: In `src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css`, the use of `!important` flags is necessary for overriding styles in the `.query-input-box` class.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.css:6-8
Timestamp: 2024-10-08T15:52:50.753Z
Learning: In this codebase, using `!important` is acceptable when overriding styles from JoyUI components.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/LogLevelChip.css:6-6
Timestamp: 2024-10-08T15:52:50.753Z
Learning: When overriding JoyUI's default styling, it's acceptable to use `!important` in CSS in this project.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
📚 Learning: 2024-09-28T01:00:02.202Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#74
File: new-log-viewer/src/components/CentralContainer/PanelTabs/TooltipTab.css:0-0
Timestamp: 2024-09-28T01:00:02.202Z
Learning: Using `!important` is acceptable when overriding JoyUI styles in this project.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#74
File: new-log-viewer/src/components/CentralContainer/PanelTabs/TooltipTab.css:0-0
Timestamp: 2024-10-08T15:52:50.753Z
Learning: When overriding JoyUI styles, `!important` may be necessary to ensure custom styles take precedence.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
📚 Learning: 2025-06-01T13:41:12.938Z
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx:37-40
Timestamp: 2025-06-01T13:41:12.938Z
Learning: The `updateWindowUrlHashParams` function in `src/utils/url.ts` doesn't throw errors, so error handling is not needed when calling this function.

Applied to files:

  • src/components/AppController.tsx
📚 Learning: 2025-06-01T13:40:12.222Z
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/components/Editor/index.tsx:138-139
Timestamp: 2025-06-01T13:40:12.222Z
Learning: In the yscope-log-viewer codebase, when using Zustand stores in React components, the preferred pattern is to use `getState()` for static setters that never change (like `setLogEventNum`) to avoid unnecessary subscriptions, while using hooks for actions that do more than just setting values. All store state variables should be declared at the beginning of the component for consistency and clear dependency overview.

Applied to files:

  • src/components/StatusBar/LogLevelSelect/index.tsx
📚 Learning: 2024-10-28T18:40:29.816Z
Learnt from: Henry8192
PR: y-scope/yscope-log-viewer#107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx:1-7
Timestamp: 2024-10-28T18:40:29.816Z
Learning: In the y-scope/yscope-log-viewer project, importing components via destructuring from 'mui/joy' is acceptable, as the package size impact has been analyzed and found acceptable.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
🧬 Code graph analysis (14)
src/services/decoders/ClpIrDecoder/index.ts (1)
src/services/decoders/JsonlDecoder/index.ts (1)
  • logLevelFilter (229-243)
src/components/AppController.tsx (1)
src/utils/url/index.ts (2)
  • getWindowUrlHashParams (383-383)
  • updateWindowUrlHashParams (389-389)
src/components/StatusBar/LogLevelSelect/index.tsx (3)
src/services/LogFileManager/index.ts (1)
  • setLogLevelFilter (193-198)
src/services/decoders/ClpIrDecoder/index.ts (1)
  • setLogLevelFilter (102-111)
src/services/decoders/JsonlDecoder/index.ts (1)
  • setLogLevelFilter (89-96)
src/services/decoders/utils.ts (1)
src/typings/formatters.ts (1)
  • ParsedKey (127-127)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (2)
src/services/LogFileManager/index.ts (1)
  • fileTypeInfo (119-121)
src/utils/url/index.ts (2)
  • copyPermalinkToClipboard (380-380)
  • URL_HASH_PARAMS_DEFAULT (391-391)
src/utils/url/index.ts (1)
src/typings/url.ts (1)
  • HASH_PARAM_NAMES (41-41)
src/services/decoders/JsonlDecoder/index.ts (1)
src/typings/logs.ts (1)
  • LogLevelFilter (51-51)
src/stores/logFileStore.ts (2)
src/typings/common.ts (1)
  • Nullable (6-6)
src/typings/file.ts (1)
  • FileTypeInfo (54-54)
src/typings/decoders.ts (2)
src/services/decoders/JsonlDecoder/index.ts (1)
  • logLevelFilter (229-243)
src/typings/logs.ts (1)
  • LogLevelFilter (51-51)
src/services/LogFileManagerProxy.ts (2)
src/services/decoders/JsonlDecoder/index.ts (1)
  • logLevelFilter (229-243)
src/typings/logs.ts (1)
  • LogLevelFilter (51-51)
src/services/LogFileManager/index.ts (1)
src/services/decoders/JsonlDecoder/index.ts (1)
  • logLevelFilter (229-243)
src/stores/viewStore/types.ts (1)
src/typings/logs.ts (1)
  • LogLevelFilter (51-51)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (1)
src/typings/states.ts (1)
  • UI_ELEMENT (105-105)
src/stores/viewStore/createViewFilterSlice.ts (3)
src/stores/viewStore/types.ts (3)
  • ViewFilterValues (65-65)
  • ViewState (70-70)
  • ViewFilterSlice (64-64)
src/utils/url/index.ts (1)
  • updateWindowUrlHashParams (389-389)
src/services/LogFileManagerProxy.ts (1)
  • startQuery (68-71)
🔇 Additional comments (12)
package.json (1)

35-35: Update and commit the lockfile for clp-ffi-js@^0.6.1

  • Verified that streamReader.filterLogEvents is consistently invoked with two parameters (logLevelFilter, kqlFilter) in
    src/services/decoders/ClpIrDecoder/index.ts:104.
  • Confirmed that the npm “latest” release of clp-ffi-js is v0.6.1, matching the bumped range .

No API-breaking changes were introduced in v0.6.1. Please regenerate and commit your lockfile (e.g. package-lock.json or yarn.lock) so CI installs the exact version.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css (1)

22-25: LGTM: end‑decorator overrides are appropriate

The display block and margin reset align with prior JoyUI overrides in this codebase.

src/stores/viewStore/types.ts (2)

57-58: Type composition looks good

ViewFilterSlice as a composition of values and actions matches the established pattern in this store.


65-65: Export surface addition is appropriate

Re-exporting ViewFilterValues is consistent with the other slices and enables consumers to import the shape directly.

src/typings/url.ts (1)

9-9: New hash param name is clear and consistent

FILTER_STRING = "filterString" follows the existing naming convention alongside queryString, etc.

src/utils/url/index.ts (1)

27-27: Default value addition is correct

Defaulting FILTER_STRING to "" matches the removal-on-default behaviour and keeps permalinks concise.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (1)

105-119: UI composition for filter→search flow looks solid

The conditional render cleanly introduces the filter box and visual cue. Props wiring and layout are clear.

src/stores/logFileStore.ts (1)

142-151: fileTypeInfo is always returned by LogFileManagerProxy.loadFile

I’ve verified that every code path populates fileTypeInfo:

  • decodeUtils.ts always returns an object with fileTypeInfo: FileTypeInfo
  • LogFileManager.create passes fileTypeInfo into the LogFileManager instance, which exposes it via its getter
  • LogFileManagerProxy.loadFile then unconditionally includes fileTypeInfo: logFileManager.fileTypeInfo in its returned LogFileInfo

Since fileTypeInfo cannot be omitted, no changes are needed here.

src/components/AppController.tsx (1)

96-104: Lifecycle handling LGTM

Listener registration/cleanup and the one-time init guard are correct and tidy.

Also applies to: 126-129

src/services/LogFileManager/index.ts (1)

243-296: Query and page-loading paths remain correct

The filtered map gating and page computations properly account for filtering; equality checks avoid ! which matches repo guidelines. No issues spotted.

Also applies to: 308-338

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (1)

21-35: Component wiring looks good

State selection via Zustand selectors and side-effect-free callbacks via getState() are appropriate here. CSS class hooks look consistent with the surrounding tab panel.

Also applies to: 57-63

src/stores/viewStore/createViewFilterSlice.ts (1)

62-67: Setter semantics look right

Resetting filterApplied to false when either filter changes is correct and keeps the button state meaningful.

@Henry8192 Henry8192 self-requested a review August 22, 2025 20:51
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

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: 3

♻️ Duplicate comments (4)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (4)

16-20: Clarify and complete the JSDoc (returns, side-effects).

The docstring is concise but incomplete. Specify what this component returns and its side-effects to aid maintainers and tooling.

-/**
- * Provides a text input to apply a KQL filter.
- *
- * @return
- */
+/**
+ * Text input and action to apply a KQL (Kusto) filter to structured IR logs.
+ *
+ * Side effects:
+ * - Updates viewStore.kqlFilter as the user types.
+ * - Invokes viewStore.filterLogs() when the Filter button is clicked.
+ *
+ * @returns JSX.Element
+ */

37-37: Avoid coupling: use a distinct UI_ELEMENT key for the filter input.

This uses UI_ELEMENT.QUERY_INPUT_BOX to gate the filter input’s disabled state, coupling it to the unrelated “query” input. Introduce UI_ELEMENT.FILTER_INPUT_BOX and update the UI state grid so the filter input can be managed independently.

Apply locally in this file:

-    const isFilterInputBoxDisabled = isDisabled(uiState, UI_ELEMENT.QUERY_INPUT_BOX);
+    const isFilterInputBoxDisabled = isDisabled(uiState, UI_ELEMENT.FILTER_INPUT_BOX);

Follow-ups needed outside this file:

  • Add FILTER_INPUT_BOX to enum UI_ELEMENT (src/typings/states.ts) and wire it through the UI state grid with appropriate defaults per state.
  • Replace any accidental uses of QUERY_INPUT_BOX for the KQL filter with FILTER_INPUT_BOX.

Run to verify enum/key usage:

#!/bin/bash
# Show UI_ELEMENT enum and check for FILTER_INPUT_BOX presence and usage
rg -n -C3 '\benum\s+UI_ELEMENT\b' src
rg -n '\bFILTER_INPUT_BOX\b' src || true
rg -n '\bQUERY_INPUT_BOX\b' src | rg -n 'FilterInputBox|SearchTabPanel|states\.ts|uiStore'

40-56: Accessibility and minor UX polish: add aria labels, remove stray whitespace, set type="button".

  • Add aria-labels for Textarea and Button to improve screen reader support.
  • Remove the stray {" "} before “Filter”.
  • Explicitly set type="button" to avoid accidental form submission.
         <Textarea
             className={"filter-input-box"}
             maxRows={7}
             placeholder={"KQL filter"}
+            aria-label={"KQL filter"}
             size={"sm"}
             value={filterString}
             endDecorator={
                 <Button
                     className={"filter-button"}
                     disabled={filterApplied || isFilterInputBoxDisabled}
                     variant={"soft"}
+                    type={"button"}
+                    aria-label={"Apply filter"}
                     onClick={handleButtonClick}
                 >
-                    {" "}
                     Filter
                 </Button>
             }

57-61: Prefer component-level disabled for Joy Textarea; keep slotProps for styling only.

Passing disabled via slotProps.textarea can miss full disabled styling/behaviour. Set disabled on Textarea and leave slotProps for classNames.

-            slotProps={{textarea: {
-                disabled: isFilterInputBoxDisabled,
-                className: "filter-input-box-textarea",
-            },
+            disabled={isFilterInputBoxDisabled}
+            slotProps={{textarea: {
+                className: "filter-input-box-textarea",
+            },
             endDecorator: {className: "filter-input-box-end-decorator"}}}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c39c378 and e6acd7b.

📒 Files selected for processing (1)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx
🧠 Learnings (1)
📚 Learning: 2024-11-14T04:35:00.985Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css:14-18
Timestamp: 2024-11-14T04:35:00.985Z
Learning: In `src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css`, the use of `!important` flags is necessary for overriding styles in the `.query-input-box` class.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx
🧬 Code graph analysis (1)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (1)
src/typings/states.ts (1)
  • UI_ELEMENT (105-105)

Comment on lines 21 to 35
const FilterInputBox = () => {
const filterApplied = useViewStore((state) => state.isFilterApplied);
const filterString = useViewStore((state) => state.kqlFilter);
const uiState = useUiStore((state) => state.uiState);

const handleFilterInputChange = useCallback((ev: React.ChangeEvent<HTMLTextAreaElement>) => {
const newFilterString = ev.target.value;
const {setKqlFilter} = useViewStore.getState();
setKqlFilter(newFilterString);
}, []);

const handleButtonClick = useCallback(() => {
const {filterLogs} = useViewStore.getState();
filterLogs();
}, []);
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Testing gaps: add a component test for disabled state and action dispatch.

Add tests to cover:

  • Textarea disabled when UI_ELEMENT.FILTER_INPUT_BOX is disabled.
  • Button disabled when either isFilterApplied or input is disabled.
  • Clicking Button dispatches filterLogs().

I can provide a Jest + RTL test scaffold that mocks useViewStore/useUiStore and asserts these behaviours. Would you like me to open a follow-up PR with tests?

🤖 Prompt for AI Agents
In
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx
around lines 21 to 35, add component tests that verify the disabled state and
action dispatches: write Jest+React Testing Library tests that mock useViewStore
and useUiStore (provide controlled values for isFilterApplied, kqlFilter, and
uiState with UI_ELEMENT.FILTER_INPUT_BOX enabled/disabled), assert the textarea
is disabled when uiState disables FILTER_INPUT_BOX, assert the button is
disabled when either isFilterApplied is true or the textarea is disabled, and
assert clicking the button calls the mocked filterLogs() function (and changing
the textarea value calls setKqlFilter with the new value); use spies/mocked
implementations for setKqlFilter and filterLogs, render the FilterInputBox
component, and use fireEvent/userEvent to simulate input and clicks to validate
behavior.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css (1)

19-21: Ensure the textarea flexes; width: 0 alone can collapse unexpectedly

If the textarea is a flex child, consider explicitly allowing it to grow/shrink and add min-width: 0 to avoid overflow when end decorators (e.g., Filter button) are present.

 .query-input-box-textarea {
-    width: 0;
+    width: 0;         /* keep to enable shrink */
+    min-width: 0;     /* prevent overflow in narrow layouts */
+    flex: 1 1 auto;   /* ensure it expands to available space */
 }

Please confirm the parent container is a flex box (looks like it is via .query-input-box { flex-direction: row !important; }) so these take effect.

♻️ Duplicate comments (5)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css (1)

2-2: Add !important and layout-safe props to withstand JoyUI inline styles

Per our prior learnings for JoyUI overrides and the earlier review on this exact rule, use !important and add layout guards to prevent overflow when rendered alongside the new Filter box. I’m re-raising since the change still shows width: 100% without !important.

 .query-input-box-with-progress {
-    width: 100%;
+    width: 100% !important;
+    min-width: 0;           /* allow shrink inside flex parents */
+    box-sizing: border-box; /* avoid overflow from padding/borders */
 }

Note: This recommendation leverages retrieved learnings that !important is acceptable here to override JoyUI styles.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css (3)

1-6: Strengthen container sizing to reliably override JoyUI and prevent overflow

Add the stronger override and flex‑shrink fix to avoid horizontal overflow in tight layouts. This follows prior learnings allowing !important for JoyUI overrides.

 .filter-input-box {
     display: flex;
     flex-direction: row !important;
-    width: 100%;
+    width: 100% !important;
+    min-width: 0;
+    box-sizing: border-box;
     border-radius: 0 !important;
 }

8-11: Don’t split KQL/code tokens mid‑word; switch to modern wrapping + better monospace stack

Improves readability for long identifiers/operators while still allowing wrap.

 .filter-input-box-textarea {
-    font-family: monospace !important;
-    word-break: break-all;
+    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
+                 "Courier New", monospace !important;
+    white-space: pre-wrap;
+    overflow-wrap: anywhere;
+    word-break: normal;
 }

13-15: Avoid fixed button width; allow label/icon growth and keep a11y focus visible

Use a minimum width instead of a hard width; add a visible focus style.

 .filter-button {
-    width: 48px;
+    min-width: 48px;
+    width: auto;
 }

Additional (outside the selected lines):

.filter-button:focus-visible {
  outline: 2px solid var(--joy-palette-primary-500, #1976d2);
  outline-offset: 2px;
}
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (1)

14-17: Use container gap instead of icon side margins; it’s RTL‑friendly and simpler

Rely on gap for consistent spacing between children; remove per‑item margins on the icon.

 .arrow-right-icon {
-    margin-right: 2px;
-    margin-left: 2px;
 }
 
 .query-input-box-container {
     display: flex;
     flex-direction: row;
     align-items: center;
 
     margin-top: 10px;
     margin-bottom: 10px;
+    gap: 4px;
 }

Also applies to: 19-26

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e6acd7b and 14874a7.

📒 Files selected for processing (3)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css (1 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-11-14T04:35:00.985Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css:14-18
Timestamp: 2024-11-14T04:35:00.985Z
Learning: In `src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css`, the use of `!important` flags is necessary for overriding styles in the `.query-input-box` class.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/index.css:6-8
Timestamp: 2024-10-08T15:52:50.753Z
Learning: In this codebase, using `!important` is acceptable when overriding styles from JoyUI components.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#77
File: new-log-viewer/src/components/StatusBar/LogLevelSelect/LogLevelChip.css:6-6
Timestamp: 2024-10-08T15:52:50.753Z
Learning: When overriding JoyUI's default styling, it's acceptable to use `!important` in CSS in this project.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
📚 Learning: 2024-10-08T15:52:50.753Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#74
File: new-log-viewer/src/components/CentralContainer/PanelTabs/TooltipTab.css:0-0
Timestamp: 2024-10-08T15:52:50.753Z
Learning: When overriding JoyUI styles, `!important` may be necessary to ensure custom styles take precedence.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
📚 Learning: 2024-09-28T01:00:02.202Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#74
File: new-log-viewer/src/components/CentralContainer/PanelTabs/TooltipTab.css:0-0
Timestamp: 2024-09-28T01:00:02.202Z
Learning: Using `!important` is acceptable when overriding JoyUI styles in this project.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.css
🔇 Additional comments (1)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css (1)

17-20: End decorator overrides look fine

display: block !important with zero top margin is a reasonable override for JoyUI’s endDecorator defaults given the surrounding flex layout.

}

enum HASH_PARAM_NAMES {
FILTER_STRING = "query",
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we keep the MACRO_NAME in consistant with the actual parameter name? Or there's a specific reason we should differenciate the two.

Copy link
Contributor Author

@hoophalab hoophalab Aug 25, 2025

Choose a reason for hiding this comment

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

Yeah, this is a tricky issue. Kirk said he wanted to rename queryString param as subquery and name the new filter string param as query.
But that will require a big refactor in the code base. For example, we need to rename QueryStore to subQueryStore.
I guess it's ok to name the new param as filter string temporarily to avoid conflict with names such as QueryStore and queryString, and plan the refactoring later.
#371 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Just some thoughts, maybe we could rename queryString term to search string? We can discuss whether if that's confusing from query.

Comment on lines +89 to +93
setLogLevelFilter (logLevelFilter: LogLevelFilter, kqlFilter: string): boolean {
if ("" !== kqlFilter) {
console.warn("KQL filtering isn't supported for JSONL logs;" +
"ignoring the provided filter.");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Shall we also add this to our newly added plain text filter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add that in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess no code change is required because plaintextdecode has a prototype of setLogLevelFilter (), which ignores all parameters

Copy link
Contributor

Choose a reason for hiding this comment

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

We can also omit the kql parameter here, without any warning though. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

like setLogLevelFilter (logLevelFilter: LogLevelFilter): boolean {...}

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@junhaoliao
Copy link
Member

image

@hoophalab @kirkrodrigues i'm still a bit uncertain whether the input boxes' layout would be intuitive to any first time users. do you think these can be better designs?

  1. Adding icons (with tooltips) in front of the input boxes:
    image
  2. Adding icons (with tooltips) as start decorators of the input boxes:
    image

@hoophalab
Copy link
Contributor Author

@hoophalab @kirkrodrigues i'm still a bit uncertain whether the input boxes' layout would be intuitive to any first time users. do you think these can be better designs?

I have no preference between the current one and both new design

@junhaoliao

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as duplicate.

[TAB_NAME.DOCUMENTATION]: "Documentation",
[TAB_NAME.FILE_INFO]: "File info",
[TAB_NAME.SEARCH]: "Search",
[TAB_NAME.SEARCH]: "Filter and search",

This comment was marked as resolved.

@junhaoliao
Copy link
Member

image @hoophalab @kirkrodrigues i'm still a bit uncertain whether the input boxes' layout would be intuitive to any first time users. do you think these can be better designs?
  1. Adding icons (with tooltips) in front of the input boxes:
    image
  2. Adding icons (with tooltips) as start decorators of the input boxes:
    image

Alternatively, we can split the search panel into two sections - one for filter and one for search. then we can display a Found {} matches out of {} messages status message right below the input box to indicate the filter query is successful. i do think adding proper projection support & merging the log level filter with this kql filter can be of higher priorities so i'm fine delaying this till another PR

@hoophalab
Copy link
Contributor Author

Alternatively, we can split the search panel into two sections - one for filter and one for search. then we can display a Found {} matches out of {} messages status message right below the input box to indicate the filter query is successful. i do think adding proper projection support & merging the log level filter with this kql filter can be of higher priorities so i'm fine delaying this till another PR

lol we discussed this in the standup and decide to change the placeholder for now... We may need to reconsider the ui in another pr

coderabbitai[bot]

This comment was marked as resolved.

Copy link
Member

@junhaoliao junhaoliao left a comment

Choose a reason for hiding this comment

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

here's my final batch of comments. as discussed offline, we can rework the query boxes in the future when we have better ideas about how they can be organized

the PR title lgtm

@hoophalab hoophalab requested a review from junhaoliao September 3, 2025 17:46
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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/stores/viewStore/createViewPageSlice.ts (1)

98-109: Ensure filterLogs’ async behaviour is handled to prevent duplicate or out-of-order paging
filterLogs currently returns void and launches an un-awaited IIFE. In the RELOAD path this can race with loadPageByCursor. Either:

  • Refactor filterLogs to return a Promise<void> and call await filterLogs() before loadPageByCursor()
  • Or move the cursor-paging logic into filterLogs and remove the explicit loadPageByCursor here
♻️ Duplicate comments (5)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (1)

2-3: Scope overflow to x-axis only (matches comment and avoids vertical clipping)

The comment says overflow-x, but the rule hides both axes.

-    /* We apply overflow-x here because JoyUI has a rounding issue when calculating the Textarea width, causing it to overflow its container. */
-    overflow: hidden;
+    /* We apply overflow-x here because JoyUI has a rounding issue when calculating the Textarea width, causing it to overflow its container. */
+    overflow-x: hidden;
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (4)

64-66: Remove redundant store writes in share handler

These lines re-set current values and create unnecessary state churn.

-        setQueryIsCaseSensitive(queryIsCaseSensitive);
-        setQueryIsRegex(queryIsRegex);
-        setQueryString(queryString);

68-74: Confirm canonical URL param naming (PR says filterString; code uses query/subquery)

Pick one scheme and apply consistently across url helpers, stores, and components. If adopting filterString for the KQL filter, update here accordingly.

-            query: kqlFilter,
+            filterString: kqlFilter,
             queryIsCaseSensitive: queryIsCaseSensitive,
             queryIsRegex: queryIsRegex,
             subquery: queryString,
#!/bin/bash
# Audit occurrences to align naming across the codebase
rg -nP -C2 '(filterString|query|subquery)\s*:' src
rg -nP -C3 'getWindowUrlHashParams|updateWindowUrlHashParams|copyPermalinkToClipboard' src/utils

77-80: Avoid brittle decoder name checks; gate via a capability or a utility

String-matching "CLP IR" couples UI to a specific decoder. Prefer a supportsKqlFiltering flag from fileTypeInfo or centralize the predicate.

-    const isKqlFilteringEnabled = null !== fileTypeInfo &&
-        "CLP IR" === fileTypeInfo.name &&
-        true === fileTypeInfo.isStructured;
+    const isKqlFilteringEnabled = supportsKqlFiltering(fileTypeInfo);

Add once in this file (outside the hunk):

type FileTypeInfo = { name: string; isStructured: boolean; supportsKqlFiltering?: boolean } | null;

const supportsKqlFiltering = (info: FileTypeInfo): boolean =>
    null !== info && (true === info.supportsKqlFiltering ||
    (true === info.isStructured && "CLP IR" === info.name));

62-74: Permalink should reflect the applied filter, not the in-progress input

Use kqlFilter (applied) instead of kqlFilterInput (draft) so shared URLs reproduce the active state.

-        const {kqlFilterInput} = useViewStore.getState();
+        const {kqlFilter} = useViewStore.getState();

         copyPermalinkToClipboard({}, {
             logEventNum: URL_HASH_PARAMS_DEFAULT.logEventNum,
-            query: kqlFilterInput,
+            query: kqlFilter,
             queryIsCaseSensitive: queryIsCaseSensitive,
             queryIsRegex: queryIsRegex,
             subquery: queryString,
         });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 53a9036 and cd950f5.

📒 Files selected for processing (5)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (2 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (5 hunks)
  • src/components/DropFileContainer/index.tsx (1 hunks)
  • src/components/MenuBar/index.tsx (1 hunks)
  • src/stores/viewStore/createViewPageSlice.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • src/components/MenuBar/index.tsx
  • src/components/DropFileContainer/index.tsx
  • src/stores/viewStore/createViewPageSlice.ts
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
🧠 Learnings (2)
📚 Learning: 2025-05-09T01:07:32.803Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#224
File: src/contexts/states/viewStore.ts:108-109
Timestamp: 2025-05-09T01:07:32.803Z
Learning: For the viewStore in the yscope-log-viewer project, it's more appropriate to suppress the max-lines-per-function ESLint rule than to split the store into slices due to the tight coupling and cohesive nature of its functions.

Applied to files:

  • src/stores/viewStore/createViewPageSlice.ts
📚 Learning: 2024-11-14T04:35:00.985Z
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#107
File: src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css:14-18
Timestamp: 2024-11-14T04:35:00.985Z
Learning: In `src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css`, the use of `!important` flags is necessary for overriding styles in the `.query-input-box` class.

Applied to files:

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
🧬 Code graph analysis (1)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (3)
src/services/LogFileManager/index.ts (1)
  • fileTypeInfo (119-121)
src/utils/url/index.ts (2)
  • copyPermalinkToClipboard (380-380)
  • URL_HASH_PARAMS_DEFAULT (391-391)
src/typings/tab.ts (2)
  • TAB_NAME (22-22)
  • TAB_DISPLAY_NAMES (21-21)
🔇 Additional comments (2)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.css (1)

14-18: LGTM: stacked inputs with gap improves layout and semantics

The vertical wrapper with gap: 8px is a clear upgrade over spacer divs and per-item margins.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (1)

121-128: LGTM: grouped two-input layout is clear and fixes prior overflow concerns

The wrapper with class query-input-boxes-container is a solid, accessible structure and aligns with the CSS gap approach.

Comment on lines +87 to +100
{isKqlFilteringEnabled &&
<PanelTitleButton
title={"KQL query syntax"}
>
<Link
color={"neutral"}
href={"https://docs.yscope.com/clp/main/user-docs/reference-json-search-syntax.html"}
level={"body-sm"}
rel={"noopener"}
target={"_blank"}
>
<HelpOutlineIcon/>
</Link>
</PanelTitleButton>}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

A11y and security: add accessible name and noreferrer; hide decorative icon

Give the link an accessible label, add noreferrer, and mark the icon decorative.

-                        <Link
+                        <Link
                             color={"neutral"}
                             href={"https://docs.yscope.com/clp/main/user-docs/reference-json-search-syntax.html"}
                             level={"body-sm"}
-                            rel={"noopener"}
+                            rel={"noopener noreferrer"}
                             target={"_blank"}
+                            aria-label={"KQL query syntax (opens in a new tab)"}
                         >
-                            <HelpOutlineIcon/>
+                            <HelpOutlineIcon aria-hidden={true}/>
                         </Link>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{isKqlFilteringEnabled &&
<PanelTitleButton
title={"KQL query syntax"}
>
<Link
color={"neutral"}
href={"https://docs.yscope.com/clp/main/user-docs/reference-json-search-syntax.html"}
level={"body-sm"}
rel={"noopener"}
target={"_blank"}
>
<HelpOutlineIcon/>
</Link>
</PanelTitleButton>}
{isKqlFilteringEnabled &&
<PanelTitleButton
title={"KQL query syntax"}
>
<Link
color={"neutral"}
href={"https://docs.yscope.com/clp/main/user-docs/reference-json-search-syntax.html"}
level={"body-sm"}
rel={"noopener noreferrer"}
target={"_blank"}
aria-label={"KQL query syntax (opens in a new tab)"}
>
<HelpOutlineIcon aria-hidden={true}/>
</Link>
</PanelTitleButton>}
🤖 Prompt for AI Agents
In src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
around lines 87-100, the Link lacks an accessible name, the rel attribute should
include "noreferrer" for security, and the icon must be marked decorative;
update the Link to include an explicit accessible label (e.g. aria-label="KQL
query syntax (opens in new tab)"), add noreferrer to the rel prop (rel="noopener
noreferrer"), and mark the HelpOutlineIcon decorative (e.g. aria-hidden="true"
or focusable={false}) so it isn't announced by assistive tech.

Comment on lines +74 to 78
const {filterLogs} = useViewStore.getState();
filterLogs();
const {loadPageByCursor} = useViewStore.getState();
await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});
})().catch(handleErrorWithNotification);
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

Avoid double-loads: clarify filterLogs side effects and ordering.

If filterLogs() triggers paging (URL update + loadPageByCursor), the explicit loadPageByCursor({code: LAST_EVENT}) right after it can cause overlapping page fetches and flicker. If it doesn't, and filterLogs performs async work, calling it without await risks a race where paging uses stale filters.

  • Pick one contract and enforce it:
    • A) filterLogs applies filters AND performs paging → remove the explicit loadPageByCursor here.
    • B) filterLogs only updates filter state (no paging) → ensure it’s either synchronous or returns Promise<void> and is awaited before paging.

Option A diff:

-            const {filterLogs} = useViewStore.getState();
-            filterLogs();
-            const {loadPageByCursor} = useViewStore.getState();
-            await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});
+            const {filterLogs} = useViewStore.getState();
+            filterLogs(); // responsible for paging internally

Option B diff (if filterLogs is async/no paging):

-            const {filterLogs} = useViewStore.getState();
-            filterLogs();
+            const {filterLogs} = useViewStore.getState();
+            await filterLogs();
             const {loadPageByCursor} = useViewStore.getState();
             await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});

🏁 Script executed:

#!/bin/bash
set -e

# Locate the definition of useViewStore
rg -n "useViewStore" -C3 --glob "*.ts" --glob "*.tsx"

# Inspect filterLogs implementation
rg -n "filterLogs" -C3 --glob "*.ts" --glob "*.tsx"

# Inspect loadPageByCursor implementation
rg -n "loadPageByCursor" -C3 --glob "*.ts" --glob "*.tsx"

Length of output: 38424


#!/bin/bash
rg -n "logFileManagerProxy" -C5 src/stores/viewStore/createViewFilterSlice.ts

🏁 Script executed:

#!/bin/bash
set -e

# Print the full filterLogs implementation including its async IIFE
rg -nP -C20 '^ *filterLogs:' -C50 src/stores/viewStore/createViewFilterSlice.ts

# Search for any loadPageByCursor calls inside filterLogs
rg -n 'loadPageByCursor' -C5 src/stores/viewStore/createViewFilterSlice.ts

Length of output: 1625


Await filterLogs before paging
filterLogs schedules its async work in an IIFE and returns immediately, so paging may run with stale filters—change it to return a Promise and call await filterLogs() before loadPageByCursor.

-            const {filterLogs} = useViewStore.getState();
-            filterLogs();
+            const {filterLogs} = useViewStore.getState();
+            await filterLogs();
             const {loadPageByCursor} = useViewStore.getState();
             await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});

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

🤖 Prompt for AI Agents
In src/components/DropFileContainer/index.tsx around lines 74 to 78, the call to
filterLogs() is not awaited but filterLogs performs async work in an IIFE and
returns immediately, causing loadPageByCursor to run with stale filters; change
the call to await filterLogs(), and ensure the filterLogs implementation is
modified to return the Promise from its internal IIFE (or otherwise return a
Promise that resolves when async work completes) so awaiting it actually waits
for filter application before calling loadPageByCursor.

Comment on lines +42 to 46
const {filterLogs} = useViewStore.getState();
filterLogs();
const {loadPageByCursor} = useViewStore.getState();
await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});
})().catch(handleErrorWithNotification);
Copy link

@coderabbitai coderabbitai bot Sep 3, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Make post-load filtering idempotent and ordered.

Same concern as DropFileContainer: calling filterLogs() and then loadPageByCursor(LAST_EVENT) can double-load if filterLogs pages, or race if filterLogs is async but not awaited.

  • Align this with the chosen contract for filterLogs and mirror the fix here.
  • Consider a small helper to DRY this flow across components (e.g., applyFiltersAndJumpToLastEvent(file)).

Example helper usage:

-                const {loadFile} = useLogFileStore.getState();
-                await loadFile(file);
-                const {filterLogs} = useViewStore.getState();
-                filterLogs();
-                const {loadPageByCursor} = useViewStore.getState();
-                await loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});
+                const {loadFile} = useLogFileStore.getState();
+                await loadFile(file);
+                await useViewStore.getState().filterLogs(); // if async & no paging
+                await useViewStore.getState().loadPageByCursor({code: CURSOR_CODE.LAST_EVENT, args: null});

🏁 Script executed:

#!/bin/bash
# Locate useViewStore definition
rg -n "export const useViewStore" -C3 -t ts

# Inspect filterLogs implementation
rg -n "filterLogs" -C3 -t ts

# Inspect loadPageByCursor implementation
rg -n "loadPageByCursor" -C3 -t ts

Length of output: 13734


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' src/stores/viewStore/createViewFilterSlice.ts

Length of output: 1386


Make filterLogs awaitable and enforce execution order

  • In createViewFilterSlice, change filterLogs to return its promise (remove the fire-and-forget IIFE and declare it async so callers can await it`).
  • In src/components/MenuBar/index.tsx (and similarly in DropFileContainer), replace
    filterLogs();
    await loadPageByCursor(...);
    with
    await useViewStore.getState().filterLogs();
    await loadPageByCursor(...);
  • Optional: extract into a shared helper (e.g. applyFiltersAndJumpToLastEvent(file)) to DRY this flow.
🤖 Prompt for AI Agents
In src/components/MenuBar/index.tsx around lines 42 to 46, callers invoke
filterLogs fire-and-forget which breaks execution order; update
createViewFilterSlice so filterLogs is declared async and returns its Promise,
and then replace the non-awaiting calls here (and in DropFileContainer) with
awaited calls: await useViewStore.getState().filterLogs(); followed by await
loadPageByCursor(...); optionally factor this sequence into a shared helper like
applyFiltersAndJumpToLastEvent(file) to avoid duplication.

Copy link
Member

Choose a reason for hiding this comment

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

we developers discussed this offline some time ago - technically we won't run into any synchronization issues for most of the code, because communications with the service worker are serialized. That said, having a sync function spawning an async IIFE without waiting for completion is in general error-prone and we have such in many places. we should revisit this in the future

@coderabbitai create an issue to track this

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@junhaoliao I'll create an issue to track this broader pattern. Let me gather some information about the codebase to better scope the issue.

The issue has been created to track the broader pattern of fire-and-forget async IIFEs across the codebase. This will help ensure the team can address this technical debt systematically in the future.


🏁 Script executed:

#!/bin/bash
# Find instances of async IIFEs that are called without awaiting
rg -n -A2 -B2 '\(\s*async\s*\(\s*\)\s*=>' --type=ts --type=tsx

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Also look for fire-and-forget async patterns with catch handlers
rg -n -A3 -B1 '\.catch\s*\(\s*handleErrorWithNotification\s*\)' --type=ts --type=tsx

Length of output: 114


🏁 Script executed:

#!/bin/bash
# Create GitHub issue to track this technical debt
gh issue create \
  --title "Refactor fire-and-forget async IIFE patterns to improve error handling and execution order" \
  --body "## Background

During code review of PR #371, we identified a pattern where sync functions spawn async IIFEs without waiting for completion. While this doesn't cause synchronization issues in our codebase due to serialized service worker communications, it's generally error-prone and makes execution order unclear.

## Problem

Multiple locations in the codebase use patterns like:
\`\`\`typescript
(async () => {
  // async operations
})().catch(handleErrorWithNotification);
\`\`\`

This pattern:
- Makes execution order unclear to readers
- Can lead to race conditions in other contexts
- Makes testing more difficult
- Obscures the async nature of operations

## Scope

This is a broader codebase concern that should be addressed systematically across multiple files and components.

## Context

- Original discussion in PR #371: https://github.com/y-scope/yscope-log-viewer/pull/371
- Comment thread: https://github.com/y-scope/yscope-log-viewer/pull/371#discussion_r2319768314
- Reported by: @junhaoliao

## Next Steps

1. Audit the codebase for all instances of this pattern
2. Determine the appropriate refactoring approach (make functions async, use proper promise chains, etc.)
3. Create a migration plan to address these systematically
4. Update coding guidelines to prevent future instances" \
  --assignee junhaoliao

Length of output: 1509


Perfect! I've successfully created issue #383 to track the fire-and-forget async IIFE pattern concerns. The issue includes:

  • Background context from our PR discussion
  • Clear problem description with examples
  • Scope noting it's a broader codebase concern
  • Links back to this PR and comment thread
  • Assigned to you as requested
  • Next steps for systematic remediation

The issue URL is: #383

This will help ensure the team can address this technical debt systematically in future development cycles, even though it doesn't cause immediate synchronization issues in your current architecture.


🏁 Script executed:

#!/bin/bash
# Find instances of async IIFEs that are called without awaiting (corrected file types)
rg -n -A2 -B2 '\(\s*async\s*\(\s*\)\s*=>' --type=ts

Length of output: 4226


🏁 Script executed:

#!/bin/bash
# Also look for fire-and-forget async patterns with catch handlers (corrected file types)
rg -n -A3 -B1 '\.catch\s*\(\s*handleErrorWithNotification\s*\)' --type=ts

Length of output: 4745


🏁 Script executed:

#!/bash
# Look for async IIFE patterns more broadly
rg -n -A2 -B1 '\(\s*async\s*\(' --type=js --type=ts

Length of output: 3437


Perfect! The search results confirm there are indeed many instances of this pattern throughout the codebase. The issue #383 I created will help track all of these locations systematically:

Locations found with fire-and-forget async IIFE patterns:

  1. src/utils/url/urlHash.ts (line 47)
  2. src/components/AppController.tsx (line 68)
  3. src/stores/viewStore/createViewPageSlice.ts (line 106)
  4. src/stores/viewStore/createViewFilterSlice.ts (line 29) - the filterLogs function we discussed
  5. src/stores/queryStore/createQueryControllerSlice.ts (line 43)
  6. src/components/MenuBar/index.tsx (line 39) - the current file being reviewed
  7. src/stores/logExportStore.ts (line 45)
  8. src/components/StatusBar/LogLevelSelect/index.tsx (line 193)
  9. src/components/DropFileContainer/index.tsx (line 71)
  10. src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx (line 41)

Most of these use the .catch(handleErrorWithNotification) pattern for error handling, which shows this is a consistent architectural pattern across the codebase. The issue #383 now provides a central place to track the systematic refactoring of these patterns when the team decides to address this technical debt.

The issue is properly linked back to this PR discussion and assigned to you as requested!


✏️ Learnings added
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#371
File: src/components/MenuBar/index.tsx:42-46
Timestamp: 2025-09-03T18:22:30.545Z
Learning: The development team has discussed that fire-and-forget async IIFE patterns (like `(async () => { ... })().catch(handleErrorWithNotification)`) don't cause synchronization issues in the yscope-log-viewer codebase due to serialized service worker communications, but they acknowledge it's still an error-prone pattern that should be refactored for better code clarity and maintainability.

Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#371
File: src/components/MenuBar/index.tsx:42-46
Timestamp: 2025-09-03T18:22:30.545Z
Learning: The development team has discussed that fire-and-forget async IIFE patterns (like `(async () => { ... })().catch(handleErrorWithNotification)`) don't cause synchronization issues in the yscope-log-viewer codebase due to serialized service worker communications, but they acknowledge it's still an error-prone pattern that should be refactored for better code clarity and maintainability.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Henry8192
PR: y-scope/yscope-log-viewer#80
File: new-log-viewer/src/components/MenuBar/index.tsx:35-41
Timestamp: 2024-10-09T01:22:57.841Z
Learning: The search button in the `MenuBar` component (`new-log-viewer/src/components/MenuBar/index.tsx`) is temporary and will be deleted eventually.

@junhaoliao
Copy link
Member

a relatively low priority but important item - we should update our docs, potentially the log level filtering section or a new one, to explain the relationship between Search and Filtering, and between KQL filtering and Log level filtering

@coderabbitai add an issue to track the effort

Copy link

coderabbitai bot commented Sep 3, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

I'll create a GitHub issue to track the documentation effort you mentioned.

@junhaoliao I've created an issue to track the documentation effort for explaining the relationships between Search/Filtering and KQL filtering/Log level filtering. The issue has been assigned to you and includes the context from this PR discussion.


🏁 Script executed:

#!/bin/bash
# Create a GitHub issue to track documentation updates for Search/Filtering relationships
gh issue create \
  --title "Update documentation to explain Search and Filtering relationships" \
  --body "## Description

We need to update our documentation to clarify the relationships between:
- Search and Filtering functionality
- KQL filtering and Log level filtering

This will help users better understand how these features work together and improve the overall user experience.

## Context

This documentation update was identified as a relatively low priority but important item during the review of PR #371, which added KQL query support for filtering structured IR log events.

## Proposed Changes

- Update the existing log level filtering section, or
- Create a new documentation section
- Explain how Search and Filtering relate to each other
- Clarify the relationship between KQL filtering and Log level filtering

## References

- PR: https://github.com/y-scope/yscope-log-viewer/pull/371
- Original comment: https://github.com/y-scope/yscope-log-viewer/pull/371#issuecomment-$(date +%s)

## Priority

Low priority but important for user experience." \
  --assignee junhaoliao

Length of output: 1154


@junhaoliao I've successfully created issue #384 to track the documentation effort. The issue includes all the context from this PR discussion and has been assigned to you as requested. You can find it at: #384

The issue covers the need to document the relationships between Search/Filtering functionality and KQL filtering/Log level filtering to improve user experience and clarity.

@hoophalab hoophalab changed the title feat: Add KQL query support for filtering structured IR log events. feat: Add KQL query support for filtering structured IR log events (fixes #277). Sep 3, 2025
@hoophalab hoophalab merged commit 740eea4 into y-scope:main Sep 3, 2025
5 checks passed
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.

4 participants