Skip to content

feat: Migrate utilities from UrlContextProvider to Zustand stores (resolves #269; fixes #294; resolves #306).#286

Merged
junhaoliao merged 31 commits intoy-scope:mainfrom
zzxthehappiest:zzx/feat-269
Jun 9, 2025
Merged

feat: Migrate utilities from UrlContextProvider to Zustand stores (resolves #269; fixes #294; resolves #306).#286
junhaoliao merged 31 commits intoy-scope:mainfrom
zzxthehappiest:zzx/feat-269

Conversation

@zzxthehappiest
Copy link
Contributor

@zzxthehappiest zzxthehappiest commented May 21, 2025

Description

#269

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. All basic operations related to URL parameters (isPrettified, logEventNum, query-related, filePath).
  2. Remove the hash parameters to see if they are reset to the default value (except the logEventNum because its default value is 0, which is not a valid one).

Summary by CodeRabbit

Summary by CodeRabbit

  • Refactor

    • Streamlined URL parameter management by removing React context and centralizing logic in dedicated utility functions and Zustand stores.
    • Deleted legacy context provider and related context store, simplifying state management.
    • Unified handling of log event numbers and prettify state within a single store for improved synchronization.
    • Refactored component event handlers and callbacks to use memoization and direct store state access for better performance and clarity.
  • Bug Fixes

    • Improved synchronization between URL parameters and application state, ensuring accurate updates when navigating or sharing links.
  • New Features

    • Added robust utilities for copying permalinks and updating URL parameters without reloading the page.
  • Style

    • Enhanced code consistency and maintainability by simplifying internal component logic and imports.
  • Documentation

    • Updated types to reflect non-nullable log event numbers and improved typings for URL parameter utilities.

@coderabbitai
Copy link

coderabbitai bot commented May 21, 2025

## Walkthrough

This change removes the React `UrlContextProvider` and its context-based URL parameter management, migrating all URL parameter handling to Zustand stores and a new `utils/url` module. Components and stores are refactored to use direct store access and utility functions for synchronizing URL state, and related context/store modules are deleted.

## Changes

| File(s)                                                                                   | Change Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
|------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| src/App.tsx                                                                              | Removed `UrlContextProvider` import and usage; now directly nests `AppController` and `Layout`.                                                                                                                                                                                                                                                                                                                                                                                                |
| src/components/AppController.tsx                                                        | Removed all React context and context store usage for URL parameters; added direct Zustand store access and new utility functions for URL hash/query param updates; added hashchange event listener; streamlined state sync logic.                                                                                                                                                                                                                                                             |
| src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx            | Switched import of `updateWindowUrlHashParams` to `utils/url`; now uses `useViewStore` for state update; memoized click handler.                                                                                                                                                                                                                                                                                                                        |
| src/components/Editor/index.tsx                                                         | Removed `UrlContext` usage; now uses `useViewStore` for state; refactored `handleEditorCustomAction` as a standalone function; unified state and URL sync logic.                                                                                                                                                                                                                                                                                        |
| src/components/StatusBar/index.tsx                                                      | Replaced `UrlContext` with Zustand store hooks for prettify state; updated toggle logic to use new store and URL utilities.                                                                                                                                                                                                                                                                                                                            |
| src/contexts/UrlContextProvider.tsx                                                     | Entire file deleted; removed all exported context, provider, constants, and utility functions for URL parameter management.                                                                                                                                                                                                                                                                                                                            |
| src/stores/contextStore.ts                                                              | Entire file deleted; removed Zustand store for `logEventNum` and all related types and exports.                                                                                                                                                                                                                                                                                                                                                         |
| src/stores/logFileStore.ts                                                              | Changed import of `updateWindowUrlSearchParams` to `utils/url`; now calls `startQuery` after loading page data; added ESLint directive.                                                                                                                                                                                                                                                                                                                |
| src/stores/viewStore.ts                                                                 | Added `logEventNum` state and setter to store; removed dependency on context store; centralized `logEventNum` handling and URL sync in view store; updated related methods.                                                                                                                                                                                                                                                                            |
| src/utils/url.ts                                                                        | Added comprehensive URL parameter parsing, updating, and clipboard utilities; exports new constants and functions for search/hash param management.                                                                                                                                                                                                                                                                                                     |
| src/typings/worker.ts                                                                   | Changed `PageData.logEventNum` from nullable to non-nullable number; updated default constant; removed unused import.                                                                                                                                                                                                                                                                                                                                  |
| src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx            | Changed import of `copyPermalinkToClipboard` to `utils/url`; now uses store's current state for query values and setters; removed dependencies array from share button callback.                                                                                                                                                                                                                                                                        |
| src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx    | Refactored event handlers to use `useCallback`; handlers update URL hash, then store state, then trigger query; unified store access pattern.                                                                                                                                                                                                                                                                                                          |
| src/typings/url.ts                                                                      | Removed exported type alias `UrlParamsType`; no other changes to types or exports.                                                                                                                                                                                                                                                                                                                                                                     |
| src/typings/common.ts                                                                   | Removed type alias `NullableProperties<T>` and its export; no other changes.                                                                                                                                                                                                                                                                                                                                                                           |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Window
    participant AppController
    participant ZustandStores
    participant UrlUtils

    Window->>AppController: hashchange event
    AppController->>UrlUtils: parseWindowUrlHashParams()
    UrlUtils-->>AppController: hash params object
    AppController->>ZustandStores: update view/query state from hash params
    AppController->>ZustandStores: trigger query if query params changed
    AppController->>UrlUtils: updateWindowUrlHashParams() (if needed)
    UrlUtils-->>Window: update browser URL hash

Possibly related issues

Possibly related PRs

Suggested reviewers

  • junhaoliao
  • Henry8192

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> npm error Exit handler never called!
> npm error This is an error with npm itself. Please report this error at:
> npm error   <https://github.com/npm/cli/issues>
> npm error A complete log of this run can be found in: /.npm/_logs/2025-06-08T14_56_39_913Z-debug-0.log
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=y-scope/yscope-log-viewer&utm_content=286):

- 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](https://www.coderabbit.ai/contact-us/support) 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](https://docs.coderabbit.ai/finishing-touches/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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@junhaoliao
Copy link
Member

@zzxthehappiest feel free to use #293 as a reference, where we replaced the NotificationConextProvider with a Zustand store

@zzxthehappiest zzxthehappiest marked this pull request as ready for review May 25, 2025 02:02
@zzxthehappiest zzxthehappiest requested a review from a team as a code owner May 25, 2025 02:02
@zzxthehappiest
Copy link
Contributor Author

For lint I added this in AppController.tsx:

/* eslint max-statements: ["error", 30] */

And modified the limitation in Editor/index.tsx:

/* eslint max-lines-per-function: ["error", 200] */
/* eslint max-statements: ["error", 30] */

Because I found I couldn't subscribe multiple states stored in Zustandard within in one statement and each one needs to be a independent statement.

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

♻️ Duplicate comments (2)
src/stores/viewStore.ts (1)

22-22: 🧹 Nitpick (assertive)

Remove the .ts extension from the import statement.

-import {updateWindowUrlHashParams} from "../utils/url.ts";
+import {updateWindowUrlHashParams} from "../utils/url";
src/components/AppController.tsx (1)

26-32: 🧹 Nitpick (assertive)

Remove the .ts extension from the import statement.

-} from "../utils/url.ts";
+} from "../utils/url";
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 69d59c9 and 0dfe7b8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • src/App.tsx (1 hunks)
  • src/components/AppController.tsx (8 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (2 hunks)
  • src/components/Editor/index.tsx (6 hunks)
  • src/components/StatusBar/index.tsx (4 hunks)
  • src/contexts/UrlContextProvider.tsx (0 hunks)
  • src/stores/contextStore.ts (0 hunks)
  • src/stores/logFileStore.ts (4 hunks)
  • src/stores/viewStore.ts (8 hunks)
  • src/utils/url.ts (3 hunks)
💤 Files with no reviewable changes (2)
  • src/stores/contextStore.ts
  • src/contexts/UrlContextProvider.tsx
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/App.tsx
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx
  • src/stores/logFileStore.ts
  • src/components/StatusBar/index.tsx
  • src/components/Editor/index.tsx
  • src/stores/viewStore.ts
  • src/components/AppController.tsx
  • src/utils/url.ts
🧠 Learnings (1)
src/stores/viewStore.ts (2)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
🧬 Code Graph Analysis (4)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (1)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (284-284)
src/components/Editor/index.tsx (5)
src/typings/worker.ts (1)
  • BeginLineNumToLogEventNumMap (105-105)
src/typings/common.ts (1)
  • Nullable (10-10)
src/utils/actions.ts (1)
  • ACTION_NAME (107-107)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (284-284)
src/typings/url.ts (1)
  • HASH_PARAM_NAMES (37-37)
src/stores/viewStore.ts (3)
src/typings/worker.ts (1)
  • BeginLineNumToLogEventNumMap (105-105)
src/utils/actions.ts (1)
  • ACTION_NAME (107-107)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (284-284)
src/utils/url.ts (2)
src/typings/url.ts (6)
  • SEARCH_PARAM_NAMES (38-38)
  • HASH_PARAM_NAMES (37-37)
  • UrlSearchParamUpdatesType (45-45)
  • UrlHashParamUpdatesType (42-42)
  • UrlHashParams (41-41)
  • UrlSearchParams (44-44)
src/typings/common.ts (1)
  • NullableProperties (11-11)
🔇 Additional comments (21)
src/App.tsx (1)

14-16: LGTM! Clean removal of UrlContextProvider wrapper.

The component tree simplification correctly reflects the migration from UrlContextProvider to Zustand stores. The nesting structure remains logical with NotificationContextProvider directly wrapping AppController and Layout.

src/stores/logFileStore.ts (2)

25-25: LGTM! Correct import path migration.

The import path change from the deleted UrlContextProvider to the new utils/url.ts module correctly reflects the migration of URL parameter utilities.


43-43: LGTM! Clean addition of setFileSrc setter.

The new setter method follows standard Zustand patterns and provides the necessary functionality for managing fileSrc state in the migration from context-based state management.

Also applies to: 160-162

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (2)

6-7: LGTM! Correct import migration to Zustand stores.

The imports correctly reflect the migration from UrlContextProvider to useViewStore and the new utils/url.ts module for URL parameter utilities.


40-40: LGTM! Proper state synchronization between store and URL.

The implementation correctly updates both the URL hash parameters and the view store state, ensuring proper synchronization between URL and internal state management.

Also applies to: 43-43

src/components/StatusBar/index.tsx (2)

13-13: LGTM! Correct migration to view store and URL utilities.

The imports correctly reflect the migration from context-based state management to Zustand view store and the new URL utility module.

Also applies to: 18-21


43-45: LGTM! Proper state management migration.

The component correctly obtains state and setters from the view store and maintains synchronization between URL parameters and store state.

Also applies to: 55-55

src/components/Editor/index.tsx (3)

138-144: LGTM! State management correctly migrated to Zustand stores.

The migration from context to useViewStore for isPrettified and logEventNum state management aligns perfectly with the PR objectives.


182-189: LGTM! Prettify toggle correctly updates both URL and store.

The implementation properly synchronizes the URL hash parameters with the store state, which is the correct pattern for this migration.


264-266: LGTM! Cursor position change correctly syncs URL and store.

The implementation properly updates both the URL hash parameters and the store state when the cursor position changes.

src/stores/viewStore.ts (4)

34-34: LGTM! logEventNum state properly added to the store.

The state property, setter method, and default value are correctly defined following the store structure.

Also applies to: 42-42, 58-58


201-203: LGTM! Setter correctly follows naming conventions.

The setLogEventNum implementation correctly uses the 'set' prefix for a simple state setter, following the established Zustand naming conventions mentioned in the learnings.


115-117: LGTM! State access correctly migrated.

The method now properly retrieves logEventNum from the local store state while maintaining postPopUp from the context store, which appears to be intentional.


242-256: LGTM! Page data update correctly synchronizes state.

The implementation properly extracts logEventNum from the page data and synchronizes it with both the URL hash parameters and the store state, with appropriate null checking.

src/components/AppController.tsx (4)

46-49: LGTM! Enhanced return type improves clarity.

The updated return type provides more detailed information about the operation result, making the function's behavior more explicit.

Also applies to: 70-70


89-102: LGTM! State management correctly migrated to stores.

All state and actions are properly migrated from context to Zustand stores, maintaining consistency with the PR objectives.


111-141: Clarify the search params update logic in the hashchange handler.

The comment at lines 122-125 suggests uncertainty about updating search params when hash params change. This mixing of concerns could lead to unexpected behavior.

Consider separating the hash and search parameter handling into distinct event handlers or clarifying why they need to be coupled. The current implementation might cause unintended side effects when only hash parameters change.


169-176: LGTM! Properly uses the enhanced return value.

The code correctly destructures and uses the enhanced return value from updateUrlIfEventOnPage.

src/utils/url.ts (3)

41-84: Well-implemented complex URL parameter handling.

The getUpdatedSearchParams function demonstrates excellent attention to detail:

  1. Correct parameter ordering: Ensures filePath is always last to handle URLs within URLs
  2. Proper null handling: Removes parameters when values are null
  3. Ambiguous character detection: Prevents URL parsing issues with smart decoding
  4. Clear documentation: Well-commented complex logic

The implementation correctly handles edge cases like remote URLs containing their own parameters.


184-203: Robust parameter parsing with proper type safety.

The hash parameter parsing correctly:

  1. Uses NullableProperties type: Properly handles the case where parameters might not be present
  2. Safe number parsing: Checks for NaN and returns null for invalid numbers
  3. Boolean parsing: Correctly converts string "true" to boolean
  4. Defensive defaults: Uses structuredClone to avoid mutation of default values

277-288: 🧹 Nitpick (assertive)

Consider organizing exports alphabetically.

For better maintainability, consider organizing the exports alphabetically, especially as this utility module grows.

 export {
     copyPermalinkToClipboard,
     getAbsoluteUrl,
     getBasenameFromUrlOrDefault,
     getWindowUrlHashParams,
     getWindowUrlSearchParams,
     openInNewTab,
     updateWindowUrlHashParams,
     updateWindowUrlSearchParams,
     URL_HASH_PARAMS_DEFAULT,
     URL_SEARCH_PARAMS_DEFAULT,
 };

The exports are already well-organized, but completely alphabetical ordering would be:

 export {
+    copyPermalinkToClipboard,
+    getAbsoluteUrl,
+    getBasenameFromUrlOrDefault,
+    getWindowUrlHashParams,
+    getWindowUrlSearchParams,
+    openInNewTab,
+    updateWindowUrlHashParams,
+    updateWindowUrlSearchParams,
+    URL_HASH_PARAMS_DEFAULT,
+    URL_SEARCH_PARAMS_DEFAULT,
-    copyPermalinkToClipboard,
-    getAbsoluteUrl,
-    getBasenameFromUrlOrDefault,
-    getWindowUrlHashParams,
-    getWindowUrlSearchParams,
-    openInNewTab,
-    updateWindowUrlHashParams,
-    updateWindowUrlSearchParams,
-    URL_HASH_PARAMS_DEFAULT,
-    URL_SEARCH_PARAMS_DEFAULT,
 };

Likely an incorrect or invalid review comment.

@zzxthehappiest zzxthehappiest marked this pull request as draft May 25, 2025 02:08
@zzxthehappiest zzxthehappiest marked this pull request as ready for review May 25, 2025 02:46
@zzxthehappiest
Copy link
Contributor Author

I think if we add unit tests for url.ts then this PR will exceed 400 lines limitation.

@zzxthehappiest zzxthehappiest requested a review from junhaoliao May 25, 2025 02:49
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/StatusBar/index.tsx (1)

55-55: Follow coding guideline for boolean expressions.

The code uses !isPrettified which violates the coding guideline that prefers false == <expression> rather than !<expression>.

Apply this diff to align with the coding guideline:

-                setIsPrettified(!isPrettified);
+                setIsPrettified(false === isPrettified);
src/components/Editor/index.tsx (1)

35-35: Remove the .ts extension from the import statement.

TypeScript imports typically don't include file extensions. This is also inconsistent with other imports in the file.

-import {updateWindowUrlHashParams} from "../../utils/url.ts";
+import {updateWindowUrlHashParams} from "../../utils/url";
src/utils/url.ts (2)

246-250: 🧹 Nitpick (assertive)

Clarify the comment about Zustand store updates.

The comment should be clearer about the separation of responsibilities between URL updates and state synchronization.

- * Note: This function only updates the URL. Callers are responsible for updating corresponding
- * Zustand store state to maintain synchronization.
+ * Note: This function only updates the URL. Callers are responsible for updating 
+ * corresponding Zustand store state to maintain synchronization.

267-271: 🧹 Nitpick (assertive)

Clarify the comment about Zustand store updates.

Same issue as the hash params function - the comment should be clearer.

- * Note: This function only updates the URL. Callers are responsible for updating corresponding
- * Zustand store state to maintain synchronization.
+ * Note: This function only updates the URL. Callers are responsible for updating 
+ * corresponding Zustand store state to maintain synchronization.
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0dfe7b8 and c808cf2.

📒 Files selected for processing (9)
  • src/App.tsx (1 hunks)
  • src/components/AppController.tsx (8 hunks)
  • src/components/Editor/index.tsx (6 hunks)
  • src/components/StatusBar/index.tsx (3 hunks)
  • src/contexts/UrlContextProvider.tsx (0 hunks)
  • src/stores/contextStore.ts (0 hunks)
  • src/stores/logFileStore.ts (4 hunks)
  • src/stores/viewStore.ts (8 hunks)
  • src/utils/url.ts (3 hunks)
💤 Files with no reviewable changes (2)
  • src/contexts/UrlContextProvider.tsx
  • src/stores/contextStore.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/App.tsx
  • src/stores/logFileStore.ts
  • src/components/StatusBar/index.tsx
  • src/components/Editor/index.tsx
  • src/components/AppController.tsx
  • src/utils/url.ts
  • src/stores/viewStore.ts
🧠 Learnings (2)
src/utils/url.ts (1)
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/utils/url.ts:46-46
Timestamp: 2025-05-25T02:45:44.343Z
Learning: In TypeScript, when comparing enum values with strings (like keys from Object.entries()), the type assertion `as string` is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error. This is a valid use of type assertion for enum-to-string comparison.
src/stores/viewStore.ts (2)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
🧬 Code Graph Analysis (3)
src/stores/logFileStore.ts (2)
src/typings/common.ts (1)
  • Nullable (10-10)
src/typings/worker.ts (1)
  • FileSrcType (108-108)
src/components/Editor/index.tsx (5)
src/typings/worker.ts (1)
  • BeginLineNumToLogEventNumMap (105-105)
src/typings/common.ts (1)
  • Nullable (10-10)
src/utils/actions.ts (1)
  • ACTION_NAME (107-107)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (288-288)
src/typings/url.ts (1)
  • HASH_PARAM_NAMES (37-37)
src/utils/url.ts (2)
src/typings/url.ts (6)
  • SEARCH_PARAM_NAMES (38-38)
  • HASH_PARAM_NAMES (37-37)
  • UrlSearchParamUpdatesType (45-45)
  • UrlHashParamUpdatesType (42-42)
  • UrlHashParams (41-41)
  • UrlSearchParams (44-44)
src/typings/common.ts (1)
  • NullableProperties (11-11)
🔇 Additional comments (6)
src/App.tsx (1)

12-14: LGTM! Clean removal of context provider wrapper.

The removal of UrlContextProvider wrapper correctly reflects the migration to Zustand stores for URL parameter management. The simplified structure where AppController directly contains Layout is appropriate.

src/stores/logFileStore.ts (1)

42-42: LGTM! Proper addition of setFileSrc method.

The new setFileSrc method is correctly typed in the interface and properly implemented in the store. This supports the migration by allowing direct store-based updates of the file source state.

Also applies to: 159-161

src/components/StatusBar/index.tsx (1)

43-45: LGTM! Proper migration to Zustand store.

The component correctly migrates from React context to useViewStore hooks for managing isPrettified and logEventNum state. The store-based approach provides better performance and cleaner state management.

src/components/Editor/index.tsx (1)

138-144: LGTM! Excellent migration to Zustand store architecture.

The component successfully migrates from React context to useViewStore hooks for state management. The integration maintains all existing functionality while providing better performance and cleaner separation of concerns. The callback dependencies are correctly updated to reflect the new store methods.

Also applies to: 196-197, 265-266

src/stores/viewStore.ts (1)

232-237: Good implementation of URL and state synchronization.

The order of operations (updating URL first, then store state) ensures proper synchronization between the URL parameters and the internal store state.

src/components/AppController.tsx (1)

39-67: Good improvement to the return type.

Returning both isUpdated and nearestLogEventNum provides more useful information to the caller, allowing for better decision-making in the consuming code.

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.

Thanks for quickly putting up the changes.

Could you also help integrating the changes in junhaoliao@b972113 into the current PR? Those will fix #294

(my fix is to align the existing parameters parsing logic. in my changes, we might also need to make sure the parsed values are of string type before calling new URLSearchParams(). That said, maybe the code should be completely rewritten - the for-loops can go through the currentParams objects to insert values into an empty new URLSearchParams(). if you think there's a better way to fix #294, feel free to make your own changes

@zzxthehappiest
Copy link
Contributor Author

Thanks for quickly putting up the changes.

Could you also help integrating the changes in junhaoliao@b972113 into the current PR? Those will fix #294

(my fix is to align the existing parameters parsing logic. in my changes, we might also need to make sure the parsed values are of string type before calling new URLSearchParams(). That said, maybe the code should be completely rewritten - the for-loops can go through the currentParams objects to insert values into an empty new URLSearchParams(). if you think there's a better way to fix #294, feel free to make your own changes

I modified the filterNullValues to filterNullValuesToStrings because the types of both keys and values need to be string (otherwise Webstorm told me it was an error), so I converted all values to strings.

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 00cadb9 and 6620583.

📒 Files selected for processing (2)
  • src/utils/js.ts (2 hunks)
  • src/utils/url.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/utils/js.ts
  • src/utils/url.ts
🧠 Learnings (1)
src/utils/url.ts (1)
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/utils/url.ts:46-46
Timestamp: 2025-05-25T02:45:44.362Z
Learning: In TypeScript, when comparing enum values with strings (like keys from Object.entries()), the type assertion `as string` is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error. This is a valid use of type assertion for enum-to-string comparison.
🔇 Additional comments (5)
src/utils/url.ts (5)

16-32: Well-structured constants with proper immutability.

Good use of Object.freeze to ensure the default values remain immutable, and the regex pattern correctly handles ambiguous URL characters.


59-78: Robust parameter parsing with proper type handling.

The function correctly handles type conversions and edge cases, including NaN checks for numeric values and proper boolean parsing.


85-109: Appropriate handling of special filePath parameter.

The implementation correctly handles the case where filePath may contain its own URL parameters by using string manipulation instead of URLSearchParams.get(). Good error handling for URL resolution.


119-163: Well-designed parameter update logic with clear documentation.

The implementation correctly handles the special requirements for filePath parameter ordering and includes appropriate type assertions for enum comparisons (as per established patterns). The comments clearly explain the rationale for keeping filePath as the last parameter.


198-212: Clean implementation with proper error handling.

Good use of the Clipboard API with appropriate error handling. The function correctly creates a modified URL without affecting the current window location.

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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6620583 and 66abf15.

📒 Files selected for processing (1)
  • src/utils/url.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/utils/url.ts
🧠 Learnings (1)
src/utils/url.ts (1)
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/utils/url.ts:46-46
Timestamp: 2025-05-25T02:45:44.362Z
Learning: In TypeScript, when comparing enum values with strings (like keys from Object.entries()), the type assertion `as string` is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error. This is a valid use of type assertion for enum-to-string comparison.
🔇 Additional comments (5)
src/utils/url.ts (5)

14-28: Well-structured constants with proper immutability.

Good use of Object.freeze to ensure the default values remain immutable. The constants are properly typed and provide clear default values.


86-110: Robust implementation for extracting filePath parameter.

Good implementation that correctly handles the filePath parameter extraction and URL resolution. The error handling for invalid URLs is appropriate.


120-164: Excellent implementation with clear documentation.

The function properly handles the special case of filePath parameter, ensuring it remains last for URL readability and parameter handling. The URL decoding logic is clever and well-thought-out.


259-269: Well-optimized implementation with clear documentation.

Good optimization to prevent unnecessary history entries by checking if the hash actually changed. The comment clearly explains the caller's responsibility for maintaining store synchronization.


280-289: Consistent implementation with hash parameter updates.

Good consistency with updateWindowUrlHashParams including the optimization check and clear documentation about store synchronization responsibility.

@zzxthehappiest
Copy link
Contributor Author

@junhaoliao Sorry for the delay. I am on a business since last week and get home late recently. I fixed most of comments except this one. I found a behaviour that when clicking the "Prettify" button, there will be a isPrettified=true. When I delete the isPrettified=true the logs remain prettified because the isPrettified is null so its state won't be updated. I wonder do we want this or do we want when a parameter is missing we set it with the default value.

I will definitely finish all things by tomorrow. I need to take a look of the changed code again to ensure all of them apply the comments.

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 (18)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (2)

20-20: Remove the .ts extension from the import path.

This was previously flagged and agreed upon but still remains unaddressed.

-import {copyPermalinkToClipboard} from "../../../../../utils/url.ts";
+import {copyPermalinkToClipboard} from "../../../../../utils/url";

53-55: Remove redundant state setter calls.

These setter calls are redundant as they're setting the same values that are already in the store. This was previously identified but not addressed.

-        setQueryIsCaseSensitive(queryIsCaseSensitive);
-        setQueryIsRegex(queryIsRegex);
-        setQueryString(queryString);
-
         copyPermalinkToClipboard({}, {
src/components/StatusBar/index.tsx (5)

1-1: LGTM! React import added as requested.


15-15: LGTM! Store import updated correctly.


20-23: LGTM! URL utility imports updated correctly.


55-57: Good adherence to coding guidelines and proper state synchronization.

The code correctly uses false === isPrettified to follow the coding guideline and properly calls updateIsPrettified to synchronize state.


95-95: Clean implementation of boolean expressions.

The props correctly use the coding guideline-compliant boolean expressions with false === isPrettified.

Also applies to: 100-102

src/components/Editor/index.tsx (2)

127-177: Excellent extraction of editor action handler.

The standalone handleEditorCustomAction function is well-structured and properly uses getState() to access store state directly. This follows the suggested pattern from past reviews and improves code organization.


267-268: Optimal use of getState() for static setter.

Correctly uses getState() to access setLogEventNum as it's a static setter that never changes, avoiding unnecessary subscriptions. This follows the established pattern from the learnings.

src/stores/viewStore.ts (2)

18-18: Proper import of URL utility function.

The import correctly references the new URL utilities module.


185-187: Correct naming convention for simple setter.

The setLogEventNum function properly follows the established naming convention for simple setters that only change state values.

src/components/AppController.tsx (5)

74-141: Excellent separation of concerns with helper functions.

The three helper functions (updateViewHashParams, updateQueryHashParams, and handleHashChange) provide clean separation of concerns and make the URL parameter handling much more maintainable. The design properly handles query restarts when parameters change and returns parsed parameters for further use.


156-156: Clean state access pattern.

Only importing the necessary logEventNum state follows the suggested pattern from past reviews, reducing unnecessary subscriptions.


159-159: Well-structured initialization and event handling.

The use of isInitialized ref properly handles React StrictMode double execution, and the main effect correctly registers the hash change handler while handling initial page load. The cleanup function properly removes the event listener.

Also applies to: 162-190


194-222: Optimal state retrieval pattern.

The effect correctly retrieves Zustand states just before usage to avoid circular dependencies, and the dependency array properly includes only logEventNum as suggested in past reviews. The logic for clamping and page switching is clear and correct.


15-15: 🧹 Nitpick (assertive)

Remove the .ts extension from the import path.

The explicit .ts extension should be removed to follow TypeScript conventions.

-} from "../typings/common.ts";
+} from "../typings/common";
src/utils/url.ts (2)

166-166: Type assertion is correctly maintained for enum comparison.

Based on the retrieved learning, the type assertion as string is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error when comparing enum values with strings from Object.entries().


241-255: 🧹 Nitpick (assertive)

Consider using notification system for user feedback.

The console logging approach for clipboard operations may not provide adequate user feedback in a production environment.

Since this PR migrates to Zustand stores, consider integrating with a notification store for better user experience:

 const copyPermalinkToClipboard = (
     searchParamUpdates: UrlSearchParamUpdatesType,
     hashParamsUpdates: UrlHashParamUpdatesType,
 ) => {
     const newUrl = new URL(window.location.href);
     newUrl.search = getUpdatedSearchParams(searchParamUpdates);
     newUrl.hash = getUpdatedHashParams(hashParamsUpdates);
     navigator.clipboard.writeText(newUrl.toString())
         .then(() => {
-            console.log("URL copied to clipboard.");
+            // TODO: Integrate with notification store for user feedback
+            console.log("URL copied to clipboard.");
         })
         .catch((error: unknown) => {
-            console.error("Failed to copy URL to clipboard:", error);
+            // TODO: Integrate with notification store for error feedback
+            console.error("Failed to copy URL to clipboard:", error);
         });
 };
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3add41d and 7166d2b.

📒 Files selected for processing (10)
  • src/components/AppController.tsx (4 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx (3 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (2 hunks)
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (2 hunks)
  • src/components/Editor/index.tsx (4 hunks)
  • src/components/StatusBar/index.tsx (4 hunks)
  • src/stores/logFileStore.ts (3 hunks)
  • src/stores/viewStore.ts (8 hunks)
  • src/typings/url.ts (0 hunks)
  • src/utils/url.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • src/typings/url.ts
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx
  • src/components/StatusBar/index.tsx
  • src/stores/viewStore.ts
  • src/components/AppController.tsx
  • src/utils/url.ts
  • src/stores/logFileStore.ts
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx
  • src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx
  • src/components/Editor/index.tsx
🧠 Learnings (5)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx (1)
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.
src/stores/viewStore.ts (2)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#286
File: src/stores/viewStore.ts:46-46
Timestamp: 2025-05-21T20:08:19.249Z
Learning: For Zustand store actions, use `setX` for functions that simply change the value of a state, and use `updateX` for functions that do more than just set a value (e.g., perform additional logic, make API calls, update multiple states).
src/components/AppController.tsx (3)
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.
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/components/AppController.tsx:40-43
Timestamp: 2025-06-01T13:44:05.278Z
Learning: In the y-scope/yscope-log-viewer codebase, when functions return invalid or default log event numbers, they should use 0 as this aligns with URL_HASH_PARAMS_DEFAULT.LOG_EVENT_NUM which is set to 0. This maintains consistency with the established default hash parameter pattern.
src/utils/url.ts (1)
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/utils/url.ts:46-46
Timestamp: 2025-05-25T02:45:44.362Z
Learning: In TypeScript, when comparing enum values with strings (like keys from Object.entries()), the type assertion `as string` is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error. This is a valid use of type assertion for enum-to-string comparison.
src/components/Editor/index.tsx (2)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.
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.
🧬 Code Graph Analysis (6)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx (3)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (334-334)
src/services/LogFileManagerProxy.ts (1)
  • startQuery (65-68)
src/services/LogFileManager/index.ts (1)
  • startQuery (356-386)
src/stores/viewStore.ts (3)
src/typings/worker.ts (3)
  • BeginLineNumToLogEventNumMap (104-104)
  • CursorType (106-106)
  • CURSOR_CODE (99-99)
src/services/LogFileManager/index.ts (1)
  • cursor (492-514)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (334-334)
src/utils/url.ts (1)
src/typings/url.ts (6)
  • SEARCH_PARAM_NAMES (38-38)
  • HASH_PARAM_NAMES (37-37)
  • UrlSearchParams (43-43)
  • UrlHashParams (41-41)
  • UrlSearchParamUpdatesType (44-44)
  • UrlHashParamUpdatesType (42-42)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (1)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (334-334)
src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/index.tsx (1)
src/utils/url.ts (1)
  • copyPermalinkToClipboard (328-328)
src/components/Editor/index.tsx (3)
src/utils/actions.ts (1)
  • ACTION_NAME (107-107)
src/utils/url.ts (1)
  • updateWindowUrlHashParams (334-334)
src/typings/url.ts (1)
  • HASH_PARAM_NAMES (37-37)
🔇 Additional comments (22)
src/stores/logFileStore.ts (3)

1-1: ESLint directive appropriately added for migration.

The increased line limit accommodates the Zustand subscription patterns that require more statements compared to the previous context-based approach.


24-24: Import successfully migrated from context to utility module.

The migration from UrlContextProvider to the new utils/url module aligns with the overall refactoring strategy.


143-144: Query restart implementation addresses past feedback.

The explicit startQuery() call ensures queries are actively restarted once a file is loaded, directly implementing the suggestion from the previous review.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/QueryInputBox.tsx (4)

1-1: useCallback import appropriately added.

The addition supports the memoization pattern recommended in previous reviews.


32-38: Excellent implementation of the suggested callback pattern.

The handler correctly follows the recommended approach: uses useCallback for memoization, updates URL hash parameters, retrieves store actions via getState(), and triggers the query.


40-46: Consistent implementation of the callback pattern.

The case sensitivity handler follows the same recommended pattern as the query input handler.


48-54: Proper implementation of the regex toggle handler.

The handler maintains consistency with the established pattern across all three event handlers.

src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/Result.tsx (2)

1-1: Proper imports added for the migration pattern.

The addition of useCallback, useViewStore, and updateWindowUrlHashParams imports supports the consistent implementation of the new state management approach.

Also applies to: 8-9


43-47: Correct implementation of the callback pattern.

The handler properly uses useCallback with the appropriate dependency array and retrieves the store action via getState(), following the established migration pattern.

src/components/StatusBar/index.tsx (1)

45-47: Excellent migration to Zustand stores.

The component now properly accesses isPrettified, logEventNum, and updateIsPrettified from the view store, replacing the previous context-based approach.

src/components/Editor/index.tsx (2)

33-33: Clean import without file extension.

The import follows TypeScript conventions correctly.


191-191: Proper store access pattern.

Correctly accesses logEventNum from the view store using the hook pattern for reactive state.

src/stores/viewStore.ts (2)

30-30: Excellent consolidation of logEventNum state.

The integration of logEventNum into the view store properly consolidates state management and follows the migration from the removed context store.

Also applies to: 38-38, 54-54


229-234: Well-implemented URL synchronization.

The updatePageData function properly extracts logEventNum from page data, updates URL parameters, and synchronizes store state, maintaining consistency between URL and application state.

src/utils/url.ts (8)

1-9: LGTM: Clean imports and ESLint configuration.

The ESLint max-lines directive and import statements are well-organized and appropriate for the file's scope.


15-28: LGTM: Well-structured default parameter constants.

The default parameter objects are properly frozen and use appropriate default values. The structure supports the migration from context-based to Zustand-based state management.


94-118: LGTM: Comprehensive hash parameter parsing.

The hash parameter parsing logic correctly handles all expected parameter types with appropriate type conversions and fallbacks.


125-138: LGTM: Clean getter implementations using composition.

The getter functions effectively combine default values with parsed parameters using the spread operator, providing a clean API for accessing URL parameters.


146-150: LGTM: Useful utility function for parameter validation.

The isEmptyOrFalsy helper function provides a centralized way to check for empty or falsy values, promoting code reuse across the module.


214-229: LGTM: Consistent hash parameter update logic.

The hash parameter update function follows the same pattern as search parameters with appropriate type handling for the URLSearchParams constructor.


298-325: LGTM: Efficient URL update implementations with change detection.

Both update functions correctly implement change detection to prevent unnecessary history entries, which is an important optimization for browser history management.


327-338: LGTM: Comprehensive export interface.

The exports provide a clean public API that covers all necessary URL parameter management functionality for the Zustand store migration.

Comment on lines +56 to +87
/**
* Parses the URL search parameters from the current window's URL.
*
* @return An object containing the parsed search parameters.
*/
const parseWindowUrlSearchParams = () : Partial<UrlSearchParams> => {
const parsedSearchParams : Partial<UrlSearchParams> = {};
const searchParams = new URLSearchParams(window.location.search.substring(1));

searchParams.forEach((value, key) => {
parsedSearchParams[key as keyof UrlSearchParams] = value;
});

if (searchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
// Extract filePath value by finding the parameter and taking everything after it
const filePathIndex = window.location.search.indexOf("filePath=");
if (-1 !== filePathIndex) {
const filePath = window.location.search.substring(filePathIndex + "filePath=".length);
if (0 !== filePath.length) {
let resolvedFilePath = filePath;
try {
resolvedFilePath = getAbsoluteUrl(filePath);
} catch (e) {
console.error("Unable to get absolute URL from filePath:", e);
}
parsedSearchParams[SEARCH_PARAM_NAMES.FILE_PATH] = resolvedFilePath;
}
}
}

return parsedSearchParams;
};
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider edge case handling in filePath extraction.

The filePath extraction logic could be more robust. If window.location.search contains multiple occurrences of "filePath=", the current implementation will use the first occurrence, which may not be the intended behavior.

Consider adding validation or using a more precise extraction method:

 const parseWindowUrlSearchParams = () : Partial<UrlSearchParams> => {
     const parsedSearchParams : Partial<UrlSearchParams> = {};
-    const searchParams = new URLSearchParams(window.location.search.substring(1));
+    const searchParams = new URLSearchParams(window.location.search);
 
     searchParams.forEach((value, key) => {
         parsedSearchParams[key as keyof UrlSearchParams] = value;
     });
 
     if (searchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
-        // Extract filePath value by finding the parameter and taking everything after it
-        const filePathIndex = window.location.search.indexOf("filePath=");
-        if (-1 !== filePathIndex) {
-            const filePath = window.location.search.substring(filePathIndex + "filePath=".length);
-            if (0 !== filePath.length) {
+        // Use URLSearchParams for more reliable parameter extraction
+        const filePath = searchParams.get(SEARCH_PARAM_NAMES.FILE_PATH);
+        if (filePath && 0 !== filePath.length) {
                 let resolvedFilePath = filePath;
                 try {
                     resolvedFilePath = getAbsoluteUrl(filePath);
                 } catch (e) {
                     console.error("Unable to get absolute URL from filePath:", e);
                 }
                 parsedSearchParams[SEARCH_PARAM_NAMES.FILE_PATH] = resolvedFilePath;
-            }
-        }
+        }
     }
📝 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
/**
* Parses the URL search parameters from the current window's URL.
*
* @return An object containing the parsed search parameters.
*/
const parseWindowUrlSearchParams = () : Partial<UrlSearchParams> => {
const parsedSearchParams : Partial<UrlSearchParams> = {};
const searchParams = new URLSearchParams(window.location.search.substring(1));
searchParams.forEach((value, key) => {
parsedSearchParams[key as keyof UrlSearchParams] = value;
});
if (searchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
// Extract filePath value by finding the parameter and taking everything after it
const filePathIndex = window.location.search.indexOf("filePath=");
if (-1 !== filePathIndex) {
const filePath = window.location.search.substring(filePathIndex + "filePath=".length);
if (0 !== filePath.length) {
let resolvedFilePath = filePath;
try {
resolvedFilePath = getAbsoluteUrl(filePath);
} catch (e) {
console.error("Unable to get absolute URL from filePath:", e);
}
parsedSearchParams[SEARCH_PARAM_NAMES.FILE_PATH] = resolvedFilePath;
}
}
}
return parsedSearchParams;
};
/**
* Parses the URL search parameters from the current window's URL.
*
* @return An object containing the parsed search parameters.
*/
const parseWindowUrlSearchParams = (): Partial<UrlSearchParams> => {
const parsedSearchParams: Partial<UrlSearchParams> = {};
const searchParams = new URLSearchParams(window.location.search);
searchParams.forEach((value, key) => {
parsedSearchParams[key as keyof UrlSearchParams] = value;
});
if (searchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
// Use URLSearchParams for more reliable parameter extraction
const filePath = searchParams.get(SEARCH_PARAM_NAMES.FILE_PATH);
if (filePath && 0 !== filePath.length) {
let resolvedFilePath = filePath;
try {
resolvedFilePath = getAbsoluteUrl(filePath);
} catch (e) {
console.error("Unable to get absolute URL from filePath:", e);
}
parsedSearchParams[SEARCH_PARAM_NAMES.FILE_PATH] = resolvedFilePath;
}
}
return parsedSearchParams;
};
🤖 Prompt for AI Agents
In src/utils/url.ts between lines 56 and 87, the filePath extraction currently
uses indexOf to find the first occurrence of "filePath=" in
window.location.search, which can cause issues if multiple filePath parameters
exist. To fix this, replace the manual substring extraction with a more precise
method by using URLSearchParams.get or iterating over all "filePath" parameters
to handle multiple occurrences correctly. This ensures the correct filePath
value is extracted and processed robustly.

Comment on lines +160 to +204
const getUpdatedSearchParams = (updates: UrlSearchParamUpdatesType) => {
const currentSearchParams = parseWindowUrlSearchParams();
const newSearchParams = new URLSearchParams(currentSearchParams);
const {filePath: newFilePath} = updates;

for (const [key, value] of Object.entries(updates)) {
if (SEARCH_PARAM_NAMES.FILE_PATH as string === key) {
// Updates to `filePath` should be handled last.
continue;
}
if (isEmptyOrFalsy(value)) {
newSearchParams.delete(key);
} else {
newSearchParams.set(key, String(value));
}
}

// `filePath` should always be the last search parameter so that:
// 1. Users can specify a remote filePath (a URL) that itself contains URL parameters without
// encoding them. E.g. "<log-viewer-url>/?filePath=https://example.com/log/?p1=v1&p2=v2"
// 2. Users can easily modify it in the URL
//
// NOTE: We're relying on URLSearchParams.set() and URLSearchParams.toString() to store and
// serialize the parameters in the order that they were set.
const originalFilePath = newSearchParams.get(SEARCH_PARAM_NAMES.FILE_PATH);
newSearchParams.delete(SEARCH_PARAM_NAMES.FILE_PATH);
if ("undefined" === typeof newFilePath && null !== originalFilePath) {
// If no change in `filePath` is specified, put the original `filePath` back.
newSearchParams.set(SEARCH_PARAM_NAMES.FILE_PATH, originalFilePath);
} else if ("undefined" !== typeof newFilePath && null !== newFilePath) {
// If the new `filePath` has a non-null value, set the value.
newSearchParams.set(SEARCH_PARAM_NAMES.FILE_PATH, newFilePath);
}

// If the stringified search params doesn't contain characters that would make the URL ambiguous
// to parse, URL-decode it so that the `filePath` remains human-readable. E.g.
// "filePath=https://example.com/log/?s1=1&s2=2#h1=0" would make the final URL ambiguous to
// parse since `filePath` itself contains URL parameters.
let searchString = newSearchParams.toString();
if (false === AMBIGUOUS_URL_CHARS_REGEX.test(searchString)) {
searchString = decodeURIComponent(searchString);
}

return searchString;
};
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

Verify filePath handling logic for edge cases.

The special handling of filePath as the last parameter is well-documented, but the logic has some complexity that could benefit from verification.

The conditions on lines 186-192 handle different scenarios for filePath updates. Let me verify this logic works correctly for all cases:


🏁 Script executed:

#!/bin/bash
# Verify that filePath handling covers all expected scenarios
echo "Testing filePath parameter handling scenarios:"

# Check for any existing tests that validate this logic
fd -e test.ts -e spec.ts | xargs rg -l "filePath" -A 3 -B 3

# Look for any documentation about filePath parameter handling
rg -i "filepath.*parameter" -A 5 -B 5

Length of output: 2449


Add comprehensive tests for filePath handling in getUpdatedSearchParams

We didn’t find any existing tests covering the complex ordering and encoding logic for filePath. Please add test cases targeting these scenarios in src/utils/url.ts (lines 160–204):

  • No update: newFilePath is undefined ⇒ original filePath is preserved
  • Removal: newFilePath is explicitly nullfilePath parameter is deleted
  • Update: newFilePath is a new URL (including its own query params) ⇒ it’s appended last and remains human-readable
  • Ambiguous characters present (e.g. ?, &, #) ⇒ verify that decodeURIComponent is applied only when safe

Cover both browser-style URLs and remote URLs with embedded parameters to ensure correct ordering, deletion and decoding.

🤖 Prompt for AI Agents
In src/utils/url.ts around lines 160 to 204, add comprehensive unit tests for
the getUpdatedSearchParams function focusing on the special handling of the
filePath parameter. Create test cases for scenarios where newFilePath is
undefined (original filePath preserved), explicitly null (filePath removed), and
updated to a new URL with embedded query parameters (filePath appended last and
human-readable). Also, test that decodeURIComponent is applied only when the
search string does not contain ambiguous URL characters. Ensure tests cover both
local and remote URLs with embedded parameters to verify correct ordering,
deletion, and decoding behavior.

@zzxthehappiest
Copy link
Contributor Author

thanks for cleaning all those up and navigating through so much undocumented stuff (i'll try to write more docs once available)

please see the comments for how we can further improve the code. feel free to take 2 or 3 days to go through all changes.

Here i'm sharing my code as a reference if at the end you want to make sure things aren't missed: src-286-junhao-ref-0602.zip

Also I am not sure if it is because my network issue... I can't download your code, but I think it's fine. There is only one function that I write it myself parseWindowUrlSearchParams (in your comment it is parseUrlSearchParams, I renamed it to make it consistent with parseWindowUrlHashParams).

@junhaoliao
Copy link
Member

junhaoliao commented Jun 4, 2025

I am on a business since last week and get home late recently.

no worries! there's a holiday in the past few days, and thanks for getting back at least one day earlier than I expected :)

I found a behaviour that when clicking the "Prettify" button, there will be a isPrettified=true. When I delete the isPrettified=true the logs remain prettified because the isPrettified is null so its state won't be updated. I wonder do we want this

i missed that in my validations. thanks for testing thoroughly and finding the case!

when a parameter is missing we set it with the default value

sounds good. I believe this is a better behavior. please proceed with the changes

Also I am not sure if it is because my network issue... I can't download your code, but I think it's fine.

no worries. i'll do a final check before approving

There is only one function that I write it myself parseWindowUrlSearchParams (in your comment it is parseUrlSearchParams, I renamed it to make it consistent with parseWindowUrlHashParams).

sounds good. I agree parseWindowUrlHashParams is consistent and thus better

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)
src/components/AppController.tsx (2)

102-109: 🛠️ Refactor suggestion

Update query parameters conditionally to match the past review suggestion.

The current implementation sets all parameters unconditionally, which could trigger unnecessary state updates. The past review suggested checking against defaults before updating.

Apply this diff to align with the suggested implementation:

     let isQueryModified = false;
-    isQueryModified ||= queryIsCaseSensitive !== currentQueryIsCaseSensitive;
-    setQueryIsCaseSensitive(queryIsCaseSensitive);
+    if (URL_HASH_PARAMS_DEFAULT.queryIsCaseSensitive !== queryIsCaseSensitive) {
+        isQueryModified ||= queryIsCaseSensitive !== currentQueryIsCaseSensitive;
+        setQueryIsCaseSensitive(queryIsCaseSensitive);
+    }

-    isQueryModified ||= queryIsRegex !== currentQueryIsRegex;
-    setQueryIsRegex(queryIsRegex);
+    if (URL_HASH_PARAMS_DEFAULT.queryIsRegex !== queryIsRegex) {
+        isQueryModified ||= queryIsRegex !== currentQueryIsRegex;
+        setQueryIsRegex(queryIsRegex);
+    }

-    isQueryModified ||= queryString !== currentQueryString;
-    setQueryString(queryString);
+    if (URL_HASH_PARAMS_DEFAULT.queryString !== queryString) {
+        isQueryModified ||= queryString !== currentQueryString;
+        setQueryString(queryString);
+    }

80-81: 🛠️ Refactor suggestion

Add conditional checks to prevent unnecessary state updates.

The current implementation updates state unconditionally, which could trigger unnecessary re-renders. Consider checking against defaults before updating, as suggested in the past review.

Apply this diff to update only when values differ from defaults:

-    updateIsPrettified(isPrettified);
-    setLogEventNum(logEventNum);
+    if (URL_HASH_PARAMS_DEFAULT.isPrettified !== isPrettified) {
+        updateIsPrettified(isPrettified);
+    }
+
+    if (URL_HASH_PARAMS_DEFAULT.logEventNum !== logEventNum) {
+        setLogEventNum(logEventNum);
+    }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between def27ea and 3c9bd0d.

📒 Files selected for processing (1)
  • src/components/AppController.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/components/AppController.tsx
🧠 Learnings (1)
src/components/AppController.tsx (3)
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.
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/components/AppController.tsx:40-43
Timestamp: 2025-06-01T13:44:05.278Z
Learning: In the y-scope/yscope-log-viewer codebase, when functions return invalid or default log event numbers, they should use 0 as this aligns with URL_HASH_PARAMS_DEFAULT.LOG_EVENT_NUM which is set to 0. This maintains consistency with the established default hash parameter pattern.
🧬 Code Graph Analysis (1)
src/components/AppController.tsx (8)
src/typings/url.ts (1)
  • UrlHashParams (41-41)
src/typings/common.ts (1)
  • Nullable (6-6)
src/utils/url.ts (5)
  • getWindowUrlHashParams (331-331)
  • updateWindowUrlHashParams (336-336)
  • getWindowUrlSearchParams (332-332)
  • URL_SEARCH_PARAMS_DEFAULT (339-339)
  • URL_HASH_PARAMS_DEFAULT (338-338)
src/services/LogFileManagerProxy.ts (2)
  • startQuery (65-68)
  • loadFile (17-42)
src/services/LogFileManager/index.ts (2)
  • cursor (492-514)
  • numEvents (124-126)
src/typings/worker.ts (2)
  • CursorType (106-106)
  • CURSOR_CODE (99-99)
src/typings/states.ts (1)
  • UI_STATE (106-106)
src/stores/notificationStore.ts (1)
  • handleErrorWithNotification (76-76)
🔇 Additional comments (1)
src/components/AppController.tsx (1)

156-187: Well-structured initialization logic!

The implementation correctly follows the past review suggestions:

  • Uses isInitialized ref to prevent re-initialization in StrictMode
  • Retrieves states close to usage to avoid circular dependencies
  • Handles both hash and search parameters on initial load
  • Good comment explaining why logEventNum 0 is skipped

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.

thanks for cleaning things up and catching the other bugs! let's address the suggestions below before merging the PR

the PR title is good

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 (5)
src/stores/logFileStore.ts (1)

140-141: 🧹 Nitpick (assertive)

Consider the placement of startQuery().

The startQuery() call is placed after updatePageData(). Based on the past review comment, it was suggested to place this before the canFormat check to ensure queries are actively restarted once a file is loaded. The current placement works but might delay query execution slightly.

Consider moving startQuery() before the canFormat check as suggested in the past review:

            const pageData = await logFileManagerProxy.loadPage(cursor, isPrettified);
            updatePageData(pageData);

            const {startQuery} = useQueryStore.getState();
            startQuery();
+
            const canFormat = fileInfo.fileType === FILE_TYPE.CLP_KV_IR ||
                fileInfo.fileType === FILE_TYPE.JSONL;
src/components/Editor/index.tsx (1)

189-189: 🧹 Nitpick (assertive)

Consider removing refs as suggested in past reviews.

The code still uses beginLineNumToLogEventNumRef (lines 192-194, 269-272) which could be replaced with direct store access via getState() as suggested in past review comments. This would simplify the code and remove the need for synchronization via useEffect.

Replace ref usage with direct store access:

-    const beginLineNumToLogEventNumRef = useRef<BeginLineNumToLogEventNumMap>(
-        beginLineNumToLogEventNum
-    );

     const handleCursorExplicitPosChange = useCallback((
         ev: monaco.editor.ICursorPositionChangedEvent
     ) => {
+        const {beginLineNumToLogEventNum} = useViewStore.getState();
         const newLogEventNum = getMapValueWithNearestLessThanOrEqualKey(
-            beginLineNumToLogEventNumRef.current,
+            beginLineNumToLogEventNum,
             ev.position.lineNumber
         );

And remove the synchronization useEffect (lines 269-272).

Also applies to: 192-194, 269-272

src/components/AppController.tsx (1)

130-133: ⚠️ Potential issue

Misleading comment about removing empty/falsy parameters.

The code spreads hashParams without any filtering, making this operation redundant since it updates the URL with the same values that were just read from it.

Remove the redundant URL update:

-    // Remove empty or falsy parameters.
-    updateWindowUrlHashParams({
-        ...hashParams,
-    });
src/utils/url.ts (2)

115-115: Type assertion is correctly used.

Based on the retrieved learnings, the as string type assertion is necessary here to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error when comparing enum values with strings from Object.entries().


248-254: 🧹 Nitpick (assertive)

Consider using a notification system for user feedback.

The console logging for clipboard operations provides developer feedback but may not be visible to end users. Consider integrating with a user-facing notification system if one exists in the application.

Would you like me to help implement user-facing notifications using the existing notification infrastructure?

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9bd0d and 9669cd5.

📒 Files selected for processing (4)
  • src/components/AppController.tsx (4 hunks)
  • src/components/Editor/index.tsx (4 hunks)
  • src/stores/logFileStore.ts (3 hunks)
  • src/utils/url.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}`: - Prefer `false == ` rather than `!`.

**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}: - Prefer false == <expression> rather than !<expression>.

  • src/stores/logFileStore.ts
  • src/components/Editor/index.tsx
  • src/components/AppController.tsx
  • src/utils/url.ts
🧠 Learnings (3)
src/components/Editor/index.tsx (2)
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.
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.
src/components/AppController.tsx (3)
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.
Learnt from: junhaoliao
PR: y-scope/yscope-log-viewer#265
File: src/stores/logFileStore.ts:125-125
Timestamp: 2025-05-17T05:13:39.947Z
Learning: When refactoring Zustand state imports to prevent circular dependencies in the yscope-log-viewer project, the preferred pattern is to move state retrievals closest to where they're actually used rather than retrieving them at the beginning of the function.
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/components/AppController.tsx:40-43
Timestamp: 2025-06-01T13:44:05.278Z
Learning: In the y-scope/yscope-log-viewer codebase, when functions return invalid or default log event numbers, they should use 0 as this aligns with URL_HASH_PARAMS_DEFAULT.LOG_EVENT_NUM which is set to 0. This maintains consistency with the established default hash parameter pattern.
src/utils/url.ts (1)
Learnt from: zzxthehappiest
PR: y-scope/yscope-log-viewer#286
File: src/utils/url.ts:46-46
Timestamp: 2025-05-25T02:45:44.362Z
Learning: In TypeScript, when comparing enum values with strings (like keys from Object.entries()), the type assertion `as string` is necessary to avoid the @typescript-eslint/no-unsafe-enum-comparison lint error. This is a valid use of type assertion for enum-to-string comparison.
🔇 Additional comments (17)
src/stores/logFileStore.ts (2)

1-1: Consider if the 70-line limit is appropriate for this store.

While limiting function length improves maintainability, the 70-line limit seems reasonable for the loadFile method which handles multiple async operations, error handling, and state updates.


24-24: Import path correctly updated.

The import has been properly migrated from the removed UrlContextProvider to the new utils/url module.

src/components/Editor/index.tsx (3)

33-33: Import path correctly updated.

The import has been properly migrated from the removed UrlContextProvider to the new utils/url module, and the .ts extension has been correctly removed as per past review comments.


127-178: Well-structured extraction of handleEditorCustomAction.

Moving this function to the global scope is a good refactoring that:

  • Improves code organization and readability
  • Reduces the component's complexity
  • Properly uses getState() to access store state and actions at the point of use
  • Correctly handles the prettify toggle with URL synchronization

The implementation follows the established patterns for Zustand store access.


264-267: Correct use of getState() for static setter.

The change properly uses getState() to retrieve setLogEventNum at the point of use, which aligns with the codebase pattern of using getState() for static setters that never change. This avoids unnecessary subscriptions.

src/components/AppController.tsx (6)

24-30: Import correctly updated to use new URL utilities.

The import has been properly migrated from the removed UrlContextProvider to the new utils/url module, with the .ts extension correctly removed as addressed in past reviews.


71-82: Clean implementation of updateViewHashParams.

The function properly updates view-related parameters from URL hash, using getState() to retrieve store actions. The implementation is straightforward and follows the established patterns.


84-112: updateQueryHashParams correctly tracks modifications.

The function properly:

  • Compares current and new query parameters to detect changes
  • Updates all query-related state
  • Returns whether any modifications occurred for triggering query restart

Good implementation that enables proper query management.


114-136: Well-structured handleHashChange implementation.

The function properly:

  • Parses hash parameters
  • Updates view and query parameters
  • Triggers query restart when needed during hash change events
  • Returns parsed parameters for use in initialization

Good separation of concerns between initialization and hash change events.


156-185: Initialization logic properly handles both hash and search parameters.

The useEffect:

  • Correctly uses isInitialized ref to prevent re-initialization in StrictMode
  • Registers the hash change handler
  • Handles initial URL parameters appropriately
  • Loads file with correct cursor based on presence of logEventNum

The implementation follows the suggestions from past reviews.


187-217: Page switching logic correctly uses direct store access.

The useEffect properly:

  • Retrieves state via getState() at point of use to avoid circular dependencies
  • Only depends on logEventNum as suggested in past reviews
  • Clamps the event number and handles page switching logic correctly

Good implementation following the established patterns.

src/utils/url.ts (6)

15-17: Consider the default filePath value based on past feedback.

The past review comments suggested making parameter values non-nullable and setting filePath to an empty string as the default. This implementation aligns with that feedback.


62-66: Helper function implementation is solid.

The isEmptyOrFalsy function correctly handles the three expected falsy cases: null, false, and empty strings. This follows the coding guideline preference for explicit boolean comparisons.


147-150: URL decoding logic is well-implemented.

The conditional decoding based on the absence of ambiguous URL characters is a clever solution to maintain human-readable URLs while avoiding parsing ambiguity. This handles the edge case mentioned in the comments effectively.


174-190: Hash parameter parsing handles all expected types correctly.

The function properly converts string values to their appropriate types (boolean, number) with safe fallbacks. The explicit handling of each parameter type ensures type safety.


299-303: Optimization prevents unnecessary history entries.

Both update functions correctly check if parameters have actually changed before calling pushState, preventing unnecessary browser history entries. This is good UX practice.

Also applies to: 317-321


327-340: Export organization follows alphabetical order.

The exports are well-organized and include all the necessary functions and constants for external consumption. The alphabetical ordering is maintained by the linter.

Comment on lines +81 to +96
if (searchParams.has(SEARCH_PARAM_NAMES.FILE_PATH)) {
// Extract filePath value by finding the parameter and taking everything after it
const filePathIndex = window.location.search.indexOf("filePath=");
if (-1 !== filePathIndex) {
const filePath = window.location.search.substring(filePathIndex + "filePath=".length);
if (0 !== filePath.length) {
let resolvedFilePath = filePath;
try {
resolvedFilePath = getAbsoluteUrl(filePath);
} catch (e) {
console.error("Unable to get absolute URL from filePath:", e);
}
parsedSearchParams[SEARCH_PARAM_NAMES.FILE_PATH] = resolvedFilePath;
}
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Review the filePath extraction approach for potential issues.

The current implementation uses indexOf and substring for filePath extraction, which could be fragile if multiple "filePath=" occurrences exist in the URL. However, based on past discussions, this approach was chosen to handle special cases where filePath contains its own URL parameters.

Consider adding a comment explaining why this manual approach is used instead of URLSearchParams.get() to clarify the design decision for future maintainers.

🤖 Prompt for AI Agents
In src/utils/url.ts around lines 81 to 96, the filePath extraction uses indexOf
and substring instead of URLSearchParams.get() to handle cases where filePath
contains its own URL parameters. Add a clear comment above this code block
explaining this design choice to clarify why the manual extraction approach is
necessary and prevent confusion for future maintainers.

@junhaoliao
Copy link
Member

@zzxthehappiest Again thanks for your contribution! I'm merging this now

@junhaoliao junhaoliao merged commit 7810e12 into y-scope:main Jun 9, 2025
6 of 8 checks passed
junhaoliao added a commit to junhaoliao/yscope-log-viewer that referenced this pull request Jun 18, 2025
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