Skip to content

feat: Moved Workspace Selector to the Titlebar of the window.#6319

Merged
bijin-bruno merged 6 commits intousebruno:mainfrom
abhishek-bruno:feat/workspace-titlebar
Dec 5, 2025
Merged

feat: Moved Workspace Selector to the Titlebar of the window.#6319
bijin-bruno merged 6 commits intousebruno:mainfrom
abhishek-bruno:feat/workspace-titlebar

Conversation

@abhishek-bruno
Copy link
Member

@abhishek-bruno abhishek-bruno commented Dec 5, 2025

Description

  • Added new AppTitleBar components and styles.
  • Replaced sidebar TitleBar with SidebarHeader, updating layout and styles.
  • Tweaked sidebar collections and search styling/behavior; added bottom bar toggle icon.
  • Minor updates to app provider, Redux app slice, Electron entry, and draft indicator test.

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

    • New app title bar with centered branding, workspace switcher, and quick action buttons.
    • New sidebar header with view tabs, import/create actions, and integrated search.
  • Refactor

    • Sidebar reorganized to support multiple views and simplified collection rendering; title bar behavior moved into the header.
  • Style

    • Adjusted titlebar/sidebar sizing, spacing, scrollbars, icons, OS-specific window chrome, and main content height.
  • Tests

    • Updated test selectors and an external test endpoint.

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

… SidebarHeader, and enhance collections search functionality
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 5, 2025

Walkthrough

Adds a draggable 36px AppTitleBar and integrates it into the main layout; replaces Sidebar TitleBar with a new SidebarHeader (tabbed), refactors sidebar/collections search and styling, introduces OS-specific Electron titlebar options and fullscreen IPC, adds UI components/utilities, and updates tests/selectors.

Changes

Cohort / File(s) Summary
AppTitleBar (new)
packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js, packages/bruno-app/src/components/AppTitleBar/index.js
New 36px draggable title bar component — left (home/workspace dropdown), center branding, right action buttons; non-drag interactive regions; theme-aware styles; fullscreen IPC handling and workspace actions.
Sidebar header & TitleBar changes
SidebarHeader (new): packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js, packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js
TitleBar (removed): packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js, packages/bruno-app/src/components/Sidebar/TitleBar/index.js
Adds SidebarHeader (view tabs, collection actions, import/create/open flows, modals) and removes legacy TitleBar files.
Sidebar integration & layout
packages/bruno-app/src/components/Sidebar/index.js, packages/bruno-app/src/components/Sidebar/StyledWrapper.js
Replaces TitleBar usage with SidebarHeader, adds activeView/tab switching and second-tab placeholder, wires search toggle, updates min-width and layout/overflow tweaks.
Collections UI & search refactor
packages/bruno-app/src/components/Sidebar/Collections/index.js, packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js, packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js, packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js
Memoizes workspaceCollections via useMemo; replaces search markup with a StyledWrapper; adjusts placeholder text; introduces custom scrollbar styling and a clear control.
CollectionsHeader removed
packages/bruno-app/src/components/Sidebar/Collections/CollectionsHeader/StyledWrapper.js, packages/bruno-app/src/components/Sidebar/Collections/CollectionsHeader/index.js
Deleted CollectionsHeader component and its styled wrapper.
Collection item styling tweak
packages/bruno-app/src/components/Sidebar/Collections/Collection/StyledWrapper.js
Reduced .collection-name left padding from 8px → 4px (styling-only).
Icons & toolbar
packages/bruno-app/src/components/Icons/IconBottombarToggle/index.js, packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
New IconBottombarToggle SVG component; swapped IconFilesIconBox from @tabler/icons in CollectionToolBar.
Workspace & small UI updates
packages/bruno-app/src/components/WorkspaceHome/index.js, packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js
Updated CloseWorkspace import path to SidebarHeader; added data-testid="add-client-cert" to client cert Add button.
Pages & layout sizing
packages/bruno-app/src/pages/Bruno/index.js
Renders <AppTitleBar />; updates main content height calc to account for new title bar (22px → 60px and console-open variant).
Platform detection, app state, Electron main
packages/bruno-app/src/providers/App/index.js, packages/bruno-app/src/providers/ReduxStore/slices/app.js, packages/bruno-electron/src/index.js
Normalizes platform detection and early returns, adds explicit os-mac/os-windows/os-linux classes; bumps leftSidebarWidth default 222→250; Electron BrowserWindow uses per-OS titleBarStyle, titleBarOverlay, trafficLightPosition and forwards enter/leave-full-screen via IPC.
Workspace selector helper
packages/bruno-app/src/components/Sidebar/SidebarHeader/WorkspaceSelector/index.js, packages/bruno-app/src/utils/common/index.js
Workspace selector now imports shared toTitleCase; added new toTitleCase utility export.
StatusBar & sidebar toggle removal
packages/bruno-app/src/components/StatusBar/index.js
Removed sidebar toggle import/usage and the toggle UI block.
Tests updated
tests/collection/draft/draft-indicator.spec.ts, tests/footer/sidebar-toggle/sidebar-toggle.spec.js, tests/response/response-actions.spec.ts
Test selectors updated to use data-testid (add-client-cert, toggle-sidebar-button); one test endpoint URL changed.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as Renderer (React)
    participant AppTitleBar as AppTitleBar Component
    participant Redux as Redux Store
    participant Main as Electron Main

    Renderer->>Redux: read workspaces, activeWorkspace, preferences
    Renderer->>AppTitleBar: mount / render
    AppTitleBar->>Redux: dispatch switchWorkspace / savePreferences / toggleUI
    AppTitleBar->>Main: IPC -> open-workspace / toggle-devtools
    Main-->>AppTitleBar: IPC -> enter-full-screen / leave-full-screen
    AppTitleBar->>Renderer: apply .fullscreen class / update UI state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing focused review:
    • AppTitleBar: CSS drag vs non-drag regions, workspace dropdown async open flow, toast/error handling.
    • SidebarHeader: modal flows (import/create/open), sorting/close-multiple logic, integration replacing TitleBar.
    • Electron main: per-OS BrowserWindow options and duplicated fullscreen handler attachments.
    • Layout sizing and state change: main content height calc and leftSidebarWidth bump.

Possibly related PRs

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno

Poem

A slim new bar atop the screen,
Workspaces swap and tabs convene.
Electron whispers fullscreen true,
Sidebars shift and icons do.
Small fixes stitched — a fresher view. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main objective of this PR—moving the workspace selector to the window titlebar—which is directly reflected in the extensive changes to title bar components and layout.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

🧹 Nitpick comments (9)
packages/bruno-app/src/providers/ReduxStore/slices/app.js (1)

8-8: Sidebar width increased to accommodate new layout.

The initial width bump from 222px to 250px aligns with the AppTitleBar integration. Consider extracting layout constants (like INITIAL_SIDEBAR_WIDTH) for easier future maintenance, though this is optional.

tests/collection/draft/draft-indicator.spec.ts (1)

149-149: Good switch to test-id selector.

Using getByTestId('add-client-cert') is more robust than role-based lookup when multiple "Add" buttons exist on the page.

Per coding guidelines, consider extracting to a locator variable for clarity:

const addClientCertBtn = page.getByTestId('add-client-cert');
await addClientCertBtn.click();

This is optional given it's a single use.

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

1-1: Consolidate React imports.

The useMemo import should be combined with the existing React import on line 1 for consistency.

Apply this diff:

-import React, { useState } from 'react';
+import React, { useState, useMemo } from 'react';
 import { useSelector } from 'react-redux';
 import Collection from './Collection';
 import CreateCollection from '../CreateCollection';
 import StyledWrapper from './StyledWrapper';
 import CreateOrOpenCollection from './CreateOrOpenCollection';
 import CollectionSearch from './CollectionSearch/index';
-import { useMemo } from 'react';

Also applies to: 8-8

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

30-32: Remove redundant hover state.

The scrollbar thumb hover state applies the same color as the default state, making it redundant.

Apply this diff:

     &::-webkit-scrollbar-thumb {
       background: ${(props) => props.theme.scrollbar.color};
       border-radius: 3px;
     }
-
-    &::-webkit-scrollbar-thumb:hover {
-      background: ${(props) => props.theme.scrollbar.color};
-    }
   }
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js (1)

1-29: LGTM! Clean refactor to use StyledWrapper.

The component structure aligns well with the StyledWrapper CSS classes. One minor accessibility consideration: the clear icon div at line 21 lacks keyboard accessibility (no tabIndex, role, or onKeyDown). Consider using a <button> element instead for better a11y.

-      <div className="clear-icon" onClick={() => setSearchText('')}>
-        <IconX size={14} strokeWidth={1.5} />
-      </div>
+      <button
+        type="button"
+        className="clear-icon"
+        onClick={() => setSearchText('')}
+        aria-label="Clear search"
+      >
+        <IconX size={14} strokeWidth={1.5} />
+      </button>
packages/bruno-electron/src/index.js (1)

81-82: Consider reusing isMac from utils/arch.js.

isMac is already defined in packages/bruno-electron/src/utils/arch.js (line 1). Reusing it would avoid duplication. However, isWindows is new and would need to be added to that utility if you choose to consolidate.

-const isMac = process.platform === 'darwin';
-const isWindows = process.platform === 'win32';
+const { isMac } = require('./utils/arch');
+const isWindows = process.platform === 'win32';

Alternatively, extend utils/arch.js with isWindows and import both.

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

43-44: Consider combining these selectors.

Two consecutive useSelector calls access the same state.collections slice. You can merge them into a single selector for minor optimization.

-  const { collections } = useSelector((state) => state.collections);
-  const { collectionSortOrder } = useSelector((state) => state.collections);
+  const { collections, collectionSortOrder } = useSelector((state) => state.collections);
packages/bruno-app/src/components/AppTitleBar/index.js (2)

56-57: Unused ref and callback - dead code.

workspaceDropdownTippyRef is assigned but never read. Either remove it or implement the intended functionality (e.g., programmatically closing the dropdown).

-  const workspaceDropdownTippyRef = useRef();
-  const onWorkspaceDropdownCreate = (ref) => (workspaceDropdownTippyRef.current = ref);

If you need programmatic control, retain and use it; otherwise, remove to reduce clutter.


69-76: Component defined inside parent causes unnecessary re-renders.

WorkspaceName is recreated on every render since it's defined inside AppTitleBar. This breaks React's reconciliation and can cause focus/state issues. Wrap with useMemo or extract outside the component.

-  const WorkspaceName = forwardRef((props, ref) => {
-    return (
-      <div ref={ref} className="workspace-name-container" onClick={() => setShowWorkspaceDropdown(!showWorkspaceDropdown)}>
-        <span className="workspace-name">{toTitleCase(activeWorkspace?.name) || 'Default Workspace'}</span>
-        <IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
-      </div>
-    );
-  });
+  const WorkspaceName = useMemo(
+    () =>
+      forwardRef((props, ref) => (
+        <div ref={ref} className="workspace-name-container" onClick={() => setShowWorkspaceDropdown((prev) => !prev)}>
+          <span className="workspace-name">{toTitleCase(activeWorkspace?.name) || 'Default Workspace'}</span>
+          <IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
+        </div>
+      )),
+    [activeWorkspace?.name]
+  );

Also add useMemo to imports on line 2.

📜 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 80b017f and a877f0e.

📒 Files selected for processing (22)
  • packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/AppTitleBar/index.js (1 hunks)
  • packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js (1 hunks)
  • packages/bruno-app/src/components/Icons/IconBottombarToggle/index.js (1 hunks)
  • packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js (2 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/Collections/index.js (3 hunks)
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js (0 hunks)
  • packages/bruno-app/src/components/Sidebar/TitleBar/index.js (0 hunks)
  • packages/bruno-app/src/components/Sidebar/index.js (2 hunks)
  • packages/bruno-app/src/components/WorkspaceHome/index.js (1 hunks)
  • packages/bruno-app/src/pages/Bruno/index.js (3 hunks)
  • packages/bruno-app/src/providers/App/index.js (1 hunks)
  • packages/bruno-app/src/providers/ReduxStore/slices/app.js (1 hunks)
  • packages/bruno-electron/src/index.js (3 hunks)
  • tests/collection/draft/draft-indicator.spec.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/bruno-app/src/components/Sidebar/TitleBar/index.js
  • packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js
  • packages/bruno-app/src/providers/ReduxStore/slices/app.js
  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js
  • packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
  • packages/bruno-app/src/components/WorkspaceHome/index.js
  • packages/bruno-app/src/components/Sidebar/index.js
  • packages/bruno-app/src/components/Sidebar/StyledWrapper.js
  • packages/bruno-app/src/pages/Bruno/index.js
  • packages/bruno-app/src/providers/App/index.js
  • packages/bruno-app/src/components/Sidebar/Collections/Collection/StyledWrapper.js
  • packages/bruno-electron/src/index.js
  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js
  • packages/bruno-app/src/components/AppTitleBar/index.js
  • packages/bruno-app/src/components/Sidebar/Collections/index.js
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
  • tests/collection/draft/draft-indicator.spec.ts
  • packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js
  • packages/bruno-app/src/components/Icons/IconBottombarToggle/index.js
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Files:

  • tests/collection/draft/draft-indicator.spec.ts
tests/**/**.*

⚙️ CodeRabbit configuration file

tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:

  • Follow best practices for Playwright code and e2e automation

  • Try to reduce usage of page.waitForTimeout(); in code unless absolutely necessary and the locator cannot be found using existing expect() playwright calls

  • Avoid using page.pause() in code

  • Use locator variables for locators

  • Avoid using test.only

  • Use multiple assertions

  • Promote the use of test.step as much as possible so the generated reports are easier to read

  • Ensure that the fixtures like the collections are nested inside the fixtures folder

    Fixture Example*: Here's an example of possible fixture and test pair

    .
    ├── fixtures
    │   └── collection
    │       ├── base.bru
    │       ├── bruno.json
    │       ├── collection.bru
    │       ├── ws-test-request-with-headers.bru
    │       ├── ws-test-request-with-subproto.bru
    │       └── ws-test-request.bru
    ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection
    ├── headers.spec.ts
    ├── persistence.spec.ts
    ├── variable-interpolation
    │   ├── fixtures
    │   │   └── collection
    │   │       ├── environments
    │   │       ├── bruno.json
    │   │       └── ws-interpolation-test.bru
    │   ├── init-user-data
    │   └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection
    └── subproto.spec.ts
    

Files:

  • tests/collection/draft/draft-indicator.spec.ts
🧠 Learnings (2)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles

Applied to files:

  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js
  • packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS may change layout, but Tailwind classes should not define colors

Applied to files:

  • packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js
  • packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
🧬 Code graph analysis (8)
packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (10)
packages/bruno-app/src/components/AppTitleBar/index.js (3)
  • dispatch (20-20)
  • useSelector (43-43)
  • activeWorkspace (47-47)
packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js (1)
  • dispatch (12-12)
packages/bruno-app/src/components/Sidebar/index.js (2)
  • dispatch (20-20)
  • activeView (18-18)
packages/bruno-app/src/components/WorkspaceHome/index.js (3)
  • dispatch (17-17)
  • useSelector (18-18)
  • activeWorkspace (33-33)
packages/bruno-app/src/providers/App/index.js (1)
  • dispatch (16-16)
packages/bruno-app/src/components/Sidebar/SidebarHeader/CloseWorkspace/index.js (2)
  • dispatch (9-9)
  • useSelector (10-10)
packages/bruno-app/src/components/Sidebar/Collections/index.js (3)
  • useSelector (12-12)
  • useSelector (13-13)
  • activeWorkspace (16-16)
packages/bruno-app/src/components/Sidebar/CreateCollection/index.js (1)
  • CreateCollection (22-341)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-25)
