Skip to content

fix: path for newly added collection & remove option for outside collections#6331

Merged
bijin-bruno merged 3 commits intousebruno:mainfrom
naman-bruno:bugfix/workspace-add/remove
Dec 6, 2025
Merged

fix: path for newly added collection & remove option for outside collections#6331
bijin-bruno merged 3 commits intousebruno:mainfrom
naman-bruno:bugfix/workspace-add/remove

Conversation

@naman-bruno
Copy link
Collaborator

@naman-bruno naman-bruno commented Dec 6, 2025

Description

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • Dynamic delete/remove confirmation that adapts title, confirm text, and messaging based on whether a collection is internal or truly deleted.
  • Bug Fixes

    • Improved cross-platform path handling so collections match reliably across Windows and Unix-style paths.
    • More robust loading/unloading and removal flows, preserving correct loaded state and messaging when switching or removing collections.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

Walkthrough

Applies a new normalizePath utility and uses it across UI components, Redux actions/reducers, and workspace utilities to normalize path comparisons, adjust internal/delete detection, and refine relative-path handling for workspace collections.

Changes

Cohort / File(s) Summary
UI - Sidebar & WorkspaceCollections
packages/bruno-app/src/components/Sidebar/Collections/index.js, packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js
Import and use normalizePath for comparisons; compute and propagate isInternal/isLoaded; adapt delete vs remove flow and modal text; use normalized matching for load/unload.
Redux Actions - Collections & Workspaces
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js, packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js
Import and apply normalizePath when matching/opening/removing collections; normalize open/loaded collection lists and filter logic; minor comment cleanup.
Redux Reducer - Workspaces Slice
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js
Use normalizePath for canonical comparison when removing collections from a workspace (replace prior dual path/location checks).
Workspace Utilities (Electron)
packages/bruno-electron/src/utils/workspace-config.js
makeRelativePath returns absolute if computed relative would ascend excessively; getWorkspaceCollections resolves non-absolute paths with path.resolve for correct absolute resolution.
Path Utility Addition
packages/bruno-app/src/utils/common/path.js
Add and export normalizePath(p) to convert backslashes to forward slashes and trim trailing slashes; exported alongside existing path helpers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Verify correctness and consistent usage of normalizePath across UI, actions, and reducer to avoid false mismatches.
  • Check removeCollectionFromWorkspace and related removal flows to ensure no unintended deletions when paths normalize to identical values.
  • Inspect makeRelativePath edge-case logic (excessive ".." handling) and path.resolve usage for platform-correct behavior.

Possibly related PRs

  • init: workspaces #6264 — Touches workspace/collection path handling and Redux workspace/collection code; likely related to these normalization/resolution fixes.

Suggested labels

size/XXL

Suggested reviewers

  • lohit-bruno
  • bijin-bruno
  • helloanoop

Poem

Backslashes bow, slashes stand tall,
Paths harmonize—no more a brawl,
Collections find their proper place,
Internal flags and trimmed-up space,
Code marches on with steadier pace. 🚶✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly describes the main changes: path normalization for newly added collections and removal logic for external collections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce1bf19 and e076956.

📒 Files selected for processing (1)
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - Windows

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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

🧹 Nitpick comments (1)
packages/bruno-electron/src/utils/workspace-config.js (1)

14-18: Clarify the "2 parent directories" threshold.

The check relativePath.split(path.sep).filter((s) => s === '..').length > 2 seems arbitrary. Why exactly 2 parent directories? This logic would benefit from a comment explaining the rationale—e.g., whether it's a security boundary, a UX convention, or a workaround for edge cases.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a8d787 and 978b44c.