packages/bruno-app/src/components/Sidebar/Collections/RemoveCollectionsModal/index.js (1)
  • RemoveCollectionsModal (48-271)
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js (1)
packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1)
  • StyledWrapper (3-113)
packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js (3)
packages/bruno-app/src/components/Sidebar/Collections/Collection/StyledWrapper.js (1)
  • Wrapper (3-113)
packages/bruno-app/src/components/Sidebar/Collections/StyledWrapper.js (1)
  • Wrapper (3-34)
packages/bruno-app/src/components/Sidebar/StyledWrapper.js (1)
  • Wrapper (3-65)
packages/bruno-app/src/pages/Bruno/index.js (1)
packages/bruno-app/src/components/AppTitleBar/index.js (2)
  • AppTitleBar (19-245)
  • isConsoleOpen (46-46)
packages/bruno-electron/src/index.js (1)
packages/bruno-electron/src/utils/arch.js (1)
  • isMac (2-2)
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js (2)
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js (1)
  • StyledWrapper (3-63)
packages/bruno-app/src/components/Sidebar/Collections/index.js (1)
  • searchText (11-11)
packages/bruno-app/src/components/AppTitleBar/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/app.js (2)
  • savePreferences (132-142)
  • savePreferences (132-142)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-25)
packages/bruno-app/src/utils/workspaces/index.js (1)
  • isPinned (51-51)
packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1)
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js (1)
  • StyledWrapper (3-63)
⏰ 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: Unit Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Linux
🔇 Additional comments (23)
packages/bruno-app/src/components/Sidebar/StyledWrapper.js (1)

57-64: LGTM! Clean placeholder implementation.

The flexbox centering and theme-based muted color are appropriate for a tab placeholder. The implementation follows the established pattern in this file and adheres to the coding guidelines.

packages/bruno-app/src/components/CollectionSettings/ClientCertSettings/index.js (1)

376-378: LGTM!

Adding data-testid attribute enables stable test automation selectors. This aligns with the test update in draft-indicator.spec.ts.

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

8-8: LGTM!

Reduced left padding tightens the collection name layout. Straightforward styling adjustment.

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

9-9: Verify CloseWorkspace export from new location.

The CloseWorkspace component import path has been updated to components/Sidebar/SidebarHeader/CloseWorkspace. Confirm that:

  • The component file exists at the new location
  • The component is properly exported from SidebarHeader
  • No other files still reference the old import path from TitleBar
packages/bruno-app/src/components/RequestTabs/CollectionToolBar/index.js (1)

3-3: LGTM—Icon swap for visual consistency.

The change from IconFiles to IconBox aligns with the broader UI refresh. The icon semantics (box/container) may better represent a collection.

Also applies to: 48-48

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

18-23: LGTM—Memoized filtering improves performance.

The memoized workspace collections filter is clean and correctly memoized with appropriate dependencies.


45-45: Verify layout behavior with static className.

The className change from dynamic (dependent on showSearch) to static may affect layout. Ensure search visibility transitions don't cause layout shifts by testing the sidebar behavior when toggling search visibility.

packages/bruno-app/src/components/Icons/IconBottombarToggle/index.js (1)

3-14: LGTM—Clean icon component with conditional rendering.

The component structure and conditional rect rendering for the collapsed state are well-implemented.

packages/bruno-app/src/pages/Bruno/index.js (2)

8-8: LGTM—AppTitleBar integrated cleanly.

The new AppTitleBar component is properly imported and rendered.

Also applies to: 91-91


110-110: Simplify redundant conditional and verify AppTitleBar height.

The height calculation has a redundant nested ternary: when isConsoleOpen is true, the inner isConsoleOpen ? '300px' : '0px' will always return '300px'. Also, verify that 60px matches the actual AppTitleBar height.

Apply this diff to simplify:

-          height: isConsoleOpen ? `calc(100vh - 60px - ${isConsoleOpen ? '300px' : '0px'})` : 'calc(100vh - 60px)'
+          height: isConsoleOpen ? 'calc(100vh - 60px - 300px)' : 'calc(100vh - 60px)'
packages/bruno-app/src/providers/App/index.js (1)

23-41: LGTM—Cleaner platform detection logic.

The refactor improves readability with includes(), upfront toLowerCase(), and early returns. The empty string guard is good defensive coding.

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

1-1: LGTM—SidebarHeader integration and view management.

The replacement of TitleBar with SidebarHeader and new view switching logic is clean. The placeholder for future tab content is appropriate.

Also applies to: 17-18, 85-99


9-9: Verify minimum sidebar width increase doesn't conflict with responsive design.

The MIN_LEFT_SIDEBAR_WIDTH increased from 221px to 250px (line 9). Ensure this change doesn't break layouts on smaller screens or conflict with existing responsive breakpoints. Check how this constant is used throughout the application and whether responsive constraints accommodate the new 250px minimum.

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

3-28: LGTM—Clean flex layout with proper theme usage.

The flex layout and scrollbar styling follow the styled-components pattern correctly, with appropriate theme token usage.

Based on learnings, Styled Components correctly define layout and use theme tokens for colors.

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

1-65: LGTM—Comprehensive search input styling with proper theming.

The styled wrapper follows best practices with consistent theme token usage, smooth transitions, and well-defined interaction states.

Based on learnings, Styled Components correctly handle all styling including colors and layout.

packages/bruno-electron/src/index.js (2)

123-127: LGTM! OS-specific title bar configuration looks correct.

The conditional logic for titleBarStyle, titleBarOverlay, and trafficLightPosition properly handles macOS, Windows, and Linux scenarios. The 36px overlay height aligns with the AppTitleBar height defined in StyledWrapper.js.


175-182: LGTM! Full-screen IPC events properly wired.

These handlers correctly notify the renderer process about full-screen state changes, allowing the AppTitleBar to adjust padding (e.g., removing macOS traffic light space in fullscreen).

packages/bruno-app/src/components/AppTitleBar/StyledWrapper.js (1)

1-248: LGTM! Well-structured cross-platform title bar styling.

Good coverage for:

  • macOS traffic light spacing (line 19) and fullscreen adjustment (lines 24-26)
  • Windows caption button reservation (lines 241-245)
  • Proper drag regions with no-drag exceptions for interactive elements

The fallback chain at line 203 (props.theme.workspace?.accent || props.theme.colors?.text?.yellow) is a nice defensive pattern. Based on learnings, this correctly uses Styled Components for styling without Tailwind color definitions.

packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (3)

35-50: LGTM! Component setup is clean.

Good integration with Redux for workspace and collection state. The modal state management pattern with separate boolean flags is clear and maintainable.


52-77: LGTM! Import flow handles workspace types correctly.

The branching logic for default vs. non-default workspaces (lines 55-63) correctly routes to different import flows. Error handling with toast notifications is appropriate.


180-291: LGTM! Dropdown menus well-structured.

Good use of Tippy refs for programmatic dropdown control. The sort icon/label helpers (lines 255-259) provide clear visual feedback for the current sort state.

packages/bruno-app/src/components/AppTitleBar/index.js (2)

19-247: Overall structure looks good.

The component is well-organized with clear separation of concerns between state management, event handlers, and rendering. Once the IPC cleanup issue is fixed and the WorkspaceName component recreation is addressed, this will be solid.


88-96: Success toast may fire even when user cancels the dialog.

If openWorkspaceDialog resolves when the user cancels (without actually opening a workspace), the success toast would be misleading. Verify the action rejects or returns a distinguishable result on cancel.

Comment on lines +24 to +40
useEffect(() => {
const { ipcRenderer } = window;
if (!ipcRenderer) return;

const handleEnterFullScreen = ipcRenderer.on('main:enter-full-screen', () => {
setIsFullScreen(true);
});

const handleLeaveFullScreen = ipcRenderer.on('main:leave-full-screen', () => {
setIsFullScreen(false);
});

return () => {
if (handleEnterFullScreen) handleEnterFullScreen();
if (handleLeaveFullScreen) handleLeaveFullScreen();
};
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

IPC listener cleanup is incorrect and will cause memory leaks.

ipcRenderer.on() returns the ipcRenderer instance, not an unsubscribe function. The current cleanup does nothing, leading to accumulated listeners on component remount.

   useEffect(() => {
     const { ipcRenderer } = window;
     if (!ipcRenderer) return;

-    const handleEnterFullScreen = ipcRenderer.on('main:enter-full-screen', () => {
+    const onEnterFullScreen = () => {
       setIsFullScreen(true);
-    });
+    };

-    const handleLeaveFullScreen = ipcRenderer.on('main:leave-full-screen', () => {
+    const onLeaveFullScreen = () => {
       setIsFullScreen(false);
-    });
+    };
+
+    ipcRenderer.on('main:enter-full-screen', onEnterFullScreen);
+    ipcRenderer.on('main:leave-full-screen', onLeaveFullScreen);

     return () => {
-      if (handleEnterFullScreen) handleEnterFullScreen();
-      if (handleLeaveFullScreen) handleLeaveFullScreen();
+      ipcRenderer.removeListener('main:enter-full-screen', onEnterFullScreen);
+      ipcRenderer.removeListener('main:leave-full-screen', onLeaveFullScreen);
     };
   }, []);
🤖 Prompt for AI Agents
In packages/bruno-app/src/components/AppTitleBar/index.js around lines 24 to 40
the code assigns the return value of ipcRenderer.on() to variables and calls
them in cleanup, but ipcRenderer.on() returns the ipcRenderer object (not an
unsubscribe), so listeners are never removed; fix by defining named handler
functions (e.g. const onEnter = () => setIsFullScreen(true); const onLeave = ()
=> setIsFullScreen(false)), register them with
ipcRenderer.on('main:enter-full-screen', onEnter) and
ipcRenderer.on('main:leave-full-screen', onLeave), and in the cleanup call
ipcRenderer.removeListener('main:enter-full-screen', onEnter) and
ipcRenderer.removeListener('main:leave-full-screen', onLeave) (or
ipcRenderer.off if available), keeping the existing ipcRenderer existence guard.

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

♻️ Duplicate comments (1)
packages/bruno-app/src/components/AppTitleBar/index.js (1)

24-40: Fix fullscreen IPC listener cleanup – current code leaks listeners.

ipcRenderer.on() returns the ipcRenderer instance, not an unsubscribe function, so removeEnterFullScreenListener() / removeLeaveFullScreenListener() in cleanup are no-ops and handlers will accumulate on remount.

Suggested fix:

   useEffect(() => {
     const { ipcRenderer } = window;
     if (!ipcRenderer) return;
 
-    const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => {
-      setIsFullScreen(true);
-    });
-
-    const removeLeaveFullScreenListener = ipcRenderer.on('main:leave-full-screen', () => {
-      setIsFullScreen(false);
-    });
-
-    return () => {
-      removeEnterFullScreenListener();
-      removeLeaveFullScreenListener();
-    };
+    const onEnterFullScreen = () => {
+      setIsFullScreen(true);
+    };
+
+    const onLeaveFullScreen = () => {
+      setIsFullScreen(false);
+    };
+
+    ipcRenderer.on('main:enter-full-screen', onEnterFullScreen);
+    ipcRenderer.on('main:leave-full-screen', onLeaveFullScreen);
+
+    return () => {
+      ipcRenderer.removeListener('main:enter-full-screen', onEnterFullScreen);
+      ipcRenderer.removeListener('main:leave-full-screen', onLeaveFullScreen);
+    };
   }, []);
🧹 Nitpick comments (5)
packages/bruno-app/src/components/AppTitleBar/index.js (3)

60-67: Tighten toTitleCase split regex to avoid ambiguous - range.

str.split(/[\s-_]+/) uses - inside a character class in a slightly ambiguous way; more explicit is safer and clearer.

-    return str
-      .split(/[\s-_]+/)
+    return str
+      .split(/[\s_-]+/)
       .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
       .join(' ');

69-76: Use functional state update when toggling dropdown visibility.

Using setShowWorkspaceDropdown(!showWorkspaceDropdown) can suffer from stale closures under rapid updates; the functional form is more robust.

-      <div ref={ref} className="workspace-name-container" onClick={() => setShowWorkspaceDropdown(!showWorkspaceDropdown)}>
+      <div
+        ref={ref}
+        className="workspace-name-container"
+        onClick={() => setShowWorkspaceDropdown((prev) => !prev)}
+      >

88-96: Verify openWorkspaceDialog async behavior and success/error toasts.

Right now you always await dispatch(openWorkspaceDialog()) and unconditionally show a success toast unless an exception is thrown. In many Redux setups (e.g. RTK thunks), dispatch(thunk) resolves even on failure unless you explicitly unwrap or inspect meta, so errors/cancels might still show “Workspace opened successfully”.

Consider something along these lines (depending on how openWorkspaceDialog is implemented):

const resultAction = await dispatch(openWorkspaceDialog());

if (openWorkspaceDialog.fulfilled.match(resultAction)) {
  toast.success('Workspace opened successfully');
} else if (openWorkspaceDialog.rejected.match(resultAction)) {
  const message = resultAction.error?.message || 'Failed to open workspace';
  toast.error(message);
}

Please double‑check the actual action shape and adjust accordingly.

packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (2)

39-45: Combine collection selectors into a single useSelector call.

You can destructure collections and collectionSortOrder in one useSelector to avoid duplicate subscriptions and keep the state derivation in one place.

-  // Get collection sort order
-  const { collections } = useSelector((state) => state.collections);
-  const { collectionSortOrder } = useSelector((state) => state.collections);
+  // Collections list and current sort order
+  const { collections, collectionSortOrder } = useSelector((state) => state.collections);

35-347: Consider adding JSDoc for SidebarHeader props and behavior.

Given this component coordinates view switching, search visibility, modals, and Electron devtools, a brief JSDoc on SidebarHeader documenting setShowSearch, activeView, and onViewChange would help future maintainers.

+/**
+ * Sidebar header for the left panel: renders the current view label/tabs,
+ * collections actions (search, add/import/open, sort, close all, devtools),
+ * and manages the related modals.
+ *
+ * @param {Object} props
+ * @param {(updater: (prev: boolean) => boolean) => void} [props.setShowSearch] Toggle for the sidebar search bar.
+ * @param {string} [props.activeView='collections'] Currently active sidebar view id.
+ * @param {(viewId: string) => void} [props.onViewChange] Callback when the active view tab changes.
+ */
 const SidebarHeader = ({ setShowSearch, activeView = 'collections', onViewChange }) => {

As per coding guidelines, this brings the abstraction in line with our JSDoc expectations.

📜 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 a877f0e and b0ff54d.

📒 Files selected for processing (3)
  • packages/bruno-app/src/components/AppTitleBar/index.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (1 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, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js
  • packages/bruno-app/src/components/AppTitleBar/index.js
🧠 Learnings (4)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Components should be used as wrappers to define both self and children component styles; Tailwind classes should be used specifically for layout-based styles

Applied to files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{jsx,tsx} : Styled Component CSS may change layout, but Tailwind classes should not define colors

Applied to files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.123Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.123Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : Space before and after the arrow in arrow functions: `() => {}`

Applied to files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
📚 Learning: 2025-12-03T08:09:57.123Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.123Z
Learning: Applies to **/*.{js,jsx,ts,tsx} : For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting

Applied to files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js
🧬 Code graph analysis (2)
packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1)
packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/StyledWrapper.js (1)
  • StyledWrapper (3-63)
packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (8)
packages/bruno-app/src/components/Sidebar/SidebarHeader/WorkspaceSelector/index.js (3)
  • dispatch (14-14)
  • useSelector (16-16)
  • activeWorkspace (18-18)
packages/bruno-app/src/components/Sidebar/index.js (2)
  • dispatch (20-20)
  • activeView (18-18)
packages/bruno-app/src/providers/App/index.js (1)
  • dispatch (16-16)
packages/bruno-app/src/components/Sidebar/Collections/index.js (3)
  • useSelector (12-12)
  • useSelector (13-13)
  • activeWorkspace (16-16)
packages/bruno-app/src/components/Sidebar/CreateCollection/index.js (1)
  • CreateCollection (22-341)
packages/bruno-app/src/components/Sidebar/ImportCollection/index.js (1)
  • ImportCollection (36-170)
packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js (1)
  • ImportCollectionLocation (84-231)
packages/bruno-js/src/sandbox/quickjs/index.js (1)
  • e (79-79)
⏰ 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: CLI Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - macOS
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
🔇 Additional comments (3)
packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1)

3-108: SidebarHeader StyledWrapper CSS and theme usage look correct.

The selector structure is clean (no stray braces/props), and the layout + color usage is consistently driven by theme.sidebar/theme.dropdown tokens, including sane fallbacks on hover. This should integrate well with existing sidebar theming and avoids the CSS parsing issues noted in earlier reviews.

Based on learnings, this matches our styled‑components vs. Tailwind usage guidelines.

packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (2)

178-290: Collections actions and dropdown flows are cohesive and match existing patterns.

The search toggle, add/import/open flows, sort/close-all, and devtools actions are wired cleanly with tippy refs and toasts, and the collectionsToClose state plus RemoveCollectionsModal hook-in keeps the close-all flow localized to the header.


37-38: Guard ipcRenderer usage so Devtools action is safe outside Electron.

If this component renders where window.ipcRenderer is missing or stubbed, ipcRenderer.invoke will throw. Add optional chaining to protect against undefined ipcRenderer:

-    ipcRenderer.invoke('renderer:open-devtools');
+    if (ipcRenderer?.invoke) {
+      ipcRenderer.invoke('renderer:open-devtools');
+    }

This applies to lines 37-38 and 148-150. Verify your preload exposes ipcRenderer conditionally and check if other call sites use a similar defensive pattern.

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

♻️ Duplicate comments (1)
packages/bruno-app/src/components/AppTitleBar/index.js (1)

23-40: Fix ipcRenderer.on listener cleanup to prevent memory leaks

ipcRenderer.on() returns the ipcRenderer instance, not an unsubscribe function, so calling removeEnterFullScreenListener() / removeLeaveFullScreenListener() in the cleanup does nothing and listeners will accumulate on remount.

   useEffect(() => {
     const { ipcRenderer } = window;
     if (!ipcRenderer) return;
 
-    const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => {
-      setIsFullScreen(true);
-    });
-
-    const removeLeaveFullScreenListener = ipcRenderer.on('main:leave-full-screen', () => {
-      setIsFullScreen(false);
-    });
-
-    return () => {
-      removeEnterFullScreenListener();
-      removeLeaveFullScreenListener();
-    };
+    const handleEnterFullScreen = () => {
+      setIsFullScreen(true);
+    };
+
+    const handleLeaveFullScreen = () => {
+      setIsFullScreen(false);
+    };
+
+    ipcRenderer.on('main:enter-full-screen', handleEnterFullScreen);
+    ipcRenderer.on('main:leave-full-screen', handleLeaveFullScreen);
+
+    return () => {
+      ipcRenderer.removeListener('main:enter-full-screen', handleEnterFullScreen);
+      ipcRenderer.removeListener('main:leave-full-screen', handleLeaveFullScreen);
+    };
   }, []);
🧹 Nitpick comments (2)
packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (1)

39-45: Merge collections selectors into a single useSelector call

You’re selecting from state.collections twice; combining them avoids duplicate subscriptions and keeps the slice snapshot consistent.

-  const { collections } = useSelector((state) => state.collections);
-  const { collectionSortOrder } = useSelector((state) => state.collections);
+  const { collections, collectionSortOrder } = useSelector((state) => state.collections);
packages/bruno-app/src/components/AppTitleBar/index.js (1)

59-67: Make the toTitleCase split regex explicit

/[\s-_]+/ is a bit ambiguous because of the - inside the character class. Using /[\s_-]+/ makes it clear you’re splitting on whitespace, hyphen, or underscore.

-    return str
-      .split(/[\s-_]+/)
+    return str
+      .split(/[\s_-]+/)
       .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
       .join(' ');
📜 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 b0ff54d and caf1468.

📒 Files selected for processing (2)
  • packages/bruno-app/src/components/AppTitleBar/index.js (1 hunks)
  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (1 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, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js
  • packages/bruno-app/src/components/AppTitleBar/index.js
🧬 Code graph analysis (2)
packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (4)
packages/bruno-app/src/components/Sidebar/ImportCollection/index.js (1)
  • ImportCollection (36-170)
packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js (1)
  • ImportCollectionLocation (84-231)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-25)
packages/bruno-app/src/components/Sidebar/SidebarHeader/StyledWrapper.js (1)
  • StyledWrapper (3-108)
packages/bruno-app/src/components/AppTitleBar/index.js (3)
packages/bruno-app/src/providers/ReduxStore/slices/app.js (2)
  • savePreferences (132-142)
  • savePreferences (132-142)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-25)
packages/bruno-app/src/utils/workspaces/index.js (1)
  • isPinned (51-51)
⏰ 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: CLI Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - Linux
🔇 Additional comments (1)
packages/bruno-app/src/components/Sidebar/SidebarHeader/index.js (1)

151-153: Guard ipcRenderer in openDevTools to avoid runtime errors

If this component is shared between Electron and web builds, unguarded ipcRenderer access will throw at runtime. Add a defensive check before invoking.

-  const openDevTools = () => {
-    ipcRenderer.invoke('renderer:open-devtools');
-  };
+  const openDevTools = () => {
+    if (!ipcRenderer?.invoke) {
+      return;
+    }
+
+    ipcRenderer.invoke('renderer:open-devtools');
+  };

}}
>
<IconDeviceDesktop size={16} stroke={1.5} className="icon" />
Devtools
Copy link
Collaborator

Choose a reason for hiding this comment

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

Rename to Dev Utils. Update functionality accordingly.

<TitleBar />
<Collections />
<div className="flex flex-col flex-grow" style={{ minHeight: 0, overflow: 'hidden' }}>
<SidebarHeader
Copy link
Collaborator

Choose a reason for hiding this comment

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

Delete CollectionHeader component if not in use anymore.

const onWorkspaceDropdownCreate = (ref) => (workspaceDropdownTippyRef.current = ref);

// Helper function to convert workspace names to title case
const toTitleCase = (str) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Move to utils

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

♻️ Duplicate comments (1)
packages/bruno-app/src/components/AppTitleBar/index.js (1)

25-41: Fix Electron IPC listener cleanup; current code leaks listeners and will throw on unmount.

ipcRenderer.on() returns the ipcRenderer instance, not an unsubscribe function. Calling the returned value as a function in cleanup both fails to remove listeners and is likely to throw. Use named handlers and removeListener instead.

-  useEffect(() => {
-    const { ipcRenderer } = window;
-    if (!ipcRenderer) return;
-
-    const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => {
-      setIsFullScreen(true);
-    });
-
-    const removeLeaveFullScreenListener = ipcRenderer.on('main:leave-full-screen', () => {
-      setIsFullScreen(false);
-    });
-
-    return () => {
-      removeEnterFullScreenListener();
-      removeLeaveFullScreenListener();
-    };
-  }, []);
+  useEffect(() => {
+    const { ipcRenderer } = window;
+    if (!ipcRenderer) return;
+
+    const onEnterFullScreen = () => {
+      setIsFullScreen(true);
+    };
+
+    const onLeaveFullScreen = () => {
+      setIsFullScreen(false);
+    };
+
+    ipcRenderer.on('main:enter-full-screen', onEnterFullScreen);
+    ipcRenderer.on('main:leave-full-screen', onLeaveFullScreen);
+
+    return () => {
+      ipcRenderer.removeListener('main:enter-full-screen', onEnterFullScreen);
+      ipcRenderer.removeListener('main:leave-full-screen', onLeaveFullScreen);
+    };
+  }, []);
🧹 Nitpick comments (6)
tests/footer/sidebar-toggle/sidebar-toggle.spec.js (2)

7-7: Prefer accessibility-based selectors over testId.

Changing from getByLabel('Toggle Sidebar') to getByTestId('toggle-sidebar-button') moves away from user-facing and accessibility-based testing. Playwright recommends getByRole, getByLabel, or getByText over getByTestId as they verify proper accessible labeling and test how users interact with the UI.

Consider reverting to a semantic selector if the button has proper accessible labeling:

-    const toggleButton = page.getByTestId('toggle-sidebar-button');
+    const toggleButton = page.getByRole('button', { name: 'Toggle Sidebar' });

Or verify the button's accessibility attributes in the implementation to confirm which selector is appropriate.


3-36: Optional: Consider using test.step for better reporting.

While not required for this straightforward test, wrapping logical sections in test.step would improve readability in generated reports, as recommended in the coding guidelines.

Example refactor:

test('should toggle sidebar visibility when clicking the toggle button', async ({ page }) => {
  await test.step('Verify initial sidebar visibility', async () => {
    const sidebar = page.locator('aside.sidebar');
    const dragHandle = page.locator('.sidebar-drag-handle');
    await expect(sidebar).toBeVisible();
    await expect(dragHandle).toBeVisible();
  });

  await test.step('Hide sidebar', async () => {
    const toggleButton = page.getByTestId('toggle-sidebar-button');
    const sidebar = page.locator('aside.sidebar');
    const dragHandle = page.locator('.sidebar-drag-handle');
    await toggleButton.click();
    await expect(sidebar).not.toBeVisible();
    await expect(dragHandle).not.toBeVisible();
    const sidebarBox = await sidebar.boundingBox();
    expect(sidebarBox?.width).toBe(0);
  });

  await test.step('Show sidebar again', async () => {
    const toggleButton = page.getByTestId('toggle-sidebar-button');
    const sidebar = page.locator('aside.sidebar');
    const dragHandle = page.locator('.sidebar-drag-handle');
    await toggleButton.click();
    await expect(sidebar).toBeVisible();
    await expect(dragHandle).toBeVisible();
    const expandedSidebarBox = await sidebar.boundingBox();
    expect(expandedSidebarBox?.width).toBeGreaterThan(0);
  });
});
packages/bruno-app/src/components/AppTitleBar/index.js (4)

55-59: Drop unused Tippy ref for workspace dropdown unless you actually need it.

workspaceDropdownTippyRef and onWorkspaceDropdownCreate are assigned but never read, so they add noise without behavior. Either wire them up or remove them (and the onCreate prop).

   const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false);
   const [showWorkspaceDropdown, setShowWorkspaceDropdown] = useState(false);
-  const workspaceDropdownTippyRef = useRef();
-  const onWorkspaceDropdownCreate = (ref) => (workspaceDropdownTippyRef.current = ref);

And in the dropdown JSX:

-          <Dropdown
-            onCreate={onWorkspaceDropdownCreate}
-            icon={<WorkspaceName />}
+          <Dropdown
+            icon={<WorkspaceName />}
             placement="bottom-start"
             style="new"
             visible={showWorkspaceDropdown}
             onClickOutside={() => setShowWorkspaceDropdown(false)}
           >

73-77: Guard the workspace switch toast against missing names and align casing with the UI.

If workspaces.find doesn’t find a match, the toast will show undefined, and the casing is currently raw. You already have toTitleCase; using it here keeps things consistent and safer.

   const handleWorkspaceSwitch = (workspaceUid) => {
     dispatch(switchWorkspace(workspaceUid));
     setShowWorkspaceDropdown(false);
-    toast.success(`Switched to ${workspaces.find((w) => w.uid === workspaceUid)?.name}`);
+    const workspace = workspaces.find((w) => w.uid === workspaceUid);
+    toast.success(`Switched to ${toTitleCase(workspace?.name) || 'workspace'}`);
   };

101-125: Clarify layout toggle preferences update and defensively handle undefined preferences.

Spreading preferences assumes it’s always non-null, and ...preferences?.layout || {} relies on operator precedence. A small refactor makes the intent clearer and safer if preferences starts out undefined.

-  const orientation = preferences?.layout?.responsePaneOrientation || 'horizontal';
-
-  const handleToggleVerticalLayout = () => {
-    const newOrientation = orientation === 'horizontal' ? 'vertical' : 'horizontal';
-    const updatedPreferences = {
-      ...preferences,
-      layout: {
-        ...preferences?.layout || {},
-        responsePaneOrientation: newOrientation
-      }
-    };
-    dispatch(savePreferences(updatedPreferences));
-  };
+  const orientation = preferences?.layout?.responsePaneOrientation || 'horizontal';
+
+  const handleToggleVerticalLayout = () => {
+    const newOrientation = orientation === 'horizontal' ? 'vertical' : 'horizontal';
+    const layout = preferences?.layout || {};
+    const updatedPreferences = {
+      ...(preferences || {}),
+      layout: {
+        ...layout,
+        responsePaneOrientation: newOrientation
+      }
+    };
+    dispatch(savePreferences(updatedPreferences));
+  };

20-20: Add a brief JSDoc comment for AppTitleBar to document its role.

Given this is a central abstraction in the new layout, a short JSDoc will help future readers understand its responsibilities.

-import { toTitleCase } from 'utils/common/index';
-
-const AppTitleBar = () => {
+import { toTitleCase } from 'utils/common/index';
+
+/**
+ * Application-level title bar hosting the workspace selector, branding, and global layout toggles.
+ */
+const AppTitleBar = () => {
📜 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 cb903e7 and f4418d5.

📒 Files selected for processing (3)
  • packages/bruno-app/src/components/AppTitleBar/index.js (1 hunks)
  • packages/bruno-app/src/components/StatusBar/index.js (1 hunks)
  • tests/footer/sidebar-toggle/sidebar-toggle.spec.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • tests/footer/sidebar-toggle/sidebar-toggle.spec.js
  • packages/bruno-app/src/components/StatusBar/index.js
  • packages/bruno-app/src/components/AppTitleBar/index.js
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Files:

  • tests/footer/sidebar-toggle/sidebar-toggle.spec.js
tests/**/**.*

⚙️ CodeRabbit configuration file

tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:

  • Follow best practices for Playwright code and e2e automation

  • Try to reduce usage of page.waitForTimeout(); in code unless absolutely necessary and the locator cannot be found using existing expect() playwright calls

  • Avoid using page.pause() in code

  • Use locator variables for locators

  • Avoid using test.only

  • Use multiple assertions

  • Promote the use of test.step as much as possible so the generated reports are easier to read

  • Ensure that the fixtures like the collections are nested inside the fixtures folder

    Fixture Example*: Here's an example of possible fixture and test pair

    .
    ├── fixtures
    │   └── collection
    │       ├── base.bru
    │       ├── bruno.json
    │       ├── collection.bru
    │       ├── ws-test-request-with-headers.bru
    │       ├── ws-test-request-with-subproto.bru
    │       └── ws-test-request.bru
    ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection
    ├── headers.spec.ts
    ├── persistence.spec.ts
    ├── variable-interpolation
    │   ├── fixtures
    │   │   └── collection
    │   │       ├── environments
    │   │       ├── bruno.json
    │   │       └── ws-interpolation-test.bru
    │   ├── init-user-data
    │   └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection
    └── subproto.spec.ts
    

Files:

  • tests/footer/sidebar-toggle/sidebar-toggle.spec.js
🧠 Learnings (1)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Applied to files:

  • tests/footer/sidebar-toggle/sidebar-toggle.spec.js
🧬 Code graph analysis (1)
packages/bruno-app/src/components/AppTitleBar/index.js (3)
packages/bruno-app/src/utils/common/index.js (2)
  • toTitleCase (353-360)
  • toTitleCase (353-360)
packages/bruno-app/src/providers/ReduxStore/slices/app.js (2)
  • savePreferences (132-142)
  • savePreferences (132-142)
packages/bruno-app/src/components/Dropdown/index.js (1)
  • Dropdown (5-25)
⏰ 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: Playwright E2E Tests
  • GitHub Check: CLI Tests
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - macOS
🔇 Additional comments (1)
packages/bruno-app/src/components/StatusBar/index.js (1)

12-12: Import narrowing matches StatusBar’s reduced responsibilities

Updating this import so StatusBar only pulls in showPreferences (and not sidebar toggle actions) is consistent with moving sidebar/workspace controls into the new titlebar/sidebar header. The remaining usages of showPreferences in this component look correct.

Comment on lines +60 to +67
const WorkspaceName = forwardRef((props, ref) => {
return (
<div ref={ref} className="workspace-name-container" onClick={() => setShowWorkspaceDropdown(!showWorkspaceDropdown)}>
<span className="workspace-name">{toTitleCase(activeWorkspace?.name) || 'Default Workspace'}</span>
<IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
</div>
);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Make the workspace dropdown trigger keyboard-accessible and use a proper button element.

Right now the trigger is a <div> with onClick, which is not focusable or activatable via keyboard and has no ARIA metadata. That’s a blocking accessibility issue for switching workspaces.

You can convert it to a real button with ARIA attributes and a functional state update:

-  const WorkspaceName = forwardRef((props, ref) => {
-    return (
-      <div ref={ref} className="workspace-name-container" onClick={() => setShowWorkspaceDropdown(!showWorkspaceDropdown)}>
-        <span className="workspace-name">{toTitleCase(activeWorkspace?.name) || 'Default Workspace'}</span>
-        <IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
-      </div>
-    );
-  });
+  const WorkspaceName = forwardRef((props, ref) => {
+    return (
+      <button
+        ref={ref}
+        type="button"
+        className="workspace-name-container"
+        onClick={() => setShowWorkspaceDropdown((prev) => !prev)}
+        aria-haspopup="listbox"
+        aria-expanded={showWorkspaceDropdown}
+      >
+        <span className="workspace-name">{toTitleCase(activeWorkspace?.name) || 'Default Workspace'}</span>
+        <IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
+      </button>
+    );
+  });

No changes needed to the <Dropdown icon={<WorkspaceName />} ...> usage.

Also applies to: 140-148

🤖 Prompt for AI Agents
In packages/bruno-app/src/components/AppTitleBar/index.js around lines 60 to 67
(and similarly apply the same change to lines 140 to 148), replace the
non-focusable <div> trigger with a semantic <button type="button"> that forwards
the ref, keeps the same className and click handler, and adds ARIA attributes:
aria-haspopup="menu" and aria-expanded={showWorkspaceDropdown} so screen readers
know it toggles a menu; ensure you keep the onClick that calls
setShowWorkspaceDropdown(!showWorkspaceDropdown) and remove any onKey handlers
since a native button handles keyboard activation.

bijin-bruno
bijin-bruno previously approved these changes Dec 5, 2025
<button
className="titlebar-action-button"
onClick={handleToggleSidebar}
title={sidebarCollapsed ? 'Show sidebar' : 'Hide sidebar'}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we use ToolHint instead of title?

<IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
</div>
);
});
Copy link
Collaborator

Choose a reason for hiding this comment

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

@abhishek-bruno Please address the above in a followup PR

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

📜 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 f4418d5 and cc1e503.

📒 Files selected for processing (1)
  • tests/response/response-actions.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

**/*.{js,jsx,ts,tsx}: Use 2 spaces for indentation, never tabs
Use single quotes for strings instead of double quotes
Always add semicolons at the end of statements
No trailing commas in code
Always use parentheses around parameters in arrow functions, even for single parameters
For multiline constructs, put opening braces on the same line with a minimum of 2 elements for multiline formatting
No newlines inside function parentheses
Space before and after the arrow in arrow functions: () => {}
No space between function name and parentheses: func() not func ()
Semicolons should go at the end of the line, not on a new line
Function names should be concise and descriptive
Add JSDoc comments to provide details to abstractions
Avoid single-line abstractions where all that is being done is increasing the call stack with one additional function
Add meaningful comments to explain complex code flow instead of obvious comments

Files:

  • tests/response/response-actions.spec.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CODING_STANDARDS.md)

Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Files:

  • tests/response/response-actions.spec.ts
tests/**/**.*

⚙️ CodeRabbit configuration file

tests/**/**.*: Review the following e2e test code written using the Playwright test library. Ensure that:

  • Follow best practices for Playwright code and e2e automation

  • Try to reduce usage of page.waitForTimeout(); in code unless absolutely necessary and the locator cannot be found using existing expect() playwright calls

  • Avoid using page.pause() in code

  • Use locator variables for locators

  • Avoid using test.only

  • Use multiple assertions

  • Promote the use of test.step as much as possible so the generated reports are easier to read

  • Ensure that the fixtures like the collections are nested inside the fixtures folder

    Fixture Example*: Here's an example of possible fixture and test pair

    .
    ├── fixtures
    │   └── collection
    │       ├── base.bru
    │       ├── bruno.json
    │       ├── collection.bru
    │       ├── ws-test-request-with-headers.bru
    │       ├── ws-test-request-with-subproto.bru
    │       └── ws-test-request.bru
    ├── connection.spec.ts # <- Depends on the collection in ./fixtures/collection
    ├── headers.spec.ts
    ├── persistence.spec.ts
    ├── variable-interpolation
    │   ├── fixtures
    │   │   └── collection
    │   │       ├── environments
    │   │       ├── bruno.json
    │   │       └── ws-interpolation-test.bru
    │   ├── init-user-data
    │   └── variable-interpolation.spec.ts # <- Depends on the collection in ./variable-interpolation/fixtures/collection
    └── subproto.spec.ts
    

Files:

  • tests/response/response-actions.spec.ts
🧠 Learnings (1)
📚 Learning: 2025-12-03T08:09:57.124Z
Learnt from: CR
Repo: usebruno/bruno PR: 0
File: CODING_STANDARDS.md:0-0
Timestamp: 2025-12-03T08:09:57.124Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Add tests for any new functionality or meaningful changes; update corresponding tests when code is added, removed, or significantly modified

Applied to files:

  • tests/response/response-actions.spec.ts
⏰ 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: CLI Tests
  • GitHub Check: Playwright E2E Tests
  • GitHub Check: Unit Tests
  • GitHub Check: SSL Tests - Windows
  • GitHub Check: SSL Tests - Linux
  • GitHub Check: SSL Tests - macOS

@bijin-bruno bijin-bruno merged commit 4a8d787 into usebruno:main Dec 5, 2025
10 of 11 checks passed
naman-bruno pushed a commit to naman-bruno/bruno that referenced this pull request Dec 6, 2025
…no#6319)

* refactor: update sidebar components and styles, replace TitleBar with SidebarHeader, and enhance collections search functionality

* refactor: improve event listener management in AppTitleBar and clean up SidebarHeader styles

* fix: ensure safe access to layout preferences in AppTitleBar and set default order in SidebarHeader

* refactor: centralize toTitleCase utility and remove redundant implementations in AppTitleBar and WorkspaceSelector

* feat: enhance accessibility and testing for sidebar and devtools toggle buttons in AppTitleBar

* chore: quick fix on a flaky test

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
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