📒 Files selected for processing (6)
  • packages/bruno-app/src/components/Sidebar/Collections/index.js (1 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (5 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (1 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (5 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js (1 hunks)
  • packages/bruno-electron/src/utils/workspace-config.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions. () => {} is good
No space between function name and parentheses. func() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
  • packages/bruno-app/src/components/Sidebar/Collections/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js
  • packages/bruno-electron/src/utils/workspace-config.js
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (8)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (1)

234-234: LGTM: Improved check using path.isAbsolute.

Replacing the startsWith('/') check with path.isAbsolute(c.path) is more robust and cross-platform (handles Windows paths like C:\...).

packages/bruno-app/src/providers/ReduxStore/slices/workspaces/index.js (1)

64-68: LGTM: Normalized path comparison prevents cross-platform issues.

The normalized path comparison ensures collections are correctly matched and removed regardless of path separator differences (backslash vs forward slash, trailing slashes). However, note the normalizePath duplication flagged in other files.

packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (1)

2288-2291: LGTM: Normalized workspace membership check.

The normalized path comparison ensures collections are correctly identified as already present in the workspace, preventing duplicate additions. The implementation is consistent with the broader normalization approach across the PR.

packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (3)

169-174: LGTM: Clear helper for internal collection detection.

The isInternalCollection helper correctly identifies collections within the workspace's collections/ folder using normalized paths. The startsWith check is safe here because paths are normalized first.


152-160: Improved UX: Dynamic delete/remove messaging.

The logic distinguishes between deleting internal non-git collections (permanent deletion) vs. removing external/git-backed collections (workspace reference removal only). The dynamic modal messaging clearly communicates this distinction to users, reducing confusion.

Also applies to: 235-259


181-182: Verify the isLoaded flag semantics.

The isLoaded flag is set to collection.isLoaded !== false (line 181) for matched collections and true (line 193) for workspace collections. This preserves existing behavior, but ensure that isLoaded: false is properly set for unloaded collections in the state to avoid false positives.

Also applies to: 193-194

packages/bruno-app/src/components/Sidebar/Collections/index.js (1)

20-23: LGTM: Normalized path filtering in sidebar.

The normalized path comparison ensures collections are correctly filtered for the active workspace, handling cross-platform path differences. The useMemo dependencies are correct. However, note the normalizePath duplication flagged in other files.

packages/bruno-electron/src/utils/workspace-config.js (1)

207-207: The change to path.resolve is intentional and properly tested; verify it aligns with your workspace path handling expectations.

The test file (workspace-config.spec.js) explicitly expects path.resolve behavior (line 63), indicating this change was deliberate. In practice, since workspace paths are typically absolute (selected via file dialogs or stored as absolute paths), path.resolve and path.join produce identical results. However, path.resolve is actually more robust: if workspacePath is ever relative, it normalizes to absolute relative to the current working directory, preventing subtle path bugs that path.join would propagate. No validation issues detected in the codebase regarding absolute paths.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/bruno-app/src/utils/common/path.js (1)

166-169: Add JSDoc documentation for consistency.

The other path utility functions in this file have comprehensive JSDoc comments, but normalizePath lacks documentation. Add a JSDoc comment explaining its purpose, parameters, return value, and cross-platform behavior.

Apply this diff:

+/**
+ * Normalize a path for cross-platform comparison.
+ *
+ * Converts backslashes to forward slashes and removes trailing slashes
+ * to ensure consistent path comparisons across Windows and Unix systems.
+ *
+ * @param {string} p - The path to normalize
+ * @returns {string} The normalized path, or empty string if input is falsy
+ *
+ * @example
+ * normalizePath('C:\\Users\\Project\\');
+ * → "C:/Users/Project"
+ *
+ * @example
+ * normalizePath('/home/user/project/');
+ * → "/home/user/project"
+ */
 const normalizePath = (p) => {
   if (!p) return '';
   return p.replace(/\\/g, '/').replace(/\/+$/, '');
 };
packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (2)

168-173: Consider moving isInternalCollection outside the component.

This helper function doesn't use hooks or component state, so it's recreated on every render. Moving it outside the component would improve performance slightly.

Apply this diff:

+const isInternalCollection = (workspace, collection) => {
+  if (!workspace.pathname || !collection.pathname) return false;
+  const workspaceCollectionsFolder = normalizePath(`${workspace.pathname}/collections`);
+  const collectionPath = normalizePath(collection.pathname);
+  return collectionPath.startsWith(workspaceCollectionsFolder);
+};
+
 const WorkspaceCollections = ({ workspace, onImportCollection }) => {
   // ... rest of component
-  const isInternalCollection = (collection) => {
-    if (!workspace.pathname || !collection.pathname) return false;
-    const workspaceCollectionsFolder = normalizePath(`${workspace.pathname}/collections`);
-    const collectionPath = normalizePath(collection.pathname);
-    return collectionPath.startsWith(workspaceCollectionsFolder);
-  };

Then update calls to pass workspace as first argument:

  • Line 171: isInternal: isInternalCollection(workspace, collection)
  • Line 193: isInternal: isInternalCollection(workspace, collection)

234-258: Refactor duplicate isDelete logic.

The computation of isDelete (whether to delete vs remove) appears in two places:

  • Line 151 in confirmRemoveCollection
  • Line 236 in the modal rendering IIFE

This violates DRY. Consider computing isDelete once and storing it in state alongside collectionToRemove.

Apply this diff:

-const [collectionToRemove, setCollectionToRemove] = useState(null);
+const [collectionToRemove, setCollectionToRemove] = useState(null);
+const [isDeleteOperation, setIsDeleteOperation] = useState(false);

Update handleRemoveCollection:

 const handleRemoveCollection = (collection) => {
+  const collectionInfo = getCollectionWorkspaceInfo(collection);
+  const isDelete = collectionInfo.isInternal && !collectionInfo.isGitBacked;
+  setIsDeleteOperation(isDelete);
   setCollectionToRemove(collection);
 };

Simplify confirmRemoveCollection:

 const confirmRemoveCollection = async () => {
   if (!collectionToRemove) return;

   try {
-    const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
-    const isDelete = collectionInfo.isInternal && !collectionInfo.isGitBacked;

     await dispatch(removeCollectionFromWorkspaceAction(workspace.uid, collectionToRemove.pathname));

-    if (isDelete) {
+    if (isDeleteOperation) {
       toast.success(`Deleted "${collectionToRemove.name}" collection`);
     } else {
       toast.success(`Removed "${collectionToRemove.name}" from workspace`);
     }

     setCollectionToRemove(null);
+    setIsDeleteOperation(false);

Simplify modal rendering:

-{collectionToRemove && (() => {
-  const collectionInfo = getCollectionWorkspaceInfo(collectionToRemove);
-  const isDelete = collectionInfo.isInternal && !collectionInfo.isGitBacked;
-
-  return (
+{collectionToRemove && (
     <Modal
       size="sm"
-      title={isDelete ? 'Delete Collection' : 'Remove Collection'}
+      title={isDeleteOperation ? 'Delete Collection' : 'Remove Collection'}
       handleCancel={() => setCollectionToRemove(null)}
       handleConfirm={confirmRemoveCollection}
-      confirmText={isDelete ? 'Delete' : 'Remove'}
+      confirmText={isDeleteOperation ? 'Delete' : 'Remove'}
       // ... rest of props
     >
       <p className="text-gray-600 dark:text-gray-300">
-        Are you sure you want to {isDelete ? 'delete' : 'remove'} <strong>"{collectionToRemove.name}"</strong>?
+        Are you sure you want to {isDeleteOperation ? 'delete' : 'remove'} <strong>"{collectionToRemove.name}"</strong>?
       </p>
       <p className="text-sm text-gray-500 dark:text-gray-400 mt-3">
-        {isDelete
+        {isDeleteOperation
           ? 'This will permanently delete the collection files from the workspace collections folder.'
           : 'This will remove the collection from the workspace. The collection files will not be deleted.'}
       </p>
     </Modal>
-  );
-})()}
+)}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 978b44c and ce1bf19.

📒 Files selected for processing (5)
  • packages/bruno-app/src/components/Sidebar/Collections/index.js (2 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (5 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (2 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (5 hunks)
  • packages/bruno-app/src/utils/common/path.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/bruno-app/src/components/Sidebar/Collections/index.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation. No tabs, just spaces
Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., )
Always add semicolons at the end of statements
No trailing commas
Always use parentheses around parameters in arrow functions, even for single params
For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline
No newlines inside function parentheses
Space before and after the arrow in arrow functions. () => {} is good
No space between function name and parentheses. func() not func ()
Semicolons go at the end of the line, not on a new line
Names for functions need to be concise and descriptive
Add in JSDoc comments to add more details to the abstractions if needed
Add in meaningful comments instead of obvious ones where complex code flow is explained properly

Files:

  • packages/bruno-app/src/utils/common/path.js
  • packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js
  • packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
🧬 Code graph analysis (3)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (2)
packages/bruno-js/src/sandbox/node-vm/index.js (1)
  • normalizedCollectionPath (198-198)
packages/bruno-app/src/utils/common/path.js (1)
  • normalizePath (166-169)
packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (2)
packages/bruno-app/src/utils/common/path.js (1)
  • normalizePath (166-169)
packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (2)
  • removeCollectionFromWorkspaceAction (120-162)
  • removeCollectionFromWorkspaceAction (120-162)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (1)
packages/bruno-app/src/utils/common/path.js (1)
  • normalizePath (166-169)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: CLI Tests
  • GitHub Check: Unit Tests
🔇 Additional comments (5)
packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js (1)

2288-2290: LGTM! Cross-platform path comparison implemented correctly.

The normalized path comparison ensures collections aren't duplicated when paths differ only in separators (backslash vs forward slash) or trailing slashes. This is essential for Windows/Unix interoperability.

packages/bruno-app/src/providers/ReduxStore/slices/workspaces/actions.js (3)

15-15: Past review concern addressed.

The previous review noted that normalizePath was defined inline in multiple files. This has now been correctly extracted to utils/common/path.js and imported consistently across all files.


233-233: Good fix: path.isAbsolute() is more robust than startsWith('/') for cross-platform support.

Windows absolute paths start with drive letters (e.g., C:\), not /, so the previous check would fail on Windows. Using path.isAbsolute() correctly handles both Unix and Windows paths.


170-170: Verify the await is necessary here.

Adding await before loadWorkspaceCollections ensures the load completes before subsequent filtering. However, verify that the downstream code at lines 173-185 actually depends on this completion. If the workspace collections can be loaded asynchronously without blocking, removing the await would improve performance.

packages/bruno-app/src/components/WorkspaceHome/WorkspaceCollections/index.js (1)

50-50: LGTM! Path normalization ensures accurate collection matching.

Using normalizePath on both sides of the comparison prevents mismatches when workspace and loaded collections have paths with different separators or trailing slashes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants