Compare commits

...

491 Commits

Author SHA1 Message Date
dependabot[bot]
51312a3148 chore(deps): bump actions/github-script from 7 to 8
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-02 19:06:25 +00:00
naman-bruno
6e6804055d fix: default format on import modal (#7017) 2026-02-02 21:31:01 +05:30
naman-bruno
5904c36cdb feat: enhance ShareCollection component with export options and UI improvements (#7016) 2026-02-02 21:01:03 +05:30
naman-bruno
8c997c46af make yml default option (#6985)
* make yml default option
2026-02-02 19:45:45 +05:30
naman-bruno
700e25a1d5 Add: dotenv visual editor (#6964) 2026-02-02 19:43:54 +05:30
naman-bruno
c9059c9905 refactor: update opencollection extension for bruno (#7013)
* refactor: update YML parsing and stringification to utilize 'bruno' extensions for ignore and presets

* fix
2026-02-02 19:35:17 +05:30
naman-bruno
416b693afc fix: YML parsing and stringification to support post-response variables (#7009) 2026-02-02 18:57:35 +05:30
lohit
bafb235e72 feat: add certs and proxy config to bru.sendRequest API (#6988)
* feat: add certs and proxy config to bru.sendRequest API

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: handle URL string argument in bru.sendRequest

When bru.sendRequest is called with a plain URL string instead of a
config object, the function now normalizes it to { url: string } before
processing. This fixes the case where spreading a string created an
invalid config object.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* feat: add variable interpolation to bru.sendRequest certs and proxy config

Interpolate environment variables in clientCertificates and proxy
configuration for bru.sendRequest API, enabling use of variables like
{{CERT_PATH}} or {{PROXY_HOST}} in certificate paths and proxy settings.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: use interpolateObject for certs and proxy config interpolation

- Add interpolateObject to electron's interpolate-string.js using
  buildCombinedVars pattern (matches CLI implementation)
- Simplify cert-utils.js by using interpolateObject instead of
  manual field-by-field interpolation
- Add interpolation for clientCertificates and proxy config in CLI's
  run-single-request.js for bru.sendRequest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: add all variable types to sendRequest interpolation options

- Add globalEnvVars, collectionVariables, folderVariables, requestVariables
  to sendRequestInterpolationOptions for complete variable support
- Use cached system proxy instead of redundant getSystemProxy() call
- Remove duplicate getOptions() call

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: skip CA cert loading when TLS verification is disabled

Only load CA certificates when shouldVerifyTls is true, since they
are not used for validation when TLS verification is disabled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 17:59:46 +05:30
Sid
06dd5c14d5 CI: flaky test monitor (#7007) 2026-02-02 17:16:27 +05:30
Sanjai Kumar
0f0c2b5912 Revert "fix: ephemeral environment variables being saved to filesystem (#6723)" (#7012)
This reverts commit 5b1b1b5541.
2026-02-02 16:50:06 +05:30
gopu-bruno
6664295b2b fix: update sidebar item copy toast message (#7011) 2026-02-02 16:29:50 +05:30
shubh-bruno
679cb91549 fix: refocus search bar in code editor on Ctrl/Cmd + F (#6980) 2026-02-02 15:53:32 +05:30
naman-bruno
b9d9a27599 add support for additional context roots in opencollection (#6995)
* add support for additional context roots in opencollection
2026-02-02 14:04:46 +05:30
Chirag Chandrashekhar
1fc703e4e3 feat: implement dynamic terminal theming based on app theme (#6812)
- Added a function to build terminal themes from the app's current theme.
- Updated terminal creation and rendering functions to accept and apply the dynamic theme.
- Implemented a useEffect hook to update terminal themes when the app theme changes.
2026-02-02 12:38:51 +05:30
anusree-bruno
89a0494e7e Feat/preferences UI polish (#6989)
* Preferences UI polish

* chore: cleanup

* chore: cleanup

* chore: removed unused classname
2026-02-02 10:34:32 +05:30
shubh-bruno
04806144a5 fix: response pane actions for GQL requests (#6911) 2026-01-31 09:30:07 +05:30
Pooja
0c3d20b198 fix: restore cursor focus on save and show placeholder for empty cells (#6795) 2026-01-31 09:08:42 +05:30
gopu-bruno
3ddf8e2a8b fix: support multiline descriptions in example blocks (#6879)
* fix: support multiline descriptions in example blocks

* refactor: use outdentString for example multiline text block parsing

* test: add test case for examples without description field

* test: add jsonToBru conversion test for multiline descriptions

* refactor: generalize descriptionvalue to textvalue in example grammar
2026-01-30 23:04:48 +05:30
gopu-bruno
f10422cca6 fix: support multiline example names (#6895)
* fix: support mutliline example names

* fix: improve multiline example name parsing and processing

* test: add test cases for example name field parsing

* refactor: simplify example name parsing

* fix: sanitize multiline example names in Postman imports

* fix: sanitize Postman example names on import

* fix: sanitize OpenAPI example names on import
2026-01-30 23:01:28 +05:30
naman-bruno
ba166561cc feat: add custom AppMenu component for windows & linux (#6934)
* feat: add custom AppMenu component for windows & linux

* fixes

* fixes

* fixes

* fixes
2026-01-30 22:58:36 +05:30
lohit
3112380289 feat: cache system proxy to avoid redundant lookups (#6990)
- bruno-cli: fetch system proxy once before request loop and store in options
- bruno-electron: initialize system proxy cache at app startup
- Add refresh button in preferences to manually update cached system proxy
- Replace per-request getSystemProxy() calls with cached values

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:15:36 +05:30
Sid
559946bcce feat: add search functionality to environment variables. (#6659) (#6966) 2026-01-30 19:04:16 +05:30
Pooja
e1c01ebe18 feat: add resizable columns to table (#6843) 2026-01-30 18:25:13 +05:30
Sid
eb5dc12b43 Merge pull request #6970 from usebruno/feature/environment-color-extended
feat(#304) Environments color 🎨 (#1053)
2026-01-30 17:12:35 +05:30
Pooja
a04ff3e819 feat(#304) Environments color 🎨 (#1053) (#6974) 2026-01-30 17:07:13 +05:30
Mathieu DREANO
5a6714f085 feat(#304) Environments color 🎨 (#1053)
* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* move dependency to appropriate package.json

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* use border instead of background color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* simplify onColorChange

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* add black, keep backgound on unselected color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* fix conflicts

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

# Conflicts:
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/StyledWrapper.js
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js
#	packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js

* Update packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentColor/index.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* unused selectedEnvironment prop in EnvironmentList

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* RequestTab, avoid unnecessary call if undefined activeCollection

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* use @uiw/reac-color instead of react-color

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

---------

Signed-off-by: mathieu <mathieu.dreano@gmail.com>
Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Anoop M D <anoop@usebruno.com>
Co-authored-by: Mathieu DREANO <122891400+mdreano@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-01-30 17:07:13 +05:30
lohit
214e1434e5 fix: ensure app gains focus when restoring main window (#6984)
Add app.focus({ steal: true }) before restoring the window to ensure
the application properly gains focus when activated.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:52:30 +05:30
naman-bruno
3996b86bcb fix: update certificate type handling in stringifyCollection function (#6986) 2026-01-30 15:51:04 +05:30
sanish chirayath
8fface4bbe feat: enhance request and response translation capabilities (#6981)
- Added translations for req.method, req.headers, and req.body to their Postman equivalents.
- Implemented handling for req.setUrl, req.setMethod, req.setBody, and req.setHeaders, converting them to appropriate Postman assignments and method calls.
- Introduced support for translating res.responseTime and res.headers properties.
- Updated tests to cover new translations and ensure accuracy in request and response handling.
2026-01-30 15:50:27 +05:30
naman-bruno
b268aa9f98 fix: global environment flag for cli (#6969)
* fix: global environment flag for cli

* fixes
2026-01-30 15:37:56 +05:30
Pooja
df3ff5e48a fix: refresh scopeInfo after variable save in tooltip editor (#6935) 2026-01-30 13:33:30 +05:30
Pooja
6ca5c71f7a fix: cURL auth import for digest and ntlm (#6292) 2026-01-30 13:05:28 +05:30
Chirag Chandrashekhar
ca4d0dd40b Feature/transient request (#6878)
* feat: add functionality to retrieve collection path from transient file requests in IPC module

* feat: implement transient directory handling in collection mounting process

* add action to store transient directory paths in Redux state
* update IPC handler to create and return a temporary directory for collections
* modify collection mount action to dispatch transient directory addition

* feat: add CreateTransientRequest component for managing transient requests

* implement CreateTransientRequest component to facilitate the creation of HTTP, GraphQL, gRPC, and WebSocket transient requests
* integrate the component into RequestTabs for user interaction
* update collection and request handling to differentiate between transient and non-transient requests
* enhance Redux actions to support transient request creation and management

* feat: enhance transient request handling and add temp directory watcher

* refactor Redux actions for HTTP, gRPC, and WebSocket requests to utilize a unified task queue for transient requests
* implement a new helper function to retrieve collection paths from temporary directory metadata
* add functionality to watch transient directories for file changes, excluding metadata.json
* integrate transient directory watcher into the collection mounting process

* feat: enhance transient request management with temp directory integration

* update generateTransientRequestName function to accept tempDirectory parameter for improved request naming
* modify CreateTransientRequest component to utilize tempDirectory for transient request creation
* adjust middleware to check for transient status based on tempDirectory
* implement transient file and directory identification in collections slice for better state management

* feat: add SaveTransientRequest component for managing transient requests

* implement SaveTransientRequest component to facilitate saving transient requests to selected folders
* create StyledWrapper for component styling
* introduce useCollectionFolderTree hook for managing folder navigation and state
* update Redux actions to handle saving requests from transient state

* feat: implement SaveTransientRequestContainer and enhance modal management

* add SaveTransientRequestContainer to manage multiple transient request modals
* refactor SaveTransientRequest component to utilize Redux for modal state management
* implement open and close actions for transient request modals in Redux slice
* update Bruno page to include SaveTransientRequestContainer for improved UI integration

* feat: enhance SaveTransientRequest component with new folder creation functionality

* add input for creating new folders within the SaveTransientRequest component
* implement validation for new folder names and filesystem names
* integrate folder creation logic with Redux actions for better state management
* update styling for new folder input elements in StyledWrapper
* improve modal behavior to reset state when opened

* feat: update CreateTransientRequest to utilize collection presets for request URLs

* Refactored CreateTransientRequest component to retrieve request URLs from collection presets.
* Enhanced request handling by dynamically setting request URLs based on the selected collection's configuration.

* refactor: clean up unused imports and adjust request handling in collections actions

* Removed unused imports from actions.js to streamline the code.
* Updated the saveRequest function to reject the modal instead of resolving it when handling transient requests.
* Cleaned up comments in index.js for better clarity.

* refactor: streamline transient request handling and improve save functionality

* Removed success toast notifications from CreateTransientRequest component to simplify user feedback.
* Enhanced SaveTransientRequest component to handle transient requests more effectively, including improved filename resolution and validation.
* Added IPC handler for saving transient requests, ensuring proper file management and error handling.
* Updated Redux actions to check for duplicate transient request names within the temporary directory.

* feat: enhance request handling in ConfirmCollectionCloseDrafts component

* Added logic to differentiate between transient and non-transient drafts, ensuring transient requests are saved individually before closing the collection.
* Improved user feedback by displaying unsaved changes for both regular and transient requests.
* Updated save and discard functionality to handle all drafts appropriately, enhancing overall user experience.

* fix:fixed useCallback dependency array

* fix:added request name checks before save

* fix: added isTransient to files

* fix: added watcher cleanup for temp directory

* refactor: enhance transient request handling and optimize component logic

* Updated CreateTransientRequest to utilize useMemo for improved performance and prevent unnecessary re-renders.
* Refactored generateTransientRequestName to focus solely on transient requests, removing tempDirectory dependency.
* Streamlined SaveTransientRequest by consolidating form reset logic and removing unused state variables.
* Improved ConfirmCollectionCloseDrafts to differentiate between transient and non-transient drafts more effectively.
* Cleaned up imports and optimized Redux actions for better maintainability.

* feat: implement transient request file deletion on tab close

* Added middleware to handle the deletion of transient request files when tabs are closed.
* Enhanced collection-watcher to unlink temporary files, ensuring metadata.json is skipped and only request files are processed.
* Improved error handling for file deletion operations.

* feat: enhance autosave middleware to skip transient requests

* Updated autosave middleware to check for transient requests and skip auto-save operations accordingly.

* fix: update ConfirmCollectionCloseDrafts to display all transient drafts

* Modified the ConfirmCollectionCloseDrafts component to show all transient drafts without limiting the display to a maximum number.
* Removed the conditional message for additional drafts not shown, enhancing the user experience by providing complete visibility of transient requests.

* feat: enhance SaveTransientRequest component for better modal management

* Refactored SaveTransientRequest and its container to improve modal handling for unsaved transient requests.
* Introduced state management for opening specific modals and added functionality to discard all unsaved requests.
* Updated Redux actions to manage transient request modals more effectively, ensuring no duplicates are added.
* Enhanced user interface to display a list of unsaved requests with options to save or discard them.

* feat: improve modal management in SaveTransientRequestContainer

* Added useEffect to reset openItemUid when the corresponding modal is no longer present.
* Implemented functionality to close all tabs associated with transient requests and show a success message upon discarding them.
* Removed unnecessary modal close handler and streamlined modal opening logic for better clarity and performance.

* refactor: streamline code formatting and improve readability in collection actions

* Consolidated multiple lines of code into single lines for better readability in ConfirmCollectionCloseDrafts and actions.js.
* Enhanced consistency in the formatting of function parameters and return statements across the collections slice.
* Removed unnecessary line breaks and improved the structure of the code for easier maintenance.

* refactor: improve code readability and structure in middleware and actions

* Consolidated multiple lines of code into single lines for better readability in middleware.js and actions.js.
* Enhanced consistency in formatting function parameters and return statements across the collections slice.
* Removed unnecessary line breaks and improved the structure of the code for easier maintenance.
* Streamlined dispatch calls for better clarity and performance.

* refactor: enhance code readability and consistency in middleware and actions

* Improved formatting and structure in middleware.js for dispatch calls.
* Streamlined comments and indentation in actions.js for better clarity.
* Consolidated multiple lines into single lines where appropriate to enhance readability.

* refactor: enhance transient request handling and modal interactions

* Improved the modal handling logic for removing collections to differentiate between regular and drafts confirmation modals.
* Added new tests for creating and saving transient requests (HTTP, GraphQL, gRPC, WebSocket) ensuring they do not appear in the sidebar until saved.
* Introduced utility functions for creating transient requests and filling request URLs, improving code reusability and clarity.

* refactor: simplify transient request modal rendering and improve collection watcher logic

* Introduced a new TransientRequestModalsRenderer component to streamline modal rendering based on the number of transient requests.
* Refactored the collection watcher logic to enhance readability by removing unnecessary setTimeout and consolidating file handling functions.
* Improved error handling and logging for the temp directory watcher.

* fix: correct spelling of 'WebSocket' in transient request components and tests

* Updated the spelling of 'Websocket' to 'WebSocket' in CreateTransientRequest component, transient requests test, and action type definitions for consistency and accuracy.
2026-01-29 18:38:42 +05:30
sanish chirayath
4b724ebd85 fix: async fns calls window.send after the window is destroyed (#6747)
* fix: app crash error

* fix: prevent app crash by ensuring window and webContents are not destroyed before sending messages
2026-01-29 15:43:04 +05:30
lohit
b3a66e9c3c fix: system proxy resolver updates (#6273) 2026-01-29 14:25:13 +05:30
gopu-bruno
4f327b7b77 fix: prevent crash when reordering query params with empty name (#6938)
* fix: prevent crash when reordering query params with empty name

* fix: prevent crash when reordering rows with empty key field
2026-01-29 13:56:40 +05:30
Sid
579cda1d1a chore: remove prettier configurations (#6967)
* chore: remove prettier

* chore: remove prettier commands

* chore: fix newline at end of package-lock.json
2026-01-29 13:31:00 +05:30
so-iwamoto
61199fb966 Add Buffer.isBuffer() check to skip interpolation for Buffer data (e.g., gzip-compressed bodies). (#6922)
Co-authored-by: Sora Iwamoto <rockbook2025@gmail.com>
2026-01-29 13:01:07 +05:30
Pierre GUYOT
79daf7700f docs(converters): Fix example code to await (#6960)
Updated example code to use await with postmanToBruno function.

Also change markdown code from `bash copy` to `javascript`
2026-01-29 12:19:09 +05:30
Abhishek S Lal
6e34fbd0ce fix: ensure terminal gains focus upon opening in the console tab (#6951) 2026-01-28 20:38:20 +05:30
Sid
1fcf9ecc32 Update React coding standards in CODING_STANDARDS.md (#6962)
Added new coding standards for React components and hooks usage.
2026-01-28 20:00:58 +05:30
shubh-bruno
fec407e2eb fix: path-param variable edit popup (#6955)
Co-authored-by: shubh-bruno <shubh-bruno@shubh-bruno.local>
2026-01-28 19:37:15 +05:30
sanish chirayath
5044241d17 feat: enhance translation for request and response URLs (#6956) 2026-01-28 18:59:14 +05:30
sanish chirayath
584344ac47 Fix: duplicate toast when saving an example (#6952)
* chore: update saveRequest calls from example to include silent parameter

- Modified multiple components to pass an additional `true` parameter to the `saveRequest` function, ensuring requests are saved silently when it comes to examples.
- Added AGENTS.md to .gitignore to exclude it from version control.

* feat: add success notifications for cloning, renaming and deleting examples

* refactor: update saveRequest calls to handle success notifications for renaming and deleting examples

* refactor: remove unused toast import from DeleteResponseExampleModal
2026-01-28 17:59:03 +05:30
Pooja
d975d0b642 fix: openapi spec with example values (#6476)
* fix: openapi spec with example values

* fix

* fix
2026-01-28 16:03:00 +05:30
shubh-bruno
af6908e9c0 fix: disable editing runtime variable if same as collection (#6835)
Co-authored-by: shubh-bruno <shubh-bruno@shubh-brunos-MacBook-Air.local>
2026-01-27 22:08:41 +05:30
Pooja
21673f46de feat: add header validation (#6859)
* feat: add header validation

* fix: test stability

* fix: scope the locator

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2026-01-27 22:06:17 +05:30
Sid
51276beaf1 chore: reduce flakiness when running parallel tests (#6848) 2026-01-27 19:56:48 +05:30
naman-bruno
7661af34c8 consistent string handling across parsers (#6866)
* consistent string handling across parsers

* fix
2026-01-27 14:10:40 +05:30
Pragadesh-45
01b87ee71c refactor: improve element interactions in preferences and fix playwright tests (#6894) 2026-01-22 20:16:33 +05:30
Abhishek S Lal
9e1c58ab6f Remove isLikelyText detection from buffer content type utility (#6870) 2026-01-22 12:40:57 +05:30
Kanak
0fb605a684 fix: update linting commands to use npx for better compatibility (#6840)
* fix: update linting commands to use npx for better compatibility

* chore: update cross-env version and adjust lint scripts

* Fix lint command syntax in package.json

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2026-01-21 21:55:34 +05:30
sanish chirayath
5fd3948028 Feature: send request translation (#6792)
* feat: implement translation utilities for converting Bruno scripts to Postman format

- Added `bru-to-pm-translator` for translating Bruno API calls to Postman equivalents.
- Introduced `pm-to-bru-translator` for reverse translations from Postman to Bruno.
- Created utility functions in `ast-utils` for efficient AST manipulations.
- Enhanced `bruno-to-postman.js` to utilize the new translation functions for script handling.
- Updated tests to cover various translation scenarios, ensuring accuracy and reliability.

* empry commint

* refactor: migrate utility functions to ES module syntax

- Converted utility functions in `ast-utils.js` to named exports for better modularity.
- Updated import statements in `bru-to-pm-translator.js` and `pm-to-bru-translator.js` to use ES module syntax.
- Refactored test files to align with the new import structure, enhancing consistency across the codebase.

* fix: translations

* fix: add info regarding cookie apis

* simplify translations removing legacy inverse translation

* fix: add translation for getFolderVAr

* refactor: simplify transformation functions by removing change tracker

* fix: renamed files and folders

* fix: import statements

* rm : file

* simplify getSize translation

* rebase

* fix: rebase

* fix: update transformCallback to support async functions

* feat: enhance object transformation to support spread operators in request data

* refactor: transform body function

* feat: added request transformation testcases, refactor
2026-01-21 20:26:13 +05:30
lohit
3e92c44a5a Merge pull request #6748 from lohit-bruno/bruno_app_instances_handling
feat: `bruno app instances` handling updates
2026-01-21 19:53:17 +05:30
Pragadesh-45
67c1d39e60 feat: preferences as tab (#6786)
* feat: preferences as tab

refactor: remove preferences tab from permanent tabs and update tab label handling

fix: comment

Co-authored-by: Sid <siddharth@usebruno.com>

* refactor: replace Checkbox component with native input elements in Preferences and ProxySettings

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2026-01-21 19:22:28 +05:30
Pragadesh-45
2288121f70 feat: status indicator for pre and post request scripts (#6865) 2026-01-21 19:01:06 +05:30
Pooja
a22eb43a27 fix(websocket): add API Key query params support and OAuth2 inheritan… (#6271)
* fix(websocket): add API Key query params support and OAuth2 inheritance warning

* add: playwright test
2026-01-21 18:56:46 +05:30
Sid
27b7fa81f2 feat: js api supports get path params (#5235) (#6762) 2026-01-21 18:41:47 +05:30
Bijin A B
1f571267b0 Merge pull request #6814 from sanjaikumar-bruno/fix/basic-auth-codegen
fix: Code Generation for Basic Auth
2026-01-21 18:38:46 +05:30
Sanjai Kumar
0b79ce9095 fix: correct action type string for global environments in autosave middleware (#6872) 2026-01-21 18:33:09 +05:30
Pooja
75e17610f0 fix: openapi query param import (#6241) 2026-01-21 17:53:14 +05:30
sanjai
acf576872c enhance: snippet generator to support header interpolation
refactor: improve snippet generation and update test cases

updated to minimise changes

fix: remove exclusive test flag

refactor: enhance interpolation utilities

refactor: expand interpolation utilities for auth, headers, body, and params

refactor: simplify request handling in snippet generation by removing lodash dependency and clarifying auth header processing

fix: tests

refactor: integrate interpolateObject utility for enhanced interpolation across auth, headers, body, and params

refactor: streamline body interpolation by removing lodash dependency and returning updated body structure

refactor: enhance body interpolation logic and streamline auth header processing in snippet generation

refactor: simplify getAuthHeaders function by removing unnecessary parameters for improved clarity

refactor: replace interpolateObject with interpolate for body
2026-01-21 15:55:21 +05:30
Cmarvin1
c94785f521 Adding interpolation utilities
Refactor interpolation

Refactor interpolation

updating tests

updating tests

minor refinements to interpolation logic

update snippet generator to handle basic auth credentials

move interpolation upstream
2026-01-21 15:55:11 +05:30
sreelakshmi-bruno
154c45d87d skip loading CA certificates when SSL verification is disabled (#6829) 2026-01-21 12:38:45 +05:30
Yash
0bf169562b feat: enhance OAuth2 support in snippet generation (#6592)
* feat: enhance OAuth2 support in snippet generation

* Updated getAuthHeaders function to handle OAuth2 authentication, including token retrieval and placement.
* Added tests for OAuth2 scenarios, ensuring correct Authorization header generation and handling of edge cases.
* Improved error handling for access token retrieval from stored credentials.

* refactor: standardize comparison operators in getAuthHeaders function

* Updated comparison operators in the getAuthHeaders function to use strict equality (===) for improved consistency and reliability in credential checks.

* fix: correct block structure in OAuth2 case of getAuthHeaders function

* Added missing block structure for the 'oauth2' case in the getAuthHeaders function to ensure proper execution flow and maintain code clarity.

* feat: enhance OAuth2 credential retrieval in getAuthHeaders function

* Updated getAuthHeaders function to support retrieval of stored OAuth2 credentials based on collection and item context.
* Improved access token handling by checking for existing credentials before falling back to default values.
* Enhanced test coverage for OAuth2 scenarios to ensure accurate token management and error handling.

* fix: preserve tokenHeaderPrefix value in OAuth2 configuration

* Updated snippet-generator.spec.js to ensure that the tokenHeaderPrefix from OAuth2 configuration is preserved, allowing for empty string scenarios.
* Default to 'Bearer' only if the tokenHeaderPrefix is undefined, enhancing flexibility in token management.

* fix: ensure consistent formatting of authorization header in OAuth2 handling

* Updated getAuthHeaders function to always trim the final result of the authorization header for consistent formatting.
* Adjusted snippet-generator.spec.js to reflect the same trimming logic for the access token, enhancing test reliability.

* fix: clarify token placement handling in getAuthHeaders function

* Updated comments in the getAuthHeaders function to specify that when tokenPlacement is 'url', no auth headers are added, and that token placement in the URL/query params must be managed separately.

* fix: ensure safe handling of OAuth2 credentials in getAuthHeaders function

* Updated getAuthHeaders function to default to an empty array when accessing oauth2Credentials, preventing potential errors when no credentials are available.
2026-01-21 12:23:05 +05:30
gopu-bruno
967b073ded fix: prevent response truncation in recursive collection runner (#6862) 2026-01-21 11:32:33 +05:30
sanish chirayath
725dfeacac feat: add user-agent support in gRPC client channel options (#6808)
* feat: add user-agent support in gRPC client channel options

- Extracted user-agent from request headers and set it as grpc.primary_user_agent channel option.
- Updated client instantiation to merge user-agent with existing channel options for enhanced request handling.

* test: add unit tests for GrpcClient user-agent handling

* test: enhance GrpcClient user-agent tests with edge case handling

* test: enhance GrpcClient channelOptions handling with override capability
2026-01-20 23:59:25 +05:30
lohit
923d26ce56 fix: get certs and proxy config based on oauth2 token and refresh urls instead of resource url (#6164) 2026-01-20 21:43:54 +05:30
lohit
7e258003d5 feat: add certs and proxy config for bruno-cli oauth2 requests (#6423) 2026-01-20 21:42:48 +05:30
Abhinandan M.S
7689288763 fix:prevent JS hint leak on Ctrl+Space and show allowed root hints (#6776) 2026-01-20 13:55:49 +05:30
fake
81faa57808 fix: add timeout for prevent ui lag (#6771) 2026-01-20 13:28:06 +05:30
naman-bruno
bac9616de4 feat: enhance SaveRequestsModal to handle environment drafts (#6857) 2026-01-20 12:40:06 +05:30
naman-bruno
9ab1ed3d90 fix: update clone collection location logic based on active workspace (#6841) 2026-01-20 12:38:31 +05:30
Sid
408c9d4a4e chore: update project dependencies (#6858) 2026-01-20 12:37:09 +05:30
Sid
ebafdd813c chore: update qs package version to 6.14.1 (#6849)
Co-authored-by: Siddharth Gelera <ahoy@barelyhuman.dev>
2026-01-19 21:40:39 +05:30
lohit
6642f4d0b0 fix: cli proxy config updates (#6846)
* fix: cli `proxy config` updates

* fix: review comment fixes
2026-01-19 20:58:20 +05:30
naman-bruno
4f75474c87 remove allowScriptFilesystemAccess flag (#6834) 2026-01-19 19:33:00 +05:30
Bijin A B
e5b7aa5ab4 fix: variables set via setVar should be interpolated only during runtime (#6823) 2026-01-19 17:28:36 +05:30
Sid
875df38501 Merge pull request #6662 from usebruno/feature/bru-safemode-5760
feat: Implement `isSafeMode()` API (#5760)
2026-01-19 17:26:45 +05:30
Pooja
a724f010ff fix bru safe mode and add test (#6667)
* fix bru safe mode and add tests

* rm: settimeout

fix: isSafe mode test (#6844)
2026-01-19 17:26:12 +05:30
Dominik D. Geyer
f9423d1238 feat: Implement isSafeMode() API (#5760)
Add `isSafeMode()` to Bru API that returns `true` in
case the runtime is a sandbox.

This allows for scripts to test for and handle whether
running in sandbox or not:

```javascript
if (bru.isSafeMode()) {
  throw new Error('This script requires Developer mode')
}
```

Co-authored-by: Anoop M D <anoop@usebruno.com>
2026-01-19 17:26:12 +05:30
shubh-bruno
51e36519f7 fix: improve {{var}} detection using cursor-based brace matching (#6691) 2026-01-19 17:17:09 +05:30
gopu-bruno
bd0894ede0 fix: resolve tab flickering when switching between requests (#6825) 2026-01-16 12:08:14 +05:30
Pragadesh-45
b1e6a707bf feat: add support for interpolation on mockDataFunctions (#6393)
feat: implement `prepareMockObj` function for enhanced mock data processing in interpolation
2026-01-14 21:58:03 +05:30
Sanjai Kumar
c51381888a fix: basic Auth inheritance in code generation (#6805)
* fix: include auth in request data for GenerateCodeItem

* fix: conditionally include auth in request data for GenerateCodeItem

* fix: simplify auth inclusion in requestData

* fix: streamline auth assignment in requestData for GenerateCodeItem

* fix: clarify comments on auth resolution
2026-01-14 21:12:04 +05:30
sreelakshmi-bruno
8b1b18cc39 fix: resolve Load Request button error when loading large collection … (#6809)
* fix: resolve Load Request button error when loading large collection requests

* scope down to .bru requests
2026-01-14 19:25:39 +05:30
Pooja
707ed63be6 fix: timestamp tooltip message (#6688) 2026-01-14 17:13:54 +05:30
Chirag Chandrashekhar
bc0bb64400 fix: prevent URL marking within variable patterns in CodeMirror (#6680) 2026-01-14 14:00:28 +05:30
shubh-bruno
4c110900c1 fix: rename requests double notifications (#6677) 2026-01-14 13:28:35 +05:30
shubh-bruno
65ed6d3cfb fix: response format auto-switch on content type change (#6773) 2026-01-14 13:28:04 +05:30
Sid
7b28b05bc1 fix: add compute key for virtual table (#6807) 2026-01-14 13:09:18 +05:30
shubh-bruno
b0f27d01b9 fix: env vars loading and switching using react-virtuoso (#6790)
* fix: environment variables fetching and switching

* chore: formatting

* fix: support active, inactive, secret vars popup

* fix: variable highlight styles

* chore: codemirror styles

* fix: show variable highlighting when editor is inactive

* fix: tab press for switching columns

* fix: environment variables loading with react-virtuoso

* fix: refactor EnvironmentVariables component for improved table rendering

* fix: update react-virtuoso to version 4.18.1

---------

Co-authored-by: shubh-bruno <shubh-bruno@shubh-brunos-MacBook-Air.local>
Co-authored-by: Sid <siddharth@usebruno.com>
2026-01-14 12:24:07 +05:30
Pooja
3351bf990a fix: websocket message scroll (#6503)
* fix: websocket message scroll

* fix

* fix: icon color

* fix: sse message list

* fix

* rm: sort test

* rm: WSResponseSortOrder

* fix: auto scroll
2026-01-13 22:09:08 +05:30
lohit
07fff423bb feat: add node-vault util functions (#6796)
* feat: add `node-vault` util functions

* fix: review comment fixes
2026-01-13 22:06:40 +05:30
sanish chirayath
36d10ab480 feat: implement translation utilities for converting Bruno scripts pm format (#6761)
* feat: implement translation utilities for converting Bruno scripts to Postman format

- Added `bru-to-pm-translator` for translating Bruno API calls to Postman equivalents.
- Introduced `pm-to-bru-translator` for reverse translations from Postman to Bruno.
- Created utility functions in `ast-utils` for efficient AST manipulations.
- Enhanced `bruno-to-postman.js` to utilize the new translation functions for script handling.
- Updated tests to cover various translation scenarios, ensuring accuracy and reliability.

* empry commint

* refactor: migrate utility functions to ES module syntax

- Converted utility functions in `ast-utils.js` to named exports for better modularity.
- Updated import statements in `bru-to-pm-translator.js` and `pm-to-bru-translator.js` to use ES module syntax.
- Refactored test files to align with the new import structure, enhancing consistency across the codebase.

* fix: translations

* fix: add info regarding cookie apis

* simplify translations removing legacy inverse translation

* fix: add translation for getFolderVAr

* refactor: simplify transformation functions by removing change tracker

* fix: renamed files and folders

* fix: import statements

* rm : file

* simplify getSize translation
2026-01-13 20:03:14 +05:30
Abhishek S Lal
c918c679d7 fix: handle optional clientSecret in OAuth2 authorization header (#6186)
* fix: handle optional clientSecret in OAuth2 authorization header

* style: standardize string quotes in OAuth2 token functions

* test: add comprehensive tests for OAuth2 client credentials and password grant flows
2026-01-13 19:30:11 +05:30
Abhishek S Lal
7e3386b1b8 feat: allow collection environment and environment file to be used together in run command (#6784) 2026-01-13 19:24:26 +05:30
Sanjai Kumar
f4162e1ce6 feat: show skipped requests with parsing errors in report (#6780)
* feat: add support for skipped files in run command and update HTML report template

* refactor: enhance skipped file handling in run command

* fix: improve error display in HTML report for skipped requests

* test: add unit test for HTML report generation of skipped requests with parsing errors

* test: update HTML report generation tests to check for skipped request summaries

* refactor: extract skipped result creation logic into a separate utility function

* refactor: enhance skipped result processing in run command to include additional metadata

* refactor: rename and enhance createSkippedResults function for improved skipped file processing

* refactor: remove unused stripExtension import from run command

* refactor: rename createSkippedResults to createSkippedFileResults for clarity and consistency
2026-01-13 18:51:27 +05:30
gopu-bruno
e6a48a73bf fix: move yup from peerDependencies to dependencies (#6794)
* fix: move yup from peerDependencies to dependencies

* chore: update package-lock.json
2026-01-13 18:49:11 +05:30
Sanjai Kumar
fceb99edc2 fix: autosave for environment tabs and folder-level auth (#6510)
* feat: enhance autosave middleware to support environment draft and folder auth drafts

* feat: extend autosave middleware to handle global and collection environment drafts

* feat: update authentication components to use unified updateAuth function

* refactor: rename updateAuth to updateFolderAuth for consistency in authentication components
2026-01-13 16:16:46 +05:30
Pooja
ebc105d42e add: autosave missing actions in middleware (#6781) 2026-01-13 12:38:07 +05:30
Abhishek S Lal
32d56f6942 fix: update modal size in CreateEnvironment component from small to medium (#6791) 2026-01-13 12:35:44 +05:30
Abhishek S Lal
e4a1fca3b1 feat: Improve response content type detection and SVG handling (#6741)
* feat: add SVG support for HTML preview in response format handling

* feat: enhance content type detection by adding support for AVIF and SVG formats

* fix: exclude SVG from byte format type detection in response preview

* feat: add helper function to detect SVG content in response handling

* fix: ensure SVG content type detection is case insensitive and remove EPS detection

* fix: correct byte offset for AVIF content type detection in response handling
2026-01-13 12:33:55 +05:30
Abhishek S Lal
59ff9bdafb Bugfix/workspace name case mismatch (#6560)
* fix: preserve workspace name casing in title bar (#6522)

* fix: improve workspace display name handling in title bar

---------

Co-authored-by: Uzairkazi695 <kaziuzair695@gmail.com>
2026-01-12 23:15:39 +05:30
naman-bruno
071ee9ab2e feat: workspace .env file support (#6777) 2026-01-12 13:40:38 +05:30
naman-bruno
176646f983 feat: add default .gitignore file creation in workspace and collection (#6778) 2026-01-12 13:26:45 +05:30
Anas Najam
d76a574c51 correct GitHub version badge URL in readme files (#6772) 2026-01-12 12:39:02 +05:30
Pragadesh-45
734ee16fe1 feat: apply modified dataBuffer to the response (#6023)
* feat: apply modified dataBuffer to the response

* fix: ensure dataBuffer regeneration only occurs when res.setBody() is called

* refactor: update dataBuffer handling in BrunoResponse
2026-01-12 11:39:26 +05:30
Pragadesh-45
33594bdcec feat: add zoom controls to key mappings (#6765) 2026-01-09 19:50:36 +05:30
Ryan
2acfe60a5f feat/ Theme-dependent screenshots in README (#6738)
* feat: add theme-aware landing image for dark/light mode

* feat: update README to use theme-aware landing images

* fix: correct filename spacing and theme logic for landing images
2026-01-09 18:58:37 +05:30
Sid
aecaab84dd feat: add script to list changed packages (#6678) 2026-01-09 16:30:52 +05:30
Abhishek S Lal
45264bfcc5 refactor: enhance WSRequestPane and WSResponsePane with ResponsiveTabs component (#6650)
* refactor: enhance WSRequestPane and WSResponsePane with ResponsiveTabs component

- Replaced custom tab implementation with ResponsiveTabs for better consistency and usability.
- Utilized useMemo and useCallback for performance optimizations in tab management.
- Cleaned up unused styles and improved error handling in both components.
- Updated StyledWrapper to remove legacy tab styles, streamlining the component structure.

* refactor: streamline authentication components and enhance WSRequestPane layout

- Removed unnecessary margin from StyledWrapper in ApiKeyAuth, BasicAuth, and BearerAuth components for a cleaner layout.
- Introduced a new right content area in WSRequestPane for better organization of authentication modes.
- Added a 'No Auth' view in WSAuth for improved user feedback when no authentication is selected.
- Cleaned up unused imports and optimized component structure for maintainability.
2026-01-09 14:06:15 +05:30
sanish chirayath
b01b8d7bc4 fix: grpc import paths (#6726)
* fix: grpc import paths

* refactor: extract protobuf include directory logic into a separate function

* rm: comment

* fix: improve filtering of enabled import paths in protobuf configuration

* refactor: streamline import path handling in protobuf configuration
2026-01-08 21:16:47 +05:30
Abhishek S Lal
58a38ac5a1 refactor: enhance tab management in ResponseExampleResponsePane component (#6655)
- Removed local state for activeTab and integrated Redux for tab state management.
- Added logic to retrieve and update the active tab using Redux store.
- Updated tab click handler to dispatch actions for tab changes.
2026-01-08 20:43:41 +05:30
Abhishek S Lal
7328988e59 refactor: simplify HtmlPreview component by extracting render logic into a separate function (#6740)
* refactor: simplify HtmlPreview component by extracting render logic into a separate function

* refactor: wrap renderHtmlPreview in a fragment for improved JSX structure

* fix: update preview visibility check in response format tests
2026-01-08 20:04:38 +05:30
Sanjai Kumar
39a6fc837d fix: Handle deleted environment variables in UI (#6703)
* fix: enhance environment variable management in collections slice

* test: refactor deleteEnvVar test
2026-01-08 20:00:43 +05:30
Sanjai Kumar
5b1b1b5541 fix: ephemeral environment variables being saved to filesystem (#6723)
* refactor: enhance environment variable persistence logic

* refactor: simplify environment variable persistence checks
2026-01-08 19:59:01 +05:30
Abhishek S Lal
578fa72dc8 refactor: enhance GrpcRequestPane and GrpcResponsePane with ResponsiveTabs component (#6649)
* refactor: enhance GrpcRequestPane and GrpcResponsePane with ResponsiveTabs component

- Replaced custom tab implementation with ResponsiveTabs for better structure and usability.
- Utilized useMemo and useCallback for performance optimizations in GrpcRequestPane.
- Removed unused imports and simplified tab management logic.
- Updated StyledWrapper to remove legacy tab styles, improving maintainability.

* fix: handle optional chaining for auth mode in GrpcRequestPane

* feat: enhance GrpcRequestPane and GrpcResponsePane with tab initialization and response count indicators

* refactor: simplify GrpcResponsePane tab management and enhance ResponsiveTabs key handling

- Removed unnecessary useMemo for tab initialization in GrpcResponsePane.
- Updated tab comparison logic in ResponsiveTabs to use key arrays for improved performance.
- Adjusted test locator for response tab count to use role-based selection for better accessibility.

* feat: add support for 'none' auth mode in GrpcAuth and integrate GrpcAuthMode in GrpcRequestPane

- Updated StyledWrapper in ApiKeyAuth, BasicAuth, BearerAuth, OAuth2, WsseAuth, and GrpcAuth components to remove unnecessary margin-top, ensuring a uniform appearance across authentication interfaces.
- Adjusted margin in GrantTypeSelector and WSAuth components for better layout consistency.

* refactor: update import statement and enhance error handling in GrpcRequestPane

- Changed the import of 'find' from lodash to a direct import for better clarity.
- Improved error handling by returning null during initialization when requestPaneTab is not set, ensuring smoother user experience.

* refactor: integrate StyledWrapper in SearchInput for improved styling

* refactor: update StyledWrapper color and adjust margin in GrpcTimelineItem for improved layout consistency
2026-01-08 15:25:39 +05:30
Sanjai Kumar
4708e8e589 fix: enhance collection item drop logic to prevent invalid moves (#6727) 2026-01-08 14:02:16 +05:30
Abhishek S Lal
2a9386ef6b fix: update ResponseExampleUrlBar styles for better overflow handling (#6535) 2026-01-08 02:34:23 +05:30
naman-bruno
9483dbf4af fix: yml format registration on collection import (#6735) 2026-01-08 00:49:48 +05:30
Abhishek S Lal
0b436e2c9f refactor: remove HTML validation functions and simplify HtmlPreview component logic (#6730)
* refactor: remove HTML validation functions and simplify HtmlPreview component logic

* chore: fix playwright - removed body value check since response is rendered in webview

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-07 22:46:31 +05:30
Anoop M D
9005e17eb5 Merge pull request #6707 from naman-bruno/improve/default-migration
improve: migration & default workspace handling
2026-01-07 19:14:01 +05:30
gopu-bruno
efe94d9c90 fix: Large Response Warning download button functionality (#6695)
* fix: Large Response Warning download button functionality
2026-01-07 15:23:10 +05:30
ganesh
848825f16a Change home image in readme with v3 UI (#6699)
* home image

* change usebruno to Bruno

* snapshot with default theme
2026-01-07 15:22:21 +05:30
naman-bruno
4d60425a05 fix: workspace already opened (#6721) 2026-01-07 14:35:03 +05:30
Abhishek S Lal
c03fe301f8 fix: avoid error toast while pasting non-cURL value in GQL url field (#6718) 2026-01-07 13:27:38 +05:30
Bijin A B
c8371410c2 chore: minor url bar alignment fixes and refactor (#6714) 2026-01-07 08:20:02 +05:30
Chirag Chandrashekhar
dcbf38bf61 fix: Query URL overflow pushes the action buttons outside view in gRPC and HTTP (#6706)
- Updated the QueryUrl component to handle text overflow. Now, overflow triggers scroll and does not move the action buttons out of view.
- Updated the GrpcQueryUrl component to handle text overflow. Now, overflow triggers scroll and does not move the action buttons out of view.
2026-01-07 07:40:58 +05:30
naman-bruno
a57ecde1d0 improve: migration & default workspace handling 2026-01-06 21:57:25 +05:30
Abhishek S Lal
791843174e refactor: improve tab state management in ResponsiveTabs component (#6687) 2026-01-06 17:13:56 +05:30
Pragadesh-45
1174f22d88 fix: improve Content-Type handling when request body is none (#6486) (#6540) 2026-01-06 17:02:53 +05:30
naman-bruno
8300abe086 fix: auth in cli (#6675)
* fix: auth in cli

* fixes
2026-01-05 22:06:26 +05:30
Abhishek S Lal
a3809ce4b9 style: remove unnecessary padding from pre elements in StyledWrapper component (#6674) 2026-01-05 20:39:58 +05:30
gopu-bruno
adb46110dd Fix/ws environment input alignment (#6672)
* style: enhance EnvironmentList component with improved flex properties

* refactor: remove report issue link for YAML format in CreateCollection component
2026-01-05 20:28:56 +05:30
naman-bruno
7cc4c0993e fix: atomic write issue (#6664) 2026-01-05 17:29:03 +05:30
Abhishek S Lal
1030d02ac7 fix: update hover background color in dark theme (#6666) 2026-01-05 17:28:49 +05:30
Anoop M D
d616be7271 Merge pull request #6661 from naman-bruno/cli/opencollection
add: oc support for cli
2026-01-05 16:09:51 +05:30
naman-bruno
afd49d146f add: oc support for cli 2026-01-05 15:57:49 +05:30
Abhishek S Lal
97e43c4489 feat: add native select styling to global styles (#6660) 2026-01-05 15:52:34 +05:30
gopu-bruno
f9af22d586 fix: apply infoTip styling to CodeMirror tooltip (#6658)
* style: apply infoTip styling to CodeMirror tooltip

* fix: add CodeMirror lint tooltip warning and  error text colors

* fix: update font size of CodeMirror lint tooltip
2026-01-05 14:16:52 +05:30
sreelakshmi-bruno
8590bacd79 add license and readme to bruno query package (#6654) 2026-01-05 13:22:54 +05:30
Bijin A B
a7d1a349e3 fix: lighten dark pastel theme modal background color (#6653) 2026-01-04 21:46:04 +05:30
Anoop M D
d03d8f01a1 feat: update @opencollection/types to version 0.7.0 and add demo image to GenerateDocumentation component (#6651) 2026-01-04 21:28:19 +05:30
lohit
97c700beba fix: update logic for checking formdata instances (#6643)
* fix: update logic for checking formdata instance

* fix: isFormData logic update

* fix: review comment fix, add isFormData to @usebruno/common package

* fix: review comment fix
2026-01-04 21:27:07 +05:30
Sid
b6a27bc66c fix: reverse sorting order for websocket messages (#6652) 2026-01-04 16:54:27 +05:30
Bijin A B
76a2889206 fix(ux): fix sidebar invisible for environments tab, grpc and ws (#6648) 2026-01-04 12:40:22 +05:30
Bijin A B
d506c37516 chore: adjust indent borders, table stripes, modal bg & tooltip bg (#6646) 2026-01-03 23:40:39 +05:30
naman-bruno
0c4ad0ed60 fix: escape closing tags in genrated docs (#6645) 2026-01-03 20:28:36 +05:30
SHUBH
30dbe34e2e fix: add a hard sequence in SSE and WS requests (#6569)
* fix: sse sequence in ipc layer

* fix: remove tick rate and flushing

* fix: added sequence logic for websockets

* fix: added sequence logic for websockets per request based

* fix: correct the order for how the messages are added.

`WSMessagesList` already handles a lot of the ordering for us, don't modify the order the messages are added since redirect and connection are internal states, it changes the execution trail

* chore: reduce whitespace diffs

* fix: a possible null case exception

Though we always create an empty data buffer at source so shouldn't happen unless that is modified

* fix: implement sequence logic for WebSocket messages

* fix: remove unused sequenceState property from WsClient

* fix: update message sorting logic to handle missing sequence numbers

* fix: remove unused lodash import

* fix: add clean method to sequencer for better sequence management

* fix: don't show dropdown when streaming

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2026-01-03 11:29:46 +05:30
Bijin A B
c83c055654 chore: theme updates (#6642) 2026-01-03 00:08:14 +05:30
Abhishek S Lal
33f47ca5e4 style: update color references to use theme.draftColor for unsaved changes across multiple components (#6641) 2026-01-02 23:32:55 +05:30
Anoop M D
475707848e style: enhance syntax highlighting in GQL Docs and Doc Gen (#6640)
* style: enhance syntax highlighting and theme integration in QueryEditor and GenerateDocs components

* fix: fixed generate code theming issues
2026-01-02 22:47:19 +05:30
Chirag Chandrashekhar
2d76c444f6 feat: integrate theme support in ImportCollection component (#6639)
* feat: integrate theme support in ImportCollection component for improved styling consistency

* chore: remove debug console log from ImportCollection component

* refactor: update theme import in ImportCollection component for improved consistency
2026-01-02 22:16:52 +05:30
Abhishek S Lal
33361ba659 refactor: update Tabs component structure and theme colors (#6638)
- Replaced the div wrapper in the Tabs component with StyledWrapper for improved styling.
- Simplified TabsList and TabsTrigger components by removing theme dependency and using classnames for styling.
- Updated inactive background colors in multiple theme files to enhance visual consistency across themes.
2026-01-02 22:01:35 +05:30
Sanjai Kumar
1e3a0d9af3 style: update variable validation colors in StyledWrapper components to use theme colors (#6633) 2026-01-02 21:26:18 +05:30
Chirag Chandrashekhar
2a5ec854cc style: update text color classes and button hover effects in RunnerResults (#6637)
* style: update text color classes and button hover effects in RunnerResults and StyledWrapper components

* Update StyledWrapper.jsx

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-02 21:21:10 +05:30
Abhishek S Lal
d758645144 feat: update "Show in Folder" label based on platform in ManageWorkspace, Collection, CollectionItem, and WorkspaceHome components (#6623)
* feat: update "Show in Folder" label based on platform in ManageWorkspace, Collection, CollectionItem, and WorkspaceHome components

* refactor: remove duplicate "Rename" item push in CollectionItem component
2026-01-02 20:57:51 +05:30
Abhishek S Lal
902d9ff968 refactor: update color references in OAuth2 components to use theme.primary.text for improved consistency (#6629)
* refactor: update color references in OAuth2 components to use theme.primary.text for improved consistency

* refactor: update modal size in ImportCollectionLocation component for improved consistency

* refactor: set isActiveTab prop in QueryResponse component and update active color in StyledWrapper for consistency
2026-01-02 20:57:20 +05:30
sanish chirayath
781def844d fix: tooltip styling (#6632) 2026-01-02 20:53:47 +05:30
sanish chirayath
15065eb2f1 fix: file picker component overflow (#6635) 2026-01-02 20:52:32 +05:30
sanish chirayath
481fa4ed12 fix: authmode in graphql (#6636) 2026-01-02 20:51:19 +05:30
Sanjai Kumar
a35b455041 style: remove font size class from button (#6630) 2026-01-02 18:50:06 +05:30
gopu-bruno
bfc8968e24 fix: folder settings var panel table alignment (#6631) 2026-01-02 18:48:26 +05:30
Sanjai Kumar
f90f256f5f fix: modal icon colors to match button colors (#6624)
* style: update warning icon color in RemoveCollection and ConfirmSwitchEnv components to use theme colors

* fix: font size in BulkEditor

* style: update error message styling to use theme colors in QueryResult component

* style: update warning icon color

* style: update warning color in ConfirmSwitchEnv

* chore: minor pr comment

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-02 18:18:36 +05:30
Pooja
4253080f6b fix: toggle switch color (#6627) 2026-01-02 18:01:50 +05:30
naman-bruno
7a83641436 update cdn links (#6628) 2026-01-02 17:17:14 +05:30
Bijin A B
7105e4e8ed fix(UX): fix sandbox selector options background color (#6626) 2026-01-02 16:49:12 +05:30
Abhishek S Lal
849465d62a Feat/v3 UI updates (#6618)
* style: enhance button layout and input styles across multiple components for improved UI consistency

* style: update RequestsNotLoaded component with new warning styles and enhance theme color definitions for status indicators

* refactor: update theme usage across components for consistency

- Changed color references from theme.brand to theme.primary.text in various StyledWrapper components.
- Added hover effects to enhance UI interactivity in CollectionSettings and FolderSettings.
- Removed unnecessary margin and padding adjustments in several components for cleaner layout.
- Improved accessibility by ensuring aria attributes are correctly set in MenuDropdown.
- Standardized styling for method indicators in RequestPane components.

These changes aim to create a more cohesive look and feel across the application while adhering to the updated theme guidelines.

* refactor: clean up method selector styling in NewRequest component

* chore: temp playwright test fixes

* refactor: update modal sizes across various components for consistency

- Changed modal size from "sm" to "md" in RenameWorkspace, CreateApiSpec, CloneCollection, DeleteCollectionItem, and RenameCollection components.
- Improved styling in HttpMethodSelector by adding padding for better layout.
- Updated theme color references in multiple theme files to use a new palette structure for consistency and maintainability.

* refactor: enhance styling and theme integration in TimelineItem components

- Updated HttpMethodSelector to clarify padding calculation in comments.
- Integrated theme colors for OAuth2 indicator and timestamp in TimelineItem for better visual consistency.
- Adjusted Method component to use uppercase styling for method display.
- Modified RelativeTime component to apply muted text color for improved readability.
- Updated INFO color in dark and light themes for better contrast and accessibility.

* refactor: remove duplicate import statements in theme files

- Cleaned up import statements in vscode.js and light-pastel.js by removing redundant lines for improved code clarity and maintainability.

* refactor: improve styling and theme integration in various components

- Added accent color and cursor style for checkbox inputs in Modal's StyledWrapper.
- Updated border-radius values in HttpMethodSelector and NewRequest StyledWrapper components to use theme variables for consistency.
- Introduced a new textbox class in NewRequest StyledWrapper for better styling control.
- Changed modal size from "sm" to "md" in CreateEnvironment for improved layout.

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-02 16:48:47 +05:30
naman-bruno
058d2e0e61 Improve delete collection in workspace overview (#6587)
* Improve delete collection in workspace overview

* fixes
2026-01-02 15:11:39 +05:30
gopu-bruno
c03c5eb927 feat: update toast UI to match theme (#6622) 2026-01-02 15:05:34 +05:30
Bijin A B
156e798f90 feat(theme): update secondary button styles (#6621) 2026-01-02 14:21:28 +05:30
Anoop M D
a1a90d19e8 feat: enhance HttpMethodSelector to include caret indicator when creating new request (#6620) 2026-01-02 04:27:06 +05:30
Anoop M D
ec40d7fc85 fix: update @opencollection/types to version 0.6.0 and refactor auth handling in request items (#6619) 2026-01-02 04:07:06 +05:30
gopu-bruno
5fda0866f8 feat: use theme colors for Console method badges (#6603)
* feat: use theme colors for Console method badges

* chore(theme): bruno devtools UX updates

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-02 04:06:55 +05:30
Sanjai Kumar
f611c05fe8 fix: update Notifications and StatusBar components for improved functionality and styling (#6607) 2026-01-02 02:41:05 +05:30
naman-bruno
b3764e1703 fix: invalid collection in workspace (#6612) 2026-01-02 01:21:44 +05:30
naman-bruno
f961e692ee add: global env and workspace flag (#6534)
* add: global env and workspace flag

* rm: await

* update: option name

* fix

* fix
2026-01-02 01:15:45 +05:30
Sanjai Kumar
a7237c2e41 feat: improve RunnerResults filter bar to use theme system (#6613)
* feat: integrate theme support in RunnerResults component for improved styling

* refactor: simplify RunnerResults component and enhance filter button styling

* style: adjust padding in StyledWrapper and remove aria-pressed from FilterButton
2026-01-02 01:13:23 +05:30
sanish chirayath
3ccaf29ddd fix: use theme styling within timeline (#6604)
* fix: use theme styling within timeline

* fix: remove inline styling and use css classes

* fix: network logs within dev tools

* compact timeline for grpc

* refactor: standardize CSS class naming in StyledWrapper components for better readability

* remove styling configuration from Network component

* fix: update colors

* update colors

* fix: color
2026-01-01 23:58:47 +05:30
Abhishek S Lal
002a5d16eb refactor: improve theme handling in ThemeProvider for better responsiveness to system preferences (#6606)
* refactor: improve theme handling in ThemeProvider for better responsiveness to system preferences

- Introduced helper functions to determine effective theme and apply it to the root element.
- Updated theme application logic to respond to system theme changes more efficiently.
- Simplified theme computation to avoid race conditions by directly using storedTheme.

* fix: update displayedTheme initialization in ThemeProvider to use storedTheme for consistency
2026-01-01 23:55:06 +05:30
gopu-bruno
877b4dcf3a fix: folder docs save button visibility issue (#6617) 2026-01-01 23:52:38 +05:30
sanish chirayath
b9856f8c64 Feat/update file picker (#6614)
* styling: file-picker editor component

* use filepicker component within filebody and response example filebody

* edit example to use button components

* fix: hide delete, disable checkbox in preview mode

* make label italic

* chore: change example cta buttons to filled style

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2026-01-01 23:47:58 +05:30
gopu-bruno
e63bac6ce4 fix: resolve request pane tooltip visibility issue (#6615) 2026-01-01 23:19:27 +05:30
Anoop M D
23e809e827 feat: improved dark mode color (#6616) 2026-01-01 22:06:38 +05:30
naman-bruno
1a4a30c8f2 add: beta tag for opencollection & fix create collection location behaviour (#6594)
* add: beta tag for opencollection

* fixes

* fix
2026-01-01 17:04:34 +05:30
naman-bruno
2c973bbd35 Merge pull request #6583 from naman-bruno/add/collection-docs
add: collection-docs
2026-01-01 17:01:31 +05:30
naman-bruno
8e74fa6233 fix: collection already opened in other workspace (#6574)
* fix: collection already opened in other workspace

* fix

* fixes
2026-01-01 14:30:26 +05:30
sanish chirayath
1ec8f55a9e fix: theme within grpc timeline (#6581)
* fix: theme within grpc timeline

* fix: use font from the theme

* remove y padding to make timeline item more compact

* fix: font

* fix: padding

* fix: use fira code

* fix: icon spacing

* add border to the method  search

* show bg for message section within request
2025-12-31 23:31:39 +05:30
Pooja
1ae05dfb0e fix: prefrence modal width (#6595) 2025-12-31 22:03:33 +05:30
gopu-bruno
72f186b38a style: update OAuth2 section labels for improved consistency and readability (#6598) 2025-12-31 21:50:22 +05:30
gopu-bruno
ea1002c7a0 fix: standardize table border colors and improve table styling (#6597) 2025-12-31 20:04:08 +05:30
gopu-bruno
89ed1da4de Fix auth panel UI updates (#6590)
* style: update padding and font size in OAuth2 and Table components for improved consistency

* style: update font styles in OAuth2 components for improved readability

* fix: add missing semicolon in StyledWrapper.js for consistent styling
2025-12-31 19:58:17 +05:30
lohit
4d519df8bc fix: oauth2 callback url field placeholder text update (#6588) 2025-12-31 18:20:02 +05:30
Sanjai Kumar
71413b9154 chore: update delete confirmation modals to use danger button color (#6589)
* refactor: remove size prop from Button components for consistency across modals and improve styling

* style: update confirm button colors in modal components for consistency
2025-12-31 18:13:33 +05:30
Chirag Chandrashekhar
ce9773b7c9 fix: Add New Request CTA alignment in tabs (#6584)
* fix: Add New Request CTA alignment in tabs
- Moved the '+' icon before the chevron to maintain alignment once chevrons appear
- Added padding to the '+' icon for better spacing.

* refactor: streamline New Request button rendering in RequestTabs component

- Simplified the rendering logic for the New Request button by removing unnecessary conditional wrappers.
- Ensured the button remains functional and maintains its styling within the tab layout.
2025-12-31 17:00:41 +05:30
Pooja
8a394cdafc remove max height for keybinding table (#6586) 2025-12-31 16:35:15 +05:30
Abhishek S Lal
ddc88b3b05 style: enhance CodeMirror dropdown styles with theme integration (#6577)
* style: enhance CodeMirror dropdown styles with theme integration for improved consistency

* style: refine dropdown and CodeMirror hint styles for improved consistency and usability

* style: clean up scrollbar and CodeMirror hint styles for improved readability
2025-12-31 15:10:16 +05:30
Abhishek S Lal
746a49faed style: enhance theme dropdown and security settings with improved styles and active indicators (#6582) 2025-12-31 14:29:52 +05:30
sanish chirayath
2827a6f133 fix: cookie modal theme (#6580)
* fix: cookie modal theme

* update toggle switch to use primary color
2025-12-31 13:38:53 +05:30
sanish chirayath
1ed9d61ee8 fix: radios buttons to use primary color (#6576) 2025-12-31 13:37:27 +05:30
sanish chirayath
ac0b69787d fix: use themes within protobuf section (#6575)
* fix: use themes within protobuf section

* chore: fix font weight for protobuf settings

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-31 13:36:41 +05:30
sanish chirayath
ab04850367 use themes within grpc (#6568)
* rm: redundant code

* add: theme to grpc

* keep same color for grpc as that of sidebar

* fix: checkbox color

* fix: use dropdown colors

* rm: redundant lines

* use: theme for grpc icons
2025-12-31 00:34:33 +05:30
sanish chirayath
1c9db0886d fix oauth checkbox, button styles (#6572)
* fix: checkboxes

* use secondary button
2025-12-31 00:00:58 +05:30
Abhishek S Lal
b75c9fdd6d style: update checkbox styles and colors in RunConfigurationPanel for improved visibility (#6570)
* style: update checkbox styles and colors in RunConfigurationPanel for improved visibility

* style: update RunnerResults component styles for configuration options and panels

* style: refine checkbox styles in StyledWrapper for consistency and clarity
2025-12-30 23:07:28 +05:30
naman-bruno
27dff7567c add: export info & remove unused component (#6547)
* add: export info

* changes
2025-12-30 23:06:57 +05:30
Bijin A B
8fa8ae5fed feat(sandbox): create a dropdown selector for sandbox mode (#6519) 2025-12-30 23:03:06 +05:30
Abhishek S Lal
0848393319 refactor: replace button elements with Button component for consistency (#6567)
* refactor: replace button elements with Button component for consistency and improved styling across various components

* chore: css cleanup

* chore: fix collection doc cancel button

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-30 21:06:10 +05:30
sanish chirayath
76f8bce9ac fix: oauth setup component colors (#6565)
* fix: oauth colors

* rm: redundant conditionals
2025-12-30 20:47:31 +05:30
sanish chirayath
676f8223ec fix: colors (#6564) 2025-12-30 20:29:43 +05:30
sanish chirayath
36b0a90de3 fix: use generic error component, tab padding fixes (#6563)
* fix: use generic error component, tab padding fixes

* fix: script error padding

* rename errorMessage to error banner, move to ui folder

* fix: replace errorAlert with errorBanner component

* show orange dot always
2025-12-30 18:48:53 +05:30
sanish chirayath
d7cef7aa4e fix: example tab not closing post delete, tab not found issue when i delete intermediate example (#6561) 2025-12-30 14:44:57 +05:30
gopu-bruno
5dad137631 fix: update secret var alert icon to use danger text color (#6562) 2025-12-30 14:33:57 +05:30
Abhishek S Lal
8b1d59fa74 feat: enhance ThemeDropdown with keyboard navigation and improved layout (#6554)
* feat: enhance ThemeDropdown with keyboard navigation and improved layout

- Added keyboard navigation support for theme selection.
- Refactored theme selection UI to improve accessibility and user experience.
- Updated styles for better responsiveness and visual clarity.
- Consolidated theme rendering logic for light and dark themes.

* style: update ThemeDropdown styles for focus and active states

- Removed outline styles for focused states to enhance visual clarity.
- Adjusted background and border colors for active state to improve accessibility and user experience.
2025-12-30 14:30:50 +05:30
Bijin A B
3a6f2f26ee chore: minor layout fixes (#6559) 2025-12-30 13:37:53 +05:30
naman-bruno
6f71717105 fix: error style for env & workspace rename (#6553) 2025-12-30 12:24:36 +05:30
Anoop M D
2b28d37c74 Merge pull request #6552 from usebruno/feat/update-primary-colors
feat: update primary colors in schema
2025-12-30 07:20:27 +05:30
Anoop M D
7675c1a4d8 feat: update primary colors in schema 2025-12-30 07:19:54 +05:30
Anoop M D
6afbaa0d91 feat: design system doc + cleanup codemirror info + collection overview icons (#6551) 2025-12-30 06:58:52 +05:30
naman-bruno
c0ac24d090 fix: default workspace docs (#6548) 2025-12-30 03:29:18 +05:30
Bijin A B
41b37c7805 feat(UX): update theme selector in preferences modal (#6550) 2025-12-30 03:23:24 +05:30
Anoop M D
6d77cacbc4 feat: dark mode input revamp (#6546) 2025-12-29 22:37:49 +05:30
Chirag Chandrashekhar
0d536fb365 refactor: simplify layout and styling in QueryUrl and HttpMethodSelector (#6545)
* refactor: simplify layout and styling in QueryUrl and HttpMethodSelector components; remove unused method selector width state

* chore: test fixes

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-29 22:09:39 +05:30
gopu-bruno
9a78432dc0 fix: unify hover and keyboard focus styles for collection component (#6544) 2025-12-29 20:57:15 +05:30
Abhishek S Lal
63d31825ff fix: update dropdown item text color to use theme text color for consistency; enhance ExampleItem dropdown behavior with fixed positioning (#6543) 2025-12-29 19:29:53 +05:30
gopu-bruno
018f39239f fix: replace hardcoded yellow text color with theme brand color (#6538) 2025-12-29 17:41:30 +05:30
Abhishek S Lal
1b57b6bee6 fix: initialize selected environment correctly to prevent flicker in EnvironmentSettings component; remove transition from StyledWrapper in RequestTabs and WorkspaceTabs (#6536) 2025-12-29 16:59:22 +05:30
naman-bruno
646f63dbeb add: filetype for import collection (#6533) 2025-12-29 16:53:43 +05:30
Anoop M D
c714e9b5d6 feat: default dark mode theme tweaks (#6531) 2025-12-29 06:13:56 +05:30
Bijin A B
f5ed96ad16 chore: reorder collection item menu option info (#6524) 2025-12-27 23:44:56 +05:30
Anoop M D
f40e4d2d79 feat: theme + ux overhaul (#6520)
* feat: theme + ux overhaul

* chore: moved @opencollection/types at root of monorepo

* chore: fixed review comments

* chore: addressed review comments

* chore: fix playwright tests

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-27 13:52:20 +05:30
Bijin A B
84f572fa88 chore: fix minor runtime warnings (#6518) 2025-12-27 00:17:14 +05:30
lohit
faec95f623 feat: collection-level and app-level proxy settings updates (#6514)
* feat: collection and app proxy settings updates

* fix: opencollection proxy config export and import

* fix: coderabbit review fixes
2025-12-26 22:17:41 +05:30
Sanjai Kumar
cd6ffc2447 refactor: replace button elements with new Button component (#6512)
* refactor: replace button elements with new Button component

* chore: fix button size for consistency

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-26 19:25:45 +05:30
Bijin A B
d37bf7a5ad chore: update responsive tab's more icon (#6509) 2025-12-26 17:11:56 +05:30
Bijin A B
b41f4974f9 chore: playwright fix (#6507) 2025-12-25 15:16:34 +05:30
naman-bruno
2446301e41 use: button component (#6504)
* use: button component

* fixes
2025-12-25 12:33:49 +05:30
naman-bruno
67903f26bc export & import in opencollection format (#6329) 2025-12-24 22:28:38 +05:30
Abhishek S Lal
1b8eece173 Add right-click context menu to request tabs with MenuDropdown # (#6502)
* refactor: replace Dropdown with MenuDropdown in RequestTab component; update Dropdown props handling in Dropdown component

* refactor: remove Portal import and simplify menuDropdown rendering in RequestTab component

* refactor: streamline RequestTabMenu functionality and improve tab closing methods with async handling

* refactor: enhance Dropdown and MenuDropdown components with improved props handling and styling adjustments

* refactor: enhance Dropdown and MenuDropdown components by improving structure and removing unused styles

* refactor: update Dropdown and MenuDropdown components to append to sidebar sections container for improved layout

* refactor: integrate dropdownContainerRef for improved MenuDropdown positioning in RequestTabs and Sidebar components

* refactor: update Dropdown component to include 'tippy-box' class for e2e test selections

* refactor: update dropdown item selection logic in selectRequestPaneTab function for improved accuracy

* refactor: add fixed positioning to popperOptions in Collection and CollectionItem components for improved dropdown behavior

---------

Co-authored-by: sanjai <sanjai@usebruno.com>
2025-12-24 21:08:53 +05:30
Pooja
1f05ffd469 fix: pasting request ito parent folder even if request is selected (#6446) 2025-12-24 12:14:37 +05:30
Anoop M D
c2acc25461 Merge pull request #6498 from usebruno/feat/button-storybook
feat: button storybook
2025-12-24 05:52:09 +05:30
Anoop M D
dc9df80638 feat: update button component with new rounded options and story 2025-12-24 05:51:32 +05:30
Anoop M D
c5abe4122b feat: button storybook 2025-12-24 05:30:04 +05:30
naman-bruno
3081c06964 update: modal styles (#6487)
* update modals styles

* chore: color and style improvements

* fix: tests

* fixes: tests

---------

Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
2025-12-23 23:29:03 +05:30
naman-bruno
8c7ed3fe51 improve: workspace handling (#6495)
* improve: workspace

* fixes
2025-12-23 20:22:51 +05:30
Pooja
ce33cee03d fix: autosave (#6392)
* fix: autosave

* rm: console
2025-12-23 19:21:56 +05:30
Abhishek S Lal
d93d1eacdb refactor: centralize tab management (#6494)
* refactor: centralize tab management by removing redundant hide calls in Collection components

- Removed dispatch calls for hiding home and API spec pages from Collection and CollectionItem components.
- Added logic in app slice to automatically hide these pages when a tab is added or focused, improving code maintainability.

* refactor: remove redundant hideHomePage dispatches from components
2025-12-23 19:19:25 +05:30
Abhishek S Lal
aeb6b12b06 fix: update SensitiveFieldWarning prop name for consistency in WsseAuth component (#6492) 2025-12-23 17:56:46 +05:30
lohit
41ed51b4e3 fix: handle additional context root paths for node-vm (#6491)
* fix: handle additional context root paths for node vm

* fix: handle additional context root paths for node vm

* fix: coderabbit review fixes
2025-12-23 17:31:51 +05:30
Sid
b85f60e1d6 fix: prevent double serialization of websocket text messages. (#6182) (#6479)
* fix: prevent double serialization of websocket text messages. (#6182)

* fix: improve websocket message handling and serialization

- Added normalization for message format to prevent double encoding.
- Updated queueMessage and sendMessage methods to handle message format.
- Refactored code for better readability and maintainability.

fix: enhance message normalization in WebSocket client

---------

Co-authored-by: Praveen kumar <praveenkumar042023@gmail.com>
2025-12-23 17:08:30 +05:30
naman-bruno
49ffdd1b8f fix: linux titlebar (#6483) 2025-12-23 16:54:30 +05:30
Abhishek S Lal
f1961a8988 refactor: update ResponsePane and QueryResultTypeSelector (#6490)
* refactor: update ResponsePane and QueryResultTypeSelector for improved tab handling and styling

- Adjusted the expanded width for right-side action buttons in ResponsePane.
- Refactored view tab toggle logic to enhance clarity and functionality.
- Introduced new styling for result view tabs and dropdown buttons.
- Added icon support for format options in QueryResultTypeSelector, improving visual feedback.
- Implemented dropdown state management to ensure proper interaction with active tabs.

* refactor: remove console log from ResponsePane for cleaner code
2025-12-23 16:38:24 +05:30
lohit
4831434e37 fix: oauth2 url update (#6489) 2025-12-23 16:00:17 +05:30
Sanjai Kumar
87c8934c45 fix: update stringifyHttpRequest to handle response body content correctly (#6488) 2025-12-23 15:48:52 +05:30
Pooja
01d4d3dc2a fix: run formatResponse execution on copy button click (#6485) 2025-12-23 14:25:18 +05:30
naman-bruno
70178f60b3 add: rename folder option while creating workspace (#6481) 2025-12-22 18:29:33 +05:30
Abhishek S Lal
cba164bc9b fix: update selectedTab prop to use selectedViewTab in ResponsePane component (#6478) 2025-12-22 14:58:54 +05:30
Pooja
669c99f40a fix: copy response based on preview toggle and selected format (#6436) 2025-12-22 13:53:08 +05:30
Abhishek S Lal
9967d863f5 feat: enhance ResponsePane with persisted response format and view tab state management (#6475)
- Added Redux state management for response format and view tab in ResponsePane.
- Implemented useCallback hooks for handling format changes and view tab toggling.
- Updated component to utilize persisted values from Redux, improving user experience by maintaining state across sessions.
2025-12-22 13:51:19 +05:30
Abhishek S Lal
3552801ca5 fix: refactor default tab selection logic for correct tab persistance (#6473) 2025-12-22 13:40:51 +05:30
Pooja
6f2804ea0f fix: var into tooltip for faker vars (#6312)
* fix: var into tooltip for faker vars

* fix: oauth variable

* rm: test

* rm: comment
2025-12-22 13:02:51 +05:30
Pragadesh-45
41efa8505b fix: restrict keyboard event handling to modal elements only (#6408)
* fix: restrict keyboard event handling to modal elements only

* chore: minor refactor

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-19 19:01:08 +05:30
Thomas
f47e9e9304 Add file attribute to Junit testsuite report (#6425)
* feat: add file attribute to Junit testsuite report

* test: update tests to include file attribute

* fix: update playwright tests to support the new file attribute

---------

Co-authored-by: Thomas Vackier <thomas.vackier@inthepocket.com>
Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-19 19:00:15 +05:30
Pooja
5f88e7d201 fix: auto-expand collapsed sidebar section when clicking action buttons (#6468) 2025-12-19 16:15:52 +05:30
Chirag Chandrashekhar
e4e17b0c74 fix: reverted the easy creation flow to the old, modal based approach (#6449)
* fix: reverted the easy creation flow to the old, modal based approach

* fix: updated the tests to use the old createRequest action and removed the usage of createUntitledRequest

* removed safe mode selection after collection open
2025-12-19 16:13:47 +05:30
Sid
83feffd41d chore: have setup install all deps (#6421)
* chore: have setup install all deps

* Update scripts/setup.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-19 16:05:28 +05:30
Chirag Chandrashekhar
7d783d473f feat: ability to open terminal at the root of a workspace (#6467)
* enhancement: ability to open terminal at the root of a workspace

* fix: imports

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-19 15:21:28 +05:30
dependabot[bot]
6a177e17d3 chore(deps): bump actions/upload-artifact from 5 to 6 (#6416)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-19 13:59:12 +05:30
Chirag Chandrashekhar
3552e7e609 Fix: Terminal Resize (#6448)
* fix: added watcher on parent div resize and window resize and calls fit() on both events

* removed effects and moved the resize handling to ref callback

* fix: added ResizeObserver cleanup

* fix: improve terminal resizing logic

- Refactored fit logic to avoid unnecessary calls during hidden states.
- Enhanced error handling when creating new terminal sessions.
- Updated ResizeObserver to ensure proper fitting on container resize.

* fix: remove unused fitRafRef in TerminalTab component

* fix: refactor terminal mount logic to use a dedicated callback

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-19 12:34:11 +05:30
Chirag Chandrashekhar
7164119695 fix: updated the selected state colors of tabs in dev tools (#6465) 2025-12-19 02:36:30 +05:30
Anoop M D
6a05b04676 Merge pull request #6463 from usebruno/feat/design-tweaks
feat: design tweaks
2025-12-19 00:32:49 +05:30
Anoop M D
8c1975ba7b feat: design tweaks 2025-12-19 00:32:02 +05:30
naman-bruno
397ccbb425 fix: export/import icons and styles (#6462) 2025-12-19 00:00:12 +05:30
Anoop M D
336496a1d7 Merge pull request #6461 from usebruno/feat/opencollection-presets
feat: opencollection presets
2025-12-18 22:43:38 +05:30
Anoop M D
aadbf8c33f feat: opencollection presets 2025-12-18 22:33:35 +05:30
Anoop M D
c5827dfa72 feat: opencollection actions (#6460) 2025-12-18 21:21:54 +05:30
Anoop M D
9738a2afb7 feat: opencollection actions 2025-12-18 21:19:29 +05:30
naman-bruno
a1c4113897 add: workspace tabs (#6456)
* add: workspace tabs

* fixes

* fixes
2025-12-18 21:01:58 +05:30
sanish chirayath
052d143d6e fix: example icon color (#6447)
* fix: example icon color

fix: example color

* fix: indentation

* fix: use gray color from colors for example

* fix:  margin  issues
2025-12-18 20:37:59 +05:30
Siddharth Gelera (reaper)
aac219d4cd fix: move gql variables prettify icon to a better position (#6455)
* move gql variable prettier icon down

* fix: doesn't need the class
2025-12-18 19:47:16 +05:30
sanish chirayath
b188a9e9a9 fix: unable to add assertions to a request (#6435)
* fix: add assertion

* rm: unnecessary wait fn

* fix: test

* fix: tests

* fix: review comments

* fix: review

* fix: review comments

* fix: review comments

* fix: test failure

* review fixes

* fix: rm sandbox accept

* fix: indentation
2025-12-18 19:37:33 +05:30
naman-bruno
6ab8fcb710 fix: duplicate message on workspace rename and close (#6457) 2025-12-18 18:48:57 +05:30
Sanjai Kumar
1cc117ceb9 fix: crash when saving empty GraphQL query in YAML collections (#6453)
* fix: ensure GraphQL variables are handled correctly in multiple components

* fix: reverted some changes

* chore: temp fix for tests

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-18 18:37:48 +05:30
Abhishek S Lal
62b8784972 feat: add hideResultTypeSelector prop to QueryResponse component (#6452)
* Introduced hideResultTypeSelector prop to conditionally render the QueryResultTypeSelector in the QueryResponse component.
* Updated BodyBlock to pass the type prop to control the visibility of the result type selector based on the request or response context.
* Adjusted styling in StyledWrapper for improved layout consistency.
2025-12-18 17:48:53 +05:30
Pooja
5e6444b8b5 feat: Set JavaScript sandbox to safe mode by default for new collections (#4824)
* feat: Set JavaScript sandbox to safe mode by default for new collections

* rm: sandbox code in playwright test

* rm: safe mode code in var interpolation test

* rm: sandbox modal code

* fix

* fix

* fix

* fix

* improve

* improvement

* fix

* fix
2025-12-18 17:27:38 +05:30
naman-bruno
bc2efb9686 Environment's as tabs (#6407)
* add: env's as tabs

* fix: test

* fix: tests

* fixes

* fix: test

* fixes

* fixes

* fix

* fix: styling

* fixes
2025-12-18 16:10:00 +05:30
Chirag Chandrashekhar
678fa88a7c perf: linkAware slow in large files (#6422)
* bugfix: linkAware slow in large files
- Added link detection and class addition operations in an editor.operation block for atomic operations and prevent multiple small rerenders.
- linkAware works on currently visible lines in the viewport. This is to speedup linkAware and defer detection for lines not in viewport.
- linkAware now runs after initial render and not before it. This ensures that we calculate to the lines on viewport and does not pause render.

* test(bruno-app): fix linkAware spec for debounced viewport marking
2025-12-18 15:52:04 +05:30
naman-bruno
80e09d1a26 fix: opencollection export as bruno json (#6444) 2025-12-18 14:24:45 +05:30
Abhishek S Lal
78ee99eab9 Fix/app titlebar windows (#6437)
* style: Update padding and margin in StyledWrapper for improved layout; adjust ActionIcon size in ResponseLayoutToggle for better UI consistency; enhance title bar color handling in Electron app

* feat: Enhance AppTitleBar with Windows-specific controls and OS detection

* refactor: Improve OS detection and error handling in AppTitleBar; streamline maximize state management

* feat: Implement IPC communication for maximize/unmaximize events in AppTitleBar; enhance state management in Electron main process
2025-12-17 21:40:24 +05:30
naman-bruno
73124fd715 add: manage workspace (#6424)
* add: manage workspace

* fixes

* replace dropdown to MenuDropdown

* rm: refs
2025-12-17 20:35:18 +05:30
naman-bruno
4c1fba611a fix: close all collection in workspace (#6434)
* fix: close all collection in workspace

* move: function
2025-12-17 18:52:17 +05:30
naman-bruno
3cfbf890ac add: export & import of workspace as zip (#6432)
* init

* fix

* update: package lock
2025-12-17 18:49:02 +05:30
Sanjai Kumar
395aa4246e fix: OpenAPI import fails when securitySchemes are not defined (#6429)
* feat: enhance OpenAPI security scheme handling

* refactor: revert test changes and update openapi-to-bruno
2025-12-17 16:52:54 +05:30
Pooja
639c8e573f fix: response pane size when devtool open (#6380) 2025-12-17 12:15:11 +05:30
Bijin A B
7d317a775b fix(playwright): interpolate request url with odata param (#6428) 2025-12-17 12:14:42 +05:30
Timon
2eb8db9b45 fix: Only update scroll position when unmounting the editor (#6420)
before the scroll position was updatet on every scroll, causing
everything related to the tab to rerender.
2025-12-16 18:34:29 +05:30
Abhishek S Lal
30d2a6d141 Refactor dropdown components to use MenuDropdown for improved functionality and keyboard accessibility (#6404)
* Refactor dropdown components to use MenuDropdown for improved functionality and keyboard accessibility

- Replaced Dropdown with MenuDropdown in various components including BodyModeSelector, AuthMode, and RequestBodyMode.
- Updated styles and structure for better usability and accessibility.
- Removed unused Dropdown component and its associated styles.
- Enhanced action buttons in ResponsePane and Collection components with ActionIcon for better UI consistency.

* fix: Update HttpMethodSelector styles and tests for improved accessibility

- Changed the class name for the "Add Custom" button to include 'text-link' for better styling.
- Updated tests to use role-based queries for dropdown items, enhancing accessibility checks.
- Ensured the correct application of classes in tests to reflect the updated structure.

* refactor: Improve component accessibility and consistency

* fix: update hover behavior for collection actions menu in runner.ts

* refactor: streamline hover interactions for collection actions across tests

* refactor: enhance component structure and accessibility across response actions

* fix: correct fill property syntax in StyledWrapper for consistent styling

* refactor: simplify isDisabled logic in response components for clarity

* fix: correct tabIndex logic in ResponseCopy component for improved accessibility

* fix: update tabIndex logic in ResponseBookmark component for improved accessibility

* fix: enable action buttons in ResponsePaneActions for improved usability

* refactor: remove unnecessary tabIndex attributes in response components for improved accessibility

* refactor: remove keyDown event handlers from response components for cleaner interaction

* refactor: remove SidebarHeader component and related styles for improved structure
2025-12-16 18:26:38 +05:30
lohit
231776ca4b feat: use default browser for oauth2 authorization bru-2167 (#6101)
* feat: use default browser for oauth2 authorization bru-2167

* fix: coderabbit review comment fixes

* fix: coderabbit review comment fixes

* fix: protocol registration updates

* fix: coderabbit review comment suggestions

* fix: oauth2 auth form use system browser option
2025-12-16 17:23:49 +05:30
Pooja
dbd966850c fix: openapi body import (#6288)
* fix: openapi body import

* add: unit test

* fix

* fix

* Revert "fix"

This reverts commit 3219e8af8e.

* fix: we need the same check here too!

* fix: handle number type

* fix: correct empty securitySchemes check

---------

Co-authored-by: Taylore Thornton <tthornton3@chewy.com>
2025-12-16 17:23:22 +05:30
Pooja
dc111ecce2 add: presets in collection setting (#6389)
* add: presets in collection setting

* fix

* add: websocket in preset

* fix: htmlFor
2025-12-16 17:16:41 +05:30
Pooja
fdff792476 feat: add support for ssl cert in websockt (#6286)
* feat: add support for ssl cert in websockt

* improvements

* add: wss in animation

* fix: avoid a race condition between the locator's promise and the expect call

JS starts resolving promises even without the await unless it's a function, this can cause a race in this case

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-16 17:12:47 +05:30
Bijin A B
a9c63e6f2a Revert "Save cookies on redirect response (#6094)" (#6413)
This reverts commit 1b9ea478da.
2025-12-15 20:10:06 +05:30
Abhishek S Lal
014817810d Fix/response pane optimizations (#6395)
* refactor: update content type detection to use base64 decoding

* fix: some styling issues and autofocus issues in input resolved

* refactor: enhance ResponsePane and QueryResult components for improved response handling and size display

* refactor: simplify size display logic in ResponseSize component

* refactor: improve size formatting logic in ResponseSize component for better readability

* refactor: enhance base64 decoding function to handle invalid input and improve error handling
2025-12-15 19:32:57 +05:30
Pragadesh-45
71cf1a8f26 fix: include request URL in prompt variable extraction and add tests (#6412) 2025-12-15 19:13:33 +05:30
naman-bruno
a769ca3ae4 fix: tabs z-index issue (#6411) 2025-12-15 18:22:08 +05:30
naman-bruno
3d61106cc1 fix: rename crash (#6410) 2025-12-15 18:20:36 +05:30
naman-bruno
6cc114100f fix (#6409) 2025-12-15 18:17:11 +05:30
Abhishek S Lal
c11266a96f fix: Improved logic for determining right side expandability of Response Actions (#6398)
* fix: Improved logic for determining right side expandability based on container width and provided width.

* refactor: Standardize naming for right-side expandability a

* refactor: Simplify collection interaction in keyboard shortcuts tests

* refactor: Update right-side expandability logic in ResponsiveTabs and StyledWrapper components
2025-12-15 16:22:32 +05:30
naman-bruno
8b0f41e3cb fix: default workspace error checking (#6379)
* fix: default workspace error checking

* add: tests

* fixes

* fix

* fixes

* fixes

* fix

* fixes

* fix

* chore: close app context in tests

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-15 15:10:53 +05:30
Jeroen Vinke
1b9ea478da Save cookies on redirect response (#6094)
Co-authored-by: Jeroen Vinke <jeroen.vinke@iddinkgroup.com>
2025-12-15 14:08:20 +05:30
sanish chirayath
8cbda5f5cc fix: refactor response examples to use MenuDropdown and Editable components (#6382)
* feat: use common dropdown component

* fix: update example ui to match v3

* fix: test cases, bugs

* fix: review comments

* fix: review comments

* fix: review

* fix: file body/binary table within response examples

* fix: file name, close btn not visible issue

* fix: unnessary transition for three  dots

* fix: install missing deps in bruno-app

* update example url when param is updated

* empty commit

* chore: update package-lock.json

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-14 16:21:06 +05:30
sanish chirayath
2f5537c8db Enhance file watching by ensuring 'node_modules' and '.git' are always ignored (#6391)
* Enhance file watching by ensuring 'node_modules' and '.git' are always ignored

* fix: tests

* rm: dialog assignments

* fix:  duplication
2025-12-12 19:04:52 +05:30
Abhishek S Lal
2327b21c85 Feat/response tabs rewamp (#6388)
* refactor: used common component for layout switching button

* refactor: replace RequestPaneTabs with ResponsiveTabs component across RequestPane and HttpRequestPane

* refactor: simplify ResponsePaneActions component and improve layout handling

* refactor: enhance ResponsePane component with improved tab handling and layout adjustments

* refactor: update layout toggle functionality and button labels in ResponsePane components

* refactor: ensure consistent action button selection in response actions
2025-12-12 17:33:07 +05:30
max-melhuish-depop
6652cca642 Removed filtering of empty strings from url paths when importing from postman collection (#5868)
* removed filtering of empty strings from url paths when importing from postman collection

* revert accidental non-pr changes

* chore: remove console logs

---------

Co-authored-by: Max Melhuish <238188923+max-melhuish-depop@users.noreply.github.com>
Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-12 11:52:29 +05:30
naman-bruno
575f37124c fixes (#6383) 2025-12-11 19:49:27 +05:30
sanish chirayath
50a72a16bc fix: tag persistence tests (#6384) 2025-12-11 19:46:36 +05:30
sanish chirayath
98513c65f0 fix: gRPC oauth2 call is not taking ssl cert and proxy config (#6313)
* fix: grpc oauth2

* fix: review comments

* fix: review comments
2025-12-11 16:06:19 +05:30
sanish chirayath
b61d2212f6 fix: Consistent multipart form handling and @contentType support in examples (#6325)
* fix: multiline multipart items within multipart within response example

* change multiline  editor to single line fot contentType

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-11 01:17:39 +05:30
naman-bruno
1ed957978a Improve tables design (#6330) 2025-12-10 19:39:16 +05:30
naman-bruno
c00cbf6cb2 workspace schema update (#6374) 2025-12-10 19:38:47 +05:30
Abhishek S Lal
632f8705e5 feat: implement sidebar accordion sections (#6373)
* feat: implement sidebar accordion sections

- Added SidebarAccordionContext for managing expanded sections.
- Introduced SidebarContent component to render sections dynamically.
- Created CollectionsSection and ApiSpecsSection for sidebar organization.
- Updated Sidebar component to utilize new sections and context.
- Enhanced StyledWrapper for improved layout and styling of sidebar sections.

* refactor: streamline Sidebar component and enhance styling

* feat: enhance SidebarSection with ActionIcon and improved hover styles

* fix: update useEffect dependencies in SidebarAccordionContext and enhance accessibility in SidebarSection

* style: increase gap in StyledWrapper and reintroduce cursor pointer for better user interaction

* style: remove custom scrollbar styles from Sidebar components for a cleaner look
2025-12-10 19:04:46 +05:30
naman-bruno
f8548225e1 fixes (#6372) 2025-12-10 17:43:35 +05:30
Anoop M D
7fe6b47aa0 chore: updated request tab padding (#6368)
* chore: updated request tab padding
2025-12-10 04:30:58 +05:30
naman-bruno
43f24ad0f1 redesign: workspace overview (#6361)
* redesign: workspace overview

* fixes

* fix: test
2025-12-10 02:56:28 +05:30
Abhishek S Lal
a798b32f25 feat: add response data type selector in response viewer (#6100)
* feat: add response data type selector in response viewer

* chore: fixed lint issue

* test: add test for resonse format change and preview.

* refactor: streamline response format tests with utility functions for navigation and format switching

* refactor: simplify ButtonDropdown component and enhance QueryResultTypeSelector with header and toggle switch

* feat: enhance ButtonDropdown with prefix and suffix props; implement content type detection and update QueryResult for improved format handling

* fix: lint errors resolved

* fix: remove unnecessary blank line to resolve lint issues

* fix: update response format tests

* refactor: remove preview tab locator from response format tests

* fix: update dependency in useEffect to include previewFormatOptions for accurate format handling

* refactor: reorganize imports and enhance QueryResult component for improved format handling and error display

* fix: update error messages in response format preview tests and adjust version in JSON fixture

* feat: add drag detection to HtmlPreview component and update structure for improved user interaction

* refactor: update ResponsePane components for improved structure and functionality;

replace QueryResult with QueryResponse, enhance layout handling, and streamline response actions

* refactor: remove ButtonDropdown component and associated styles;

* refactor: moved ErrorAlert to ui folder

* fix: lint error

* feat: add data-testid attributes to Collection and CollectionItem components for improved testability

* feat: hide dropdown on select in response selector

* fix: update QueryResult component to use detectedContentType for format handling

* test: update ResponseLayoutToggle tests to use data-testid for button selection

* feat: add data-testid attribute to ResponseClear component for improved testability

* refactor: implement clickResponseAction utility for streamlined response action handling in tests

* feat: add data-testid attribute to ResponseCopy component for enhanced testability

* fix: unwanted code in test
2025-12-09 23:45:01 +05:30
Bijin A B
4d1c3f9e52 chore: reduce ux conflicts with toasts in playwright (#6367) 2025-12-09 23:11:16 +05:30
Abhishek S Lal
879d2271b7 fix: update default state for advanced options and change default collection format (#6366) 2025-12-09 22:54:56 +05:30
naman-bruno
cf4c896431 improve: tabs design (#6363)
* improve: tabs design

* fixes: tests
2025-12-09 21:35:27 +05:30
Chirag Chandrashekhar
f6363389d0 Prototype/simplify request creation (#6295)
* feat: add dropdown for quick request creation in tab bar

- Create reusable CreateUntitledRequest component with customizable trigger
- Add generateUniqueRequestName utility for unique request naming
- Replace modal-based request creation with dropdown in tab bar
- Support HTTP, GraphQL, WebSocket, and gRPC request types
- Generate unique names (Untitled, Untitled1, etc.) automatically
- Create requests at collection root level

* Update request creation and collection components

* Fix dropdown positioning and styling when appended to document.body

- Change appendTo from 'parent' to document.body for absolute positioning
- Add comprehensive styling via onShow handler to ensure proper width, padding, text color, and opacity
- Add global styles as fallback for dropdown elements
- Ensure dropdown overlaps parent without expanding it

* Update RequestTabs and Collection components

* Add curl paste detection and parsing for HTTP requests

* Fix generateUniqueRequestName to check filesystem for existing files

* feat: add placeholder text to HTTP request URL input

Add helpful placeholder text 'Enter URL or paste a cURL request' to the HTTP request URL input field. This guides users on how to use the input field, indicating they can either enter a URL directly or paste a cURL command which will be automatically parsed.

* Simplify request creation in collection menu

* fix: fixed issues with cURL paste for GraphQL requests in the URL input bar

* fix: added icons to create request dropdown

* fix: fixed the icon | text gap in dropdown

* fix: removed unnecessary updates on the Dropdown Component

* added onCreate to Dropdown to remove unwanted diffs

* fix: simplified the generateUniqueRequestName function. ai writes complex code

* chore: formatting and removed unnecessary diffs

* Update packages/bruno-app/src/components/RequestPane/QueryUrl/index.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: format

* Fix failing E2E tests by updating to new request creation flow

- Replace #create-new-tab selector with new dropdown flow using createUntitledRequest helper
- Update generateUniqueRequestName to handle .bru, .yml, and .yaml file extensions
- Add createUntitledRequest helper function with optional URL and tag parameters
- Update all failing tests to use the new helper function
- Fix selectors from .collection-item-name to .item-name where needed
- All 13 previously failing tests now pass

* chore: removed unused import

---------

Co-authored-by: Sid <siddharth@usebruno.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-12-09 19:08:52 +05:30
Anoop M D
03e8f2d67d Merge pull request #6303 from bpacholek/feature/windows-on-arm64-support
Feature: Enabled ARM64 build for Windows.
2025-12-09 17:14:17 +05:30
dependabot[bot]
8e855e53bf chore(deps): bump body-parser from 1.20.3 to 2.2.0 (#4383)
* chore(deps): bump body-parser from 1.20.3 to 2.2.0

Bumps [body-parser](https://github.com/expressjs/body-parser) from 1.20.3 to 2.2.0.
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.3...v2.2.0)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: parse raw body for content types not already handled by other parsers

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-09 12:19:06 +05:30
james-ha-bruno
599636d56b update for duplicative entry (#6356) 2025-12-09 12:17:10 +05:30
Anoop M D
9b9534c1eb feat: toolbar design updates (#6354)
* feat: toolbar design updates

* chore: addressed coderabbit review comments

* fix: update unit tests

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-09 12:15:18 +05:30
Abhishek S Lal
0197ae37c8 refactor: update AppTitleBar and SidebarHeader components (#6341)
* refactor: update AppTitleBar and SidebarHeader components to use MenuDropdown and ActionIcon for improved UI consistency

* refactor: update button locators in tests to use data-testid for consistency and improved readability
2025-12-08 22:06:24 +05:30
Dániel Seres
cf969dfcd6 fix: Support @contentType for multiline values (#6217)
* fix: Support @contentType for multiline values

Fixes the issue where the @contentType annotation broke the parsing of multiline values.

* chore: add dotall flag to fileExtractContentType

Not strictly needed since body:file uses single-line values in practice,
but doesn't hurt and matches what multipartExtractContentType does.

---------

Co-authored-by: Márk Dániel Seres <markdaniel.seres@tesco.com>
2025-12-08 18:39:25 +05:30
Pragadesh-45
a66be21523 feat: Enhance runCollectionFolder to support selected request ordering (#6320)
* Refactor `runCollectionFolder` action to accept `selectedRequestUids` for filtering and ordering requests.
* Update IPC handler to process `selectedRequestUids`, ensuring requests are executed in the specified order while preserving folder data.
2025-12-08 17:16:24 +05:30
naman-bruno
4016754d71 feat: integrate import/export modals and refactor environment handling (#6346) 2025-12-08 15:17:53 +05:30
Anoop M D
f3aebf6374 Merge pull request #6345 from usebruno/feat/design-updates
feat: design updates
2025-12-08 14:42:06 +05:30
naman-bruno
f87460b00e refactor: simplify last opened workspaces management by removing workspace config from storage and improving path handling (#6343) 2025-12-08 14:30:16 +05:30
naman-bruno
354e8d7496 feat: add hideApiSpecPage dispatch to Collection and CollectionItem components (#6344) 2025-12-08 14:24:47 +05:30
Anoop M D
dc107f8b96 init (#6337) 2025-12-08 01:37:16 +05:30
naman-bruno
cd0f1e45ba init 2025-12-07 21:53:47 +05:30
Bijin A B
33022843f2 fix: CWE-347: Improper Verification of Cryptographic Signature (#6336) 2025-12-07 14:16:39 +05:30
Anoop M D
facdf3264a feat: changes to incorporate oc schema updates (#6335)
* feat: changes to incorporate oc schema updates
* chore: fixed oc types resolution issue
2025-12-07 06:05:48 +05:30
naman-bruno
4ffb447c53 fix: path for newly added collection & remove option for outside collections (#6331)
* fixes

* fixes

* fix
2025-12-06 18:43:53 +05:30
Sanjai Kumar
3e5ae613f5 feat: Increase visibility of text in Request tabs (#6243)
* refactor(RequestTabs): update tab width calculation and improve styling

* refactor: replace close icon implementation with GradientCloseButton and adjust styles

* changes: design

* fix: failing tests

* fixes

* fixes: coderabbit

* fixes

* fixes

* gradient color fix

---------

Co-authored-by: naman-bruno <naman@usebruno.com>
Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-12-06 18:42:57 +05:30
naman-bruno
42bef4ae1e fix: traffic light styling on light mode (#6333)
* fix

* fixes
2025-12-06 18:16:37 +05:30
naman-bruno
e93e545b81 improve: tests (#6321)
* improve: tests

* fixes

* fixes
2025-12-06 15:36:58 +05:30
Abhishek S Lal
4a8d787f31 feat: Moved Workspace Selector to the Titlebar of the window. (#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>
2025-12-06 02:07:05 +05:30
Bijin A B
f5211f6a08 Update quotes rule for string in CODING_STANDARDS.md (#6327) 2025-12-06 02:01:03 +05:30
Sanjai Kumar
57222d2500 feat: enhance collection settings with environment modals (#6242)
* feat: enhance collection settings with environment modals

* refactor: remove unused environment modal state and simplify Info component structure
2025-12-05 19:20:04 +05:30
Sanjai Kumar
f479e0d325 refactor: Rename runtime to runDuration (#6323)
* refactor: Rename 'runtime' to 'runDuration'

* revert changes made in report.html
2025-12-05 19:17:06 +05:30
naman-bruno
5302addda0 fix: clone collection (#6322)
* fix: clone collection
2025-12-05 17:26:06 +05:30
Sanjai Kumar
80b017f224 feat: Include pre-request and post-response tests in JUnit reports (#6284)
* enhance: JUnit output to include preRequest and postResponse test results

* fix: lint
2025-12-05 12:04:32 +05:30
Bijin A B
b18d582004 Merge pull request #6310 from sanjaikumar-bruno/chore/eslint-ignore-paths
chore: update ESLint configuration to ignore additional directories
2025-12-04 19:23:25 +05:30
Bijin A B
109394c65b Merge pull request #6308 from Pragadesh-45/fix/6254
feat: Streamline gRPC requests to use right context
2025-12-04 18:58:10 +05:30
Sid
c355153f26 Revert: Re-add post response vars (#6307)
* Partial Revert "remove: presets and response var (#6195)"

This reverts commit 786a3414b8 while keeping code related to presets deleted

* revert: remove global environment variables assignment
2025-12-04 18:04:47 +05:30
Pragadesh-45
b87a02beb3 feat: Streamline gRPC requests to use right context 2025-12-04 18:00:32 +05:45
sanjai
4624ffb116 chore: update ESLint configuration to ignore additional directories 2025-12-04 16:34:48 +05:30
Sid
a9ce97fb1b fix: update content security policy to remove unsafe-inline (#6305) 2025-12-04 12:40:52 +05:30
Pooja
72ce6cadeb fix: request and response pane height (#6294) 2025-12-04 12:31:57 +05:30
Bijin A B
c4ff2918a2 Merge pull request #6080 from dssagar93/feature/auto-scroll-on-tab-change
Auto scroll to show this item when its tab becomes active
2025-12-04 10:05:32 +05:30
Bijin A B
9972eb3de6 Merge branch 'main' of github.com:usebruno/bruno into feature/auto-scroll-on-tab-change 2025-12-04 05:15:39 +05:30
naman-bruno
ebe0203415 init: workspaces (#6264)
* init: workspaces
2025-12-04 04:56:43 +05:30
IDCT Bartosz Pachołek
b3ef91fe8e Enabled ARM64 build for Windows. 2025-12-03 23:57:15 +01:00
Bijin A B
6786f19d04 Merge pull request #6300 from usebruno/chore/oss-lint-fixes
chore: repo-wide lint fixes
2025-12-04 03:40:29 +05:30
Bijin A B
05fe8b1b27 chore: repo-wide lint fixes 2025-12-04 01:37:20 +05:30
sanish chirayath
b5722bf11c fix: wrap script in async IIFE to create isolated scope (#6229)
* fix: lexical scope for running scripts

* review fixes
2025-12-04 00:23:06 +05:30
Siddharth Gelera (reaper)
2b8da39bcf fix: update content security policy to allow inline scripts (#6139) 2025-12-03 19:57:10 +05:30
Pragadesh-45
38ba53be9f fix: stringify rawValue in brunoVarInfo (#6281) 2025-12-03 18:27:12 +05:30
Chirag Chandrashekhar
9159f523d9 Inbuilt Terminal (#6066)
* add: terminal

* added support for multiple terminal sessions and opening terminal from collection's working directory

* Use PowerShell as default shell on Windows

Replace cmd.exe with powershell.exe for terminal sessions on Windows.
Falls back to PWSH environment variable if set (for PowerShell Core).

* refactor(ui): improved session list by moving path display to hover for cleaner view

* chore: format

* refactor: improve terminal code quality and UI consistency

- Add terminal icon to 'Open in Terminal' dropdown item in CollectionItem
- Remove unused imports and functions (IconPlus, callIpc, canWriteToTerminal)
- Fix React key prop placement in SessionList component
- Replace deprecated substr with substring in terminal session ID generation
- Improve error handling for terminal cleanup on app quit
- Simplify terminal cleanup logic in window close handler

---------

Co-authored-by: naman-bruno <naman@usebruno.com>
Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-03 17:48:28 +05:30
Abhishek S Lal
a3d2d35d2e feat: introduce REQUEST_TYPES constant and update item deletion logic (#6244)
- Added REQUEST_TYPES constant to centralize request type definitions.
- Updated deleteItem action to filter items based on REQUEST_TYPES and folders.
- Modified collection.js to handle REQUEST_TYPES during file resequence operations.
2025-12-03 16:07:38 +05:30
Chirag Chandrashekhar
9caef9e573 Fix: WS variable interpolation (#6184)
* feat: add variable interpolation support for WebSocket requests

- Add WebSocket body interpolation in interpolateVars function
- Interpolate URL, headers, and all messages in request.body.ws array with full variable context
- Refactor sendWsRequest to use main process preparation (removes duplication)
- Add mode property to wsRequest object for proper request type detection
- Ensure consistent variable precedence matching HTTP/gRPC requests
- Centralize all interpolation logic in main process via prepareWsRequest

* Add Playwright tests for WebSocket variable interpolation

- Add tests for URL interpolation (wss://echo.{{url}}.org)
- Add tests for message content interpolation ({"test": "{{data}}"})
- Update test fixtures to use wss://echo.websocket.org echo server
- Add WEBSOCKET_FLOWS.md documentation
- Refactor queueWsMessage to handle variable interpolation in main process

* removed ws flow documentation

* chore: updated the network/index.js file to reduce merge conflicts by moving around code

* fix: added collection and item to WsQueryUrl Editor to fix available variable highlight

* chore: remove unnecessary whitespace in WebSocket event handlers

* feat: add automatic WebSocket reconnection on URL variable changes

- Detect changes to interpolated URL (including variable changes)
- Automatically disconnect and reconnect when interpolated URL changes
- Add debouncing (400ms) to prevent excessive reconnections
- Track previous interpolated URL to avoid unnecessary reconnects
- Store interpolated URL when connection becomes active
- Improve error handling and cleanup

* chore: removing diff

* Add WebSocket connection status IPC method

- Add connectionStatus() method to WsClient that returns detailed status
  ('disconnected', 'connecting', 'connected') instead of boolean
- Add renderer:ws:connection-status IPC handler in electron layer
- Add getWsConnectionStatus() utility function in network utils
- Provides more granular connection state information for UI components

* refactor: improve WebSocket connection status tracking in WsQueryUrl

- Replace boolean isConnectionActive with connectionStatus state ('disconnected', 'connecting', 'connected')
- Add useWsConnectionStatus hook to poll connection status every 2 seconds
- Refactor connection handlers: handleConnect, handleDisconnect, handleReconnect
- Update to use getWsConnectionStatus instead of isWsConnectionActive for more granular status
- Improve reconnect logic to handle URL variable interpolation changes
- Add proper connection status indicators in UI (connecting state with pulse animation)

* fix: improve WebSocket URL handling and request initialization

- Fix WebSocket URL state management by reading directly from item instead of local state
- Add handleUrlChange function to properly dispatch URL changes
- Fix interpolated URL change detection logic in useEffect
- Initialize params array for new WebSocket requests to prevent undefined errors
- Ensure params array is initialized when URL changes in draft/request
- Remove console.log statements and unused imports
- Update persistence test replacement URL to avoid port conflicts

These changes ensure WebSocket requests properly handle URL changes and
maintain consistent state between draft and saved requests.

* feat: refactor WebSocket connection status handling

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-03 15:52:52 +05:30
Siddharth Gelera (reaper)
893058067d chore: improve coderabbit review instructions (#6282)
* chore: add more review items

* Update CODING_STANDARDS.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Sid <siddharth@usebruno.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-03 13:39:24 +05:30
Anoop M D
4a38f2d49f Update PR template to consent for AI usage 2025-12-03 03:49:02 +05:30
Tarunkumar
d56e4f625b fixed min width issue so two windows can be used in parallel (#5863)
* fixed min width issue so two windows can be used in parallel

* updated the min height as well, so that Bruno can be viewed in quarter window on screen

---------

Co-authored-by: Tarunkumar N Bagmar <tarunkumar@Tarunkumars-MacBook-Air.local>
Co-authored-by: Anoop M D <anoop@usebruno.com>
2025-12-02 21:39:03 +05:30
Sid
9bbcf7ecbe fix:match to full string (#6272) 2025-12-02 17:54:53 +05:30
Chirag Chandrashekhar
ee4c923bc5 Merge pull request #6117 from chirag-bruno/bugfix/postman-export-omit-collection-vars
fix: Exporting Bruno collection as Postman collection omits collection variables
2025-12-02 16:47:15 +05:30
naman-bruno
bc82536a82 fix: ws and grpc request not saving (#6267) 2025-12-02 15:54:54 +05:30
Sid
b95ef99ef2 Merge pull request #6266 from usebruno/feat/copy-response-to-clipboard-5409
added copy button to copy response (#5409)
2025-12-02 15:47:18 +05:30
Pooja
2a251b1a62 added copy button to copy response (#6131)
* added copy button to copy response

* add: response action component

* fix: lint error

* add: playwright test

---------

Co-authored-by: Shashank Shekhar <48152748+sha5-git@users.noreply.github.com>
Co-authored-by: Sid <siddharth@usebruno.com>
2025-12-02 15:33:01 +05:30
Pooja
06a024a1d9 feat: add copy paste feature for folder (#6097)
* feat: add copy paste feature for folder

* add: playwright test

* fix

* fix

* add: keyboardFocusBg in light mode

* fix: copy paste in yaml collection

* improvement
2025-12-02 15:29:32 +05:30
Shashank Shekhar
8cee7bad39 added copy button to copy response (#5409)
Co-authored-by: Shashank Shekhar <48152748+sha5-git@users.noreply.github.com>
2025-12-02 14:53:51 +05:30
Chirag Chandrashekhar
bc4062b950 Bugfix/inaccurate process metrics (#6257) 2025-12-02 14:40:20 +05:30
Sid
b3ffc904ad feature/autoSave (#582) (#6211) 2025-12-02 14:32:34 +05:30
Sid
af707de684 fix(autoSave): minor bug fixes 2025-12-02 14:09:19 +05:30
Sid
be94224cfd Merge branch 'main' into feat/autosave-internal-582 2025-12-02 13:53:46 +05:30
Pooja
786a3414b8 remove: presets and response var (#6195) 2025-12-02 13:53:04 +05:30
Sid
7de56bd85c Merge branch 'main' into feat/autosave-internal-582 2025-12-02 13:52:12 +05:30
Pooja
4ce5debc4c feature/autoSave (#582) (#6225) 2025-12-02 13:46:06 +05:30
Pooja
8716e2b2a6 change: default cli sandbox to safe (#6198)
* change: default cli sandbox to safe
2025-12-02 13:09:16 +05:30
Sanjai Kumar
9d6486ba3e fix: update OAuth2 authorization header encoding to remove unnecessary URI encoding (#6263) 2025-12-02 12:56:08 +05:30
tlaloc911
dd72ee5d77 Add package directories to .gitignore (fix #6249) (#6250) 2025-12-01 09:23:44 +05:30
dependabot[bot]
7f204a8769 chore(deps): bump actions/upload-artifact from 4 to 5 (#5912)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-29 00:21:24 +05:30
dependabot[bot]
32990db3fb chore(deps): bump actions/checkout from 5 to 6 (#6192)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-29 00:18:20 +05:30
lohit
4f8d2c0c67 feat: default developer mode to nodevm and remove vm2 (#6187) 2025-11-29 00:04:55 +05:30
Anoop M D
8c06a229e9 Merge pull request #6238 from usebruno/chore/add-codemirror-placeholder
chore: added codemirror placeholder
2025-11-28 06:07:18 +05:30
Anoop M D
f367ea5a89 chore: added codemirror placeholder 2025-11-28 06:06:22 +05:30
Anoop M D
55a6af1ce3 Merge pull request #6236 from naman-bruno/bugfix/dropdown
rm: api spec from collection dropdown
2025-11-28 05:59:23 +05:30
Anoop M D
e9efcb48ac Merge pull request #6237 from usebruno/feat/design-revamp
feat: design revamp
2025-11-28 05:39:14 +05:30
Anoop M D
fa94efaa24 feat: design revamp 2025-11-28 05:10:29 +05:30
naman-bruno
1b2df9fba4 fix: dropdown 2025-11-28 02:09:48 +05:30
naman-bruno
7ee366eb81 Redesign dropdowns (#6235)
* redesign dropdowns

* fix: colors

---------

Co-authored-by: Anoop M D <anoop@usebruno.com>
2025-11-27 22:20:51 +05:30
Abhishek S Lal
59514127d5 Merge pull request #6171 from abhishek-bruno/style/update-font-size
style: updated font size to 13px using theme props.
2025-11-27 22:19:35 +05:30
Sanjai Kumar
9d98eb86c4 refactor: update deprecation messages for Presets and Post Response Vars (#6230)
* refactor: update DeprecationWarning component to accept children and enhance deprecation messages for Presets and Post Response Vars

* refactor: update DeprecationWarning component to use props for feature names and links, enhancing deprecation messages across various components
2025-11-27 21:57:39 +05:30
Sanjai Kumar
bb0096eb38 feat: added multipart data formatting in timeline (#6185)
refactor: remove escapeHeaderValue function and enhance formatMultipartData utility
2025-11-27 18:12:20 +05:30
Sanjai Kumar
6e88671788 feat: add support for legacy request object translations in Postman converter (#6174) 2025-11-27 10:42:06 +05:30
naman-bruno
d17048f80c feat(opencollection): add YAML-based collection support (#6155)
* add: opencollection

---------

Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
2025-11-27 00:55:08 +05:30
Sanjai Kumar
172479edad feat: Add deprecation warnings for Presets and Post Response Vars (#6212)
* feat: add deprecation warnings for presets and post response vars

* refactor: update deprecation warnings for presets and post response vars to include version
2025-11-26 21:14:50 +05:30
sanish chirayath
486b91894c fix: fetching reflection adds draft state in gRPC (#6218) 2025-11-26 19:51:34 +05:30
sanish chirayath
ca8ef36f9f fix: grpc messages vanishes after save if the body contains variables (#6216) 2025-11-26 18:50:10 +05:30
Chirag Chandrashekhar
7ed474c8ba fix: add Error constructors to NodeVM context for jsonwebtoken tests (#6209)
When jsonwebtoken throws errors inside the NodeVM context, those errors
were instances of the VM's isolated Error class, which caused
instanceOf(Error) checks in tests to fail.

By adding Error constructors (Error, TypeError, ReferenceError,
SyntaxError, RangeError) from the global scope to the scriptContext,
errors thrown by jsonwebtoken and other modules now use the same Error
class that tests check against, ensuring instanceOf checks pass correctly.

This fixes jsonwebtoken test failures when using the NodeVM runtime.
2025-11-26 17:57:11 +05:30
srikary12
086d0d98ef feature/autoSave (#582) 2025-11-26 12:17:52 +05:30
Pooja
b0405b1e1a fix: variable name validation in brunoVarInfo (#6203)
* fix: variable name validation in brunoVarInfo
2025-11-26 00:50:57 +05:30
Bijin A B
c2d000e805 fix: disallow prompts with leading or trailing spaces (#6201) 2025-11-25 16:53:05 +05:30
sanish chirayath
6aaccabc04 fix: openapi import fails when requestbody content is empty (#6200) 2025-11-25 16:19:57 +05:30
Sid
daf23c9e2d feat: add coding standards documentation (#6141) 2025-11-25 13:30:26 +05:30
Pooja
f952688032 improve: add var functions (#6175)
* improve: add var functions

* Update packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js

---------

Co-authored-by: Bijin A B <bijin@usebruno.com>
2025-11-21 21:12:06 +05:30
Bijin A B
f429fa94e3 fix(security): prototype pollution vulnerability in js-yaml (#6168) 2025-11-21 17:42:31 +05:30
Pooja
fb420fcea4 fix: preserve draft state when creating variables via varInfoToolbar (#6152)
* fix: preserve draft state when creating variables via varInfoToolbar
2025-11-21 15:47:23 +05:30
Anoop M D
cc3d6a961a Merge pull request #6169 from pooja-bruno/fix/reduce-font-size-of-tabs-text
fix: reduce font size of tab test
2025-11-21 13:41:31 +05:30
pooja-bruno
27c37192b2 fix: reduce font size of tab test 2025-11-21 12:59:27 +05:30
Bijin A B
faa2ef5de2 Merge pull request #6162 from bijin-bruno/fix/bruno-to-postman-converter
fix: sync bruno to postman converter with enterprise edition
2025-11-20 19:13:33 +05:30
Sanjai Kumar
c05d56fd21 Improve "Close All Collections" community PR (#5994)
* refactor: move CollectionsBadge to a dedicaced folder

Co-authored-by: Jérémy Munsch <github@jeremydev.ovh>
2025-11-19 22:48:30 +05:30
Pragadesh-45
b4d19ab8ca fix: push event only if exec has content (#6121) 2025-11-19 12:09:18 +05:30
Siddharth Gelera (reaper)
0cedf48e68 feat: encapsulate tab boundaries into a hook for managing pane dimensions (#5878)
* feat: implement useTabPaneBoundaries hook for managing pane dimensions

* fix: replace hardcoded divisor with constant in useTabPaneBoundaries

* chore: un-needed event calls

* fix: remove redundant import of sendRequest

* update main rediff
2025-11-19 11:30:39 +05:30
Pooja
4e7bc1a351 fix: prevent import failure for Postman collections with missing response headers (#6129) 2025-11-19 07:53:18 +05:30
Sid
9d3c8b2401 feat: Allow ctrl/cmd + click to open URLs present in codemirror (#5930)
* feat: Allow ctrl/cmd + click to open URLs present in codemirror editors (#5160)

* Allow ctrl/cmd + click to open URLs

* fix for when user does cmd+tab, then comes back without it

---------

Co-authored-by: Sid <siddharth@usebruno.com>

* Feature/cmd click on links (#5927)

fix: clean up whitespace and formatting in linkAware functions

fix rediff

Feature/cmd click on links (#6132)

* Allow ctrl/cmd + click to open URLs

* fix for when user does cmd+tab, then comes back without it

* refactored the community contribution to match Autocomplete's implementation

* updated the code to resolve issues caused during merge conflict resolution with the use of makeLinkAware

* fix: updated the code to use lodash's debounce and removed redundant undefined checks

* fix: correct debouncing test expectation in linkAware.spec.js

The test was incorrectly expecting 3 setTimeout calls when debouncing
should only result in one active timeout. Updated the test to verify
debouncing behavior correctly by checking that setTimeout is called
with the correct delay, and that only one execution happens after
the debounce delay.

* fix: fixed merge issues in linkAware.js

* fix: fixed CodeMirror assignment to this.editor

* fix: formatting fixes

* fix: formatting fix

---------

Co-authored-by: abansal21 <37215457+abansal21@users.noreply.github.com>
Co-authored-by: Chirag Chandrashekhar <chirag@usebruno.com>

---------

Co-authored-by: Arun Bansal <37215457+abansal21@users.noreply.github.com>
Co-authored-by: Chirag Chandrashekhar <chirag@usebruno.com>
2025-11-18 17:56:37 +05:30
Sid
39dfd8d360 Feature/cmd click on links (#5927)
fix: clean up whitespace and formatting in linkAware functions

fix rediff

Feature/cmd click on links (#6132)

* Allow ctrl/cmd + click to open URLs

* fix for when user does cmd+tab, then comes back without it

* refactored the community contribution to match Autocomplete's implementation

* updated the code to resolve issues caused during merge conflict resolution with the use of makeLinkAware

* fix: updated the code to use lodash's debounce and removed redundant undefined checks

* fix: correct debouncing test expectation in linkAware.spec.js

The test was incorrectly expecting 3 setTimeout calls when debouncing
should only result in one active timeout. Updated the test to verify
debouncing behavior correctly by checking that setTimeout is called
with the correct delay, and that only one execution happens after
the debounce delay.

* fix: fixed merge issues in linkAware.js

* fix: fixed CodeMirror assignment to this.editor

* fix: formatting fixes

* fix: formatting fix

---------

Co-authored-by: abansal21 <37215457+abansal21@users.noreply.github.com>
Co-authored-by: Chirag Chandrashekhar <chirag@usebruno.com>
2025-11-18 17:44:24 +05:30
Arun Bansal
460832f3ed feat: Allow ctrl/cmd + click to open URLs present in codemirror editors (#5160)
* Allow ctrl/cmd + click to open URLs

* fix for when user does cmd+tab, then comes back without it

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-11-18 17:43:56 +05:30
Sanjai Kumar
50442d960d feat: enhance HTML report generation by including environment name (#6055) 2025-11-18 16:09:57 +05:30
Abhishek S Lal
2ac41806a2 fix: update result structure to use 'name' instead of 'suitename' in JUnit output (#6120)
* fix: update result structure to use 'name' instead of 'suitename' in JUnit output
2025-11-18 12:31:41 +05:30
Bijin A B
e9111c0529 Merge pull request #6104 from bijin-bruno/feature/prompt-vars-extended 2025-11-17 22:02:11 +05:30
Bijin Bruno
48a09f6f50 feat: enhance support for prompt variables 2025-11-17 20:12:20 +05:30
Bijin A B
e613e4cbcd Merge pull request #5975 from abhishek-bruno/fix/reorder-item-when-deleting-v2
Refactor: Enhance Request Item sequencing
2025-11-17 16:19:00 +05:30
Pooja
4631eda281 Merge pull request #6069 from pooja-bruno/feat/add-edit-variable-in-place
feat: edit variable in place
2025-11-17 16:13:09 +05:30
Abhishek S Lal
3f7ab31b2b refactor: enhance deleteItem action to handle item reordering after deletion 2025-11-17 16:05:33 +05:30
Bijin A B
27a7b623c7 Merge pull request #6039 from sanish-bruno/feat/openapi-examples
fix: import multiple types of example formats from openapi
2025-11-17 14:12:35 +05:30
sanish-bruno
95bc670d8c fix: regex 2025-11-17 13:53:24 +05:30
sanish-bruno
6d8f428140 refactor 2025-11-17 13:53:24 +05:30
sanish-bruno
ed18cb6d90 fix: improve logic for and tests 2025-11-17 13:53:24 +05:30
sanish-bruno
bb83fbfb9d fix: add schema based example 2025-11-17 13:53:24 +05:30
Sid
ddfdeda4d6 Merge pull request #6074 from usebruno/feature/http-stream-internal
Feature: HTTP Streaming
2025-11-17 13:44:53 +05:30
skewnart
adb0b90457 fix: reorder request and directory when deleting item 2025-11-17 13:40:13 +05:30
Pooja
8c7888533a feat: support newlines in headers, params, and variables (#5795)
* feat: support newlines in headers, params, and variables

* add: collectin unit test

* fix: assertion and additional header multiline

* fix: assert

* rm: useEffect for header validation

* rm: comments

* fix: already encoded url

* rm: new line changes

* handle new line in url

* fix: lint error

* add: unit test for multi line test

* change: unit test

* mv: functions in util

* fix: drag icon position

* improve: arrow height

* improvements

* rm: getKeyString from assert

* fix: single line editor

* fix: import MultiLineEditor

* import getKeyString and getValueUrl

* add: getTableCell in utils

* rm: multiline key logic

* fix

* mv: getTableCell in locators.ts
2025-11-17 13:27:00 +05:30
Sid
0a188575a0 fix: update request cancel icon 2025-11-17 13:14:09 +05:30
Chirag Chandrashekhar
2be602d16c Feature/prompt save before collection close (#6062)
* added confirmation dialog before collection close for items in draft state

* chore: lint fix

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-11-17 12:09:12 +05:30
Chirag Chandrashekhar
8ec1925b9f feat: add variable interpolation support for WebSocket requests (#6064)
* feat: add variable interpolation support for WebSocket requests

- Add WebSocket body interpolation in interpolateVars function
- Interpolate URL, headers, and all messages in request.body.ws array with full variable context
- Refactor sendWsRequest to use main process preparation (removes duplication)
- Add mode property to wsRequest object for proper request type detection
- Ensure consistent variable precedence matching HTTP/gRPC requests
- Centralize all interpolation logic in main process via prepareWsRequest

* Add Playwright tests for WebSocket variable interpolation

- Add tests for URL interpolation (wss://echo.{{url}}.org)
- Add tests for message content interpolation ({"test": "{{data}}"})
- Update test fixtures to use wss://echo.websocket.org echo server
- Add WEBSOCKET_FLOWS.md documentation
- Refactor queueWsMessage to handle variable interpolation in main process

* removed ws flow documentation

* chore: updated the network/index.js file to reduce merge conflicts by moving around code

* fix: added collection and item to WsQueryUrl Editor to fix available variable highlight

* chore: remove unnecessary whitespace in WebSocket event handlers

---------

Co-authored-by: Sid <siddharth@usebruno.com>
2025-11-17 12:02:25 +05:30
Bobby Bonestell
d28f2f32e9 feat: add support for prompt variables in the bruno app 2025-11-14 18:57:45 +05:30
Sid
76a1532695 Merge branch 'main' into feature/http-stream-internal 2025-11-14 17:01:09 +05:30
Siddharth Gelera (reaper)
efad149afc HTTP stream enhancements (#6077)
* feat: add stop request button in api url bar

* docs: add farsi translation

* fix: handle escaped forward slashes by fast-json-format library upgrade

* refactor: change ui to use one from Websockets

* chore: cleanup

* fix: lint issues

* Replace IconPlayerStop with IconSquareRoundedX

* update json request and response formatting logic

* chore: format changes

* chore: remove un-needed diffs

* chore: sanitize

* bugfix(#5939): curl import fails for custom content-types

* chore: remove un-needed diffs

* chore: enhance response handling for streaming

* fix: disable requestid check for tests and assertions to be updated after streaming result

* chore: housekeeping

* fix: streamline loading and cancel request icon logic

* chore: formatting

* fix: multiple co-pilot changes

* fix: handle in folders

* feat: add WaitGroup utility for managing concurrent tasks

* refactor: remove WaitGroup utility and clean up network IPC logic

* refactor: remove unused setTimeout import and clean up post script execution

* refactor: clean up post-response script execution logic

* undiff

* re-align

* refactor: streamline post-response script execution

- Cleaned up formatting and improved readability of the post-response script execution logic.
- Consolidated parameters in function calls for consistency.

* fix: keep original dataBuffer for saving response

---------

Co-authored-by: adarshajit <adarshajit@gmail.com>
Co-authored-by: sajadoncode <sajadoncode@gmail.com>
Co-authored-by: lohit-bruno <lohit@usebruno.com>
Co-authored-by: Bijin A B <bijin@usebruno.com>
Co-authored-by: Pragadesh-45 <temporaryg7904@gmail.com>
Co-authored-by: Anoop M D <anoop@usebruno.com>
Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
Co-authored-by: Dawid Góra <dawidgora@icloud.com>
2025-11-14 16:57:29 +05:30
Sid
2d2a17c90f Merge pull request #6070 from usebruno/feature/collection-test-results-and-filtering-internal
Feature/collection test results and filtering internal
2025-11-14 13:46:32 +05:30
Sid
3d8d93f20d fix: add missing newline at end of file in RunnerResults component 2025-11-14 13:19:39 +05:30
Bijin A B
94c33e6833 Merge pull request #5653 from sanish-bruno/feat/support-grpc-v1-reflection
feat: support v1 reflection for grpc server reflection
2025-11-13 20:12:26 +05:30
SAGAR KHATRI
f7ea1f8dbb Added semi colon 2025-11-13 19:31:21 +05:30
sanish-bruno
2ef451c80b rm: adding metadata to credentials 2025-11-13 19:04:34 +05:30
sanish-bruno
044fcce49f fix 2025-11-13 19:04:34 +05:30
sanish-bruno
dffb600dab fix: set metadata 2025-11-13 19:04:34 +05:30
sanish-bruno
99478b7068 fix: add metadata for insecure methods calls 2025-11-13 19:04:34 +05:30
sanish-bruno
252fd386b7 add: metadata to creds 2025-11-13 19:04:33 +05:30
sanish-bruno
b982f6db16 refactor: replace grpc-reflection-js with grpc-js-reflection-client in grpc-client implementation
rm: comment

fix: type generation

feat: implement reflection client support for gRPC v1 and v1alpha in grpc-client

refactor: simplify reflection client handling in grpc-client by removing service list retrieval

refactor: enhance reflection client return structure in grpc-client to include service list

fix: lint
2025-11-13 19:04:33 +05:30
Bijin A B
3b4e5686b8 Merge pull request #5800 from sanish-bruno/add/grpc-make-request-tests
add: tests for grpc requests
2025-11-13 18:36:11 +05:30
Bijin Bruno
2ef1a1948b chore: minor folder structure refactor 2025-11-13 18:23:34 +05:30
sanish-bruno
f2273821b0 add: tests for grpc requests
feat: add common selectors to locator.ts

fix: add dataTestId prop

update locator
2025-11-13 18:10:30 +05:30
Bijin A B
8a22f6acb8 Merge pull request #6083 from dawidgora/bugfix/5939_curl-import-fails-for-custom-content-types
bugfix(#5939): curl import fails for custom content-types
2025-11-13 17:26:49 +05:30
Chirag Chandrashekhar
6049530634 refactor: update runner tests to use new filter implementation and reusable helpers (#6085) 2025-11-13 15:56:33 +05:30
Dawid Góra
5784b04129 bugfix(#5939): curl import fails for custom content-types 2025-11-13 08:50:29 +01:00
Anoop M D
fec37f43e0 Merge pull request #5993 from adarshajit/feature/stop-request-button-in-api-url-bar
feat: add stop request button in api url bar
2025-11-13 12:14:48 +05:30
SAGAR KHATRI
cf19035b0b Merge branch 'usebruno:main' into feature/auto-scroll-on-tab-change 2025-11-12 23:53:05 +05:30
Bijin A B
b8fef7b796 Merge pull request #6079 from lohit-bruno/json_response_formatting
fix: update json request and response formatting logic
2025-11-12 23:12:40 +05:30
lohit-bruno
04f8dba1b1 update json request and response formatting logic 2025-11-12 22:47:35 +05:30
SAGAR KHATRI
d9a3f74cb7 Auto scroll to show this item when its tab becomes active 2025-11-12 20:50:44 +05:30
Anoop M D
cd1500bd01 Replace IconPlayerStop with IconSquareRoundedX 2025-11-12 19:48:09 +05:30
Anoop M D
e8a8b5d220 Merge pull request #6027 from sajadoncode/feature/add-farsi-readme
docs: add farsi translation
2025-11-12 19:04:11 +05:30
Pragadesh-45
bc3dfc59f6 fix: lint issues 2025-11-12 18:35:14 +05:45
Bijin A B
2c399ca33c Merge pull request #6075 from lohit-bruno/upgrade_fast_json_format_library
fix: handle `escaped forward slashes` by `fast-json-format` library upgrade
2025-11-12 18:06:09 +05:30
lohit-bruno
ccac4d6112 fix: handle escaped forward slashes by fast-json-format library upgrade 2025-11-12 16:38:35 +05:30
DaviXavier
fc5093eab4 fix: #1884 - Fixes infinite loading issue for text/event-stream requests (#4472)
* #1884 - Add support for text/event-stream content-type

* #1884 - Fix bugs with streaming

Fix bug when streaming response is not ok
Fix bug when clearing response of streaming request
Show text signaling that the response is being streamed in the reponse status
Update response size when new data is streamed in

* #1884 - Fix multiple requests when spamming send button

* #1884 - Add time counter for streamed response and fix final time

* #1884 - Run post script only at end of streamed request

* #1884 - add support for automatic "upgrade" to streaming data

* #1884 - adjustments for stopwatch in stream implementation and remove unused imports

* #1884 - fix imports indentation in useIpcEvents.js

* #1884 - remove stream data ended export function from collections

---------

Co-authored-by: Siddharth Gelera <ahoy@barelyhuman.dev>
2025-11-12 15:56:26 +05:30
Bijin A B
631b05330d Merge pull request #6071 from barelyhuman/fix/restore-duplicate-hasher
test: Add test for restoring duplicate hashes in patternHasher
2025-11-12 15:45:17 +05:30
reaper
be34c86c47 fix: replace regex with replaceAll for secure string replace 2025-11-12 15:12:51 +05:30
reaper
67c9f1373e test: Add test for restoring duplicate hashes in patternHasher 2025-11-12 14:40:30 +05:30
Sid
6628f95677 fix: add missing newline at end of file in RunnerResults component 2025-11-12 14:15:06 +05:30
Chirag Chandrashekhar
44ed0b01d8 Test Runner UI Revamp (#6011)
* Moved collection results to runner title bar so they are move visible.
Added breakdown of test results within collection.
Added filtering based on passing/failing requests and tests by click on results text.

* feat: revamp Test Runner UI with unified filter and improved layout

- Add unified filter bar (All/Passed/Failed/Skipped) with counts and active indicator
- Implement filtering that filters both requests and tests within requests
- Move action buttons to top bar, prevent filter wrapping
- Add close button and placeholder to response view
- Update styling for light/dark modes with proper colors and typography

* refactored the RunnerResults component to be more clear and readable

* refactor: revert formatting changes while preserving new UI and filtering logic

- Restore original function formatting with return statements and braces
- Restore removed input attributes (autoCorrect, autoCapitalize, spellCheck)
- Revert ternary operator changes to match original code style
- Restore original variable names (savedConfiguration) and comments
- Restore original test results rendering structure
- Preserve new filter bar UI, filtering logic, and response view improvements

* fix: implement smart auto-scroll behavior in test runner

- Only auto-scroll when user is near the bottom (within 100px)
- Preserve user's scroll position if they've scrolled up to view content
- Move ref to actual scrollable container for proper scroll detection

* Update RunnerResults component

* chore: reformat

---------

Co-authored-by: Morgan English <morgan.english@canterbury.ac.nz>
Co-authored-by: Sid <siddharth@usebruno.com>
2025-11-12 14:10:36 +05:30
morgan-se
45cfbc5c49 Moved collection results to runner title bar so they are move visible. (#3808)
Added breakdown of test results within collection.
Added filtering based on passing/failing requests and tests by click on results text.

Co-authored-by: Morgan English <morgan.english@canterbury.ac.nz>
Co-authored-by: Sid <siddharth@usebruno.com>
2025-11-12 13:48:31 +05:30
Pooja
14bece8696 feat: Add tabs component for pre-request and post-response scripts (#5926) 2025-11-12 12:53:32 +05:30
Pooja
9e19244665 move: import setting into import collection modal (#5929) 2025-11-12 11:36:21 +05:30
Pooja
f439f2de9a add: draft for collection and folder settings (#5947) 2025-11-12 11:11:12 +05:30
Bijin A B
e844d35b03 Merge pull request #6051 from abhishek-bruno/fix/postman-export-missing-tests
fix: modify brunoToPostman function to include tests in event section
2025-11-11 14:22:12 +05:30
Abhishek S Lal
26e140aca0 feat(converters): add test scripts in bruno to postman export 2025-11-11 12:56:45 +05:30
sajadoncode
b15c421270 docs: add farsi translation 2025-11-07 09:36:09 +01:00
adarshajit
1656e951fb feat: add stop request button in api url bar 2025-11-05 17:08:19 +05:30
1408 changed files with 107149 additions and 26830 deletions

66
.coderabbit.yaml Normal file
View File

@@ -0,0 +1,66 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
language: 'en-US'
early_access: false
tone_instructions: 'You are an expert code reviewer in TypeScript, JavaScript, NodeJS, and ElectronJS. You work in an enterprise software developer team, providing concise and clear code review advice. You only elaborate or provide detailed explanations when requested.'
knowledge_base:
opt_out: false
code_guidelines:
enabled: true
filePatterns:
- '**/CODING_STANDARDS.md'
reviews:
profile: 'chill'
request_changes_workflow: false
high_level_summary: true
poem: true
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
base_branches: ['main', 'release/*']
path_instructions:
- path: 'tests/**/**.*'
instructions: |
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
```
chat:
auto_reply: true

View File

@@ -1,9 +1,10 @@
# Description
### Description
<!-- Explain here the changes your PR introduces and text to help us understand the context of this change. -->
### Contribution Checklist:
#### 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.**
@@ -12,6 +13,6 @@
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
#### Publishing to New Package Managers
Please see [here](../publishing.md) for more information.

View File

@@ -23,4 +23,5 @@ runs:
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
npm run build:bruno-converters
npm run build:bruno-requests
npm run build:schema-types
npm run build:bruno-filestore

View File

@@ -0,0 +1,70 @@
const fs = require('fs');
const { execSync } = require('child_process');
// Check if flaky-tests.json exists
if (!fs.existsSync('flaky-tests.json')) {
console.log('No flaky-tests.json found');
process.exit(0);
}
// Get changed files in PR
let changedFiles = [];
try {
changedFiles = execSync('git diff --name-only origin/main...HEAD')
.toString()
.split('\n')
.filter(f => f.endsWith('.spec.ts'));
} catch (error) {
console.log('Could not determine changed files:', error.message);
process.exit(0);
}
if (changedFiles.length === 0) {
console.log('No test files were modified in this PR');
process.exit(0);
}
// Read flaky tests
const flakyTests = JSON.parse(fs.readFileSync('flaky-tests.json', 'utf8'));
if (flakyTests.length === 0) {
console.log('No flaky/failed tests found');
process.exit(0);
}
// Find modified flaky tests
const modifiedFlakyTests = flakyTests.filter(test =>
changedFiles.some(file => test.file.includes(file))
);
if (modifiedFlakyTests.length === 0) {
console.log('No modified test files are flaky');
process.exit(0);
}
// Generate comment markdown
let comment = '## ⚠️ Warning: You modified flaky/failed test files\n\n';
comment += 'The following test files you modified have reliability issues:\n\n';
modifiedFlakyTests.forEach(test => {
const testType = test.status === 'failed' ? '❌ Failed' : '⚠️ Flaky';
comment += `### ${testType}: \`${test.file}\`\n`;
comment += `**Test:** ${test.testTitle}\n`;
comment += `**Status:** ${test.status}\n`;
if (test.retryAttempt > 0) {
comment += `**Retry Attempt:** ${test.retryAttempt}\n`;
}
comment += '\n**To debug locally, run:**\n';
comment += '```bash\n';
comment += `npx playwright test ${test.file} --repeat-each=5 --workers=1\n`;
comment += '```\n\n';
});
comment += '---\n';
comment += '**Note:** Flaky tests passed after retrying, failed tests did not pass. ';
comment += 'Please investigate and fix the root cause before merging.\n';
// Save comment to file for GitHub Action to post
fs.writeFileSync('pr-comment.md', comment);
console.log(`Found ${modifiedFlakyTests.length} modified flaky tests`);

78
.github/scripts/detect-flaky-tests.js vendored Normal file
View File

@@ -0,0 +1,78 @@
const fs = require('fs');
// Read Playwright JSON report
const resultsPath = 'playwright-report/results.json';
if (!fs.existsSync(resultsPath)) {
console.log('No Playwright results found at', resultsPath);
process.exit(0);
}
const results = JSON.parse(fs.readFileSync(resultsPath, 'utf8'));
// Extract flaky tests
// A test is flaky if: status === "passed" AND retry > 0
// A test is failed if: status === "failed"
// This means it failed initially but passed on retry OR failed completely
const flakyTests = [];
function traverseSuites(suites) {
for (const suite of suites) {
// Process specs in this suite
for (const spec of suite.specs || []) {
for (const test of spec.tests || []) {
// Check each test result
for (const result of test.results || []) {
// Track two types of problematic tests:
// 1. Flaky: passed on a retry attempt (retry > 0)
// 2. Failed: failed on all attempts
if ((result.status === 'passed' && result.retry > 0) || result.status === 'failed') {
flakyTests.push({
file: spec.file,
title: spec.title,
testTitle: spec.title,
line: spec.line,
status: result.status,
retryAttempt: result.retry
});
break; // Only record once per test
}
}
}
}
// Recursively process nested suites
if (suite.suites && suite.suites.length > 0) {
traverseSuites(suite.suites);
}
}
}
traverseSuites(results.suites || []);
// Save flaky tests to JSON
fs.writeFileSync('flaky-tests.json', JSON.stringify(flakyTests, null, 2));
// Generate markdown report
let markdown = '## ⚠️ Flaky/Failed Tests Detected\n\n';
markdown += 'The following tests are problematic:\n\n';
flakyTests.forEach(test => {
const testType = test.status === 'failed' ? '❌ Failed' : '⚠️ Flaky';
markdown += `### ${testType}: \`${test.file}\`\n`;
markdown += `- **Test:** ${test.testTitle}\n`;
markdown += `- **Status:** ${test.status}\n`;
if (test.retryAttempt > 0) {
markdown += `- **Retry Attempt:** ${test.retryAttempt}\n`;
}
markdown += `- **Debug command:**\n`;
markdown += '```bash\n';
markdown += `npx playwright test ${test.file} --repeat-each=5 --workers=1\n`;
markdown += '```\n\n';
});
fs.writeFileSync('flaky-report.md', markdown);
console.log(`Found ${flakyTests.length} flaky/failed tests`);
process.exit(flakyTests.length > 0 ? 1 : 0);

View File

@@ -0,0 +1,119 @@
name: Flaky Test Detector
on:
pull_request:
branches: [main]
paths:
- 'tests/**/*.spec.ts'
permissions:
contents: read
pull-requests: write
checks: write
jobs:
detect-flaky-tests:
name: Detect Flaky Tests
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0 # Need full history to compare with main
- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get --no-install-recommends install -y \
libglib2.0-0 libnss3 libdbus-1-3 libatk1.0-0 libatk-bridge2.0-0 \
libcups2 libgtk-3-0 libasound2t64 xvfb
- name: Install npm dependencies
run: |
npm ci --legacy-peer-deps
sudo chown root /home/runner/work/bruno/bruno/node_modules/electron/dist/chrome-sandbox
sudo chmod 4755 /home/runner/work/bruno/bruno/node_modules/electron/dist/chrome-sandbox
- name: Install test collection dependencies
run: npm ci --prefix packages/bruno-tests/collection
- name: Build libraries
run: |
npm run build:graphql-docs
npm run build:bruno-query
npm run build:bruno-common
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
npm run build:bruno-converters
npm run build:bruno-requests
npm run build:schema-types
npm run build:bruno-filestore
- name: Run Playwright tests
run: xvfb-run npm run test:e2e
continue-on-error: true # Continue even if tests fail
- name: Detect flaky tests
id: detect
run: node .github/scripts/detect-flaky-tests.js
continue-on-error: true # Don't fail workflow if flaky tests found
- name: Check modified flaky tests
id: check-modified
run: node .github/scripts/comment-on-flaky-tests.js
continue-on-error: true
- name: Post PR comment
if: hashFiles('pr-comment.md') != ''
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const comment = fs.readFileSync('pr-comment.md', 'utf8');
// Check if we already commented
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Warning: You modified flaky/failed test files')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: comment
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
- name: Upload flaky test artifacts
if: always()
uses: actions/upload-artifact@v6
with:
name: flaky-test-results
path: |
flaky-tests.json
flaky-report.md
playwright-report/
retention-days: 30

View File

@@ -25,7 +25,7 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
@@ -40,7 +40,7 @@ jobs:
run: |
cd packages/bruno-tests/collection
npm install
bru run --env Prod --output junit.xml --format junit
bru run --env Prod --output junit.xml --format junit --sandbox developer
- name: Publish Test Report
uses: dorny/test-reporter@v2

View File

@@ -15,7 +15,7 @@ jobs:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Node Dependencies
uses: ./.github/actions/common/setup-node-deps
@@ -44,7 +44,7 @@ jobs:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Node Dependencies
uses: ./.github/actions/common/setup-node-deps
@@ -73,7 +73,7 @@ jobs:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Node Dependencies
uses: ./.github/actions/common/setup-node-deps

View File

@@ -13,7 +13,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
@@ -30,6 +30,7 @@ jobs:
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
npm run build --workspace=packages/bruno-converters
npm run build --workspace=packages/bruno-requests
npm run build --workspace=packages/bruno-schema-types
npm run build --workspace=packages/bruno-filestore
- name: Lint Check
@@ -57,6 +58,8 @@ jobs:
run: npm run test --workspace=packages/bruno-converters
- name: Test Package bruno-electron
run: npm run test --workspace=packages/bruno-electron
- name: Test Package bruno-requests
run: npm run test --workspace=packages/bruno-requests
cli-test:
name: CLI Tests
@@ -66,7 +69,7 @@ jobs:
pull-requests: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-node@v5
with:
node-version-file: '.nvmrc'
@@ -83,6 +86,7 @@ jobs:
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
npm run build --workspace=packages/bruno-converters
npm run build --workspace=packages/bruno-requests
npm run build --workspace=packages/bruno-schema-types
npm run build --workspace=packages/bruno-filestore
- name: Run Local Testbench
@@ -94,7 +98,7 @@ jobs:
run: |
cd packages/bruno-tests/collection
npm install
node ../../bruno-cli/bin/bru.js run --env Prod --output junit.xml --format junit
node ../../bruno-cli/bin/bru.js run --env Prod --output junit.xml --format junit --sandbox developer
- name: Publish Test Report
uses: EnricoMi/publish-unit-test-result-action@v2
@@ -108,7 +112,7 @@ jobs:
timeout-minutes: 60
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-node@v5
with:
node-version: v22.11.x
@@ -134,12 +138,13 @@ jobs:
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
npm run build:bruno-converters
npm run build:bruno-requests
npm run build:schema-types
npm run build:bruno-filestore
- name: Run Playwright tests
run: |
xvfb-run npm run test:e2e
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v6
if: ${{ !cancelled() }}
with:
name: playwright-report

12
.gitignore vendored
View File

@@ -48,6 +48,18 @@ yarn-error.log*
bruno.iml
.idea
.vscode
.cursor
# Playwright
/blob-report/
# Development plan files
CLAUDE.md
AGENTS.md
*.plan.md
# packages dist
packages/bruno-filestore/dist
packages/bruno-requests/dist
packages/bruno-schema-types/dist
packages/bruno-converters/dist

2
.nvmrc
View File

@@ -1 +1 @@
v22.11.0
v22.12.0

View File

@@ -1,7 +0,0 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120
}

87
CODING_STANDARDS.md Normal file
View File

@@ -0,0 +1,87 @@
# Bruno Coding Standards
- No diffs unless an actual change is made, the code changes need to be as minimal as possible, avoid making un-necessary whitespace diffs. This is already handled by eslint but make sure you check your code changes before commiting and raising a PR.
## General Style Rules
- Use 2 spaces for indentation. No tabs, just spaces keeps everything neat and uniform.
- Stick to single quotes for strings. For JSX/TSX attributes, use double quotes (e.g., <svg xmlns="..." viewBox="...">) to follow React conventions.
- Always add semicolons at the end of statements. It's like putting a period at the end of a sentence clarity matters.
- JSX is enabled, so feel free to use it where it makes sense.
## Punctuation and Spacing
- No trailing commas. Keep it clean, no extra commas hanging around.
- Always use parentheses around parameters in arrow functions. Even for single params consistency is key.
- For multiline constructs, put opening braces on the same line, and ensure consistency. Minimum 2 elements for multiline.
- No newlines inside function parentheses. Keep 'em tight.
- 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.
- No strict max length write readable code, not cramped lines.
- Multiple expressions per line in JSX are fine flexibility is nice.
Remember, these rules are here to make our codebase harmonious. If something doesn't fit perfectly, let's chat about it. Happy coding! 🚀
## Tests
- Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created.
- Prioritise high-value tests over maximum coverage. Focus on testing behaviour that is critical, complex, or likely to break—dont chase coverage numbers for their own sake.
- Write behaviour-driven tests, not implementation-driven ones. Tests should validate real expected output and observable behaviour, not internal details or mocked-out logic unless absolutely necessary.
- Minimise mocking unless it meaningfully increases clarity or isolates external dependencies. Prefer real flows where practical; only mock external services, slow systems, or non-deterministic behaviour.
- Keep tests readable and maintainable. Optimise for clarity over cleverness. Name tests descriptively, keep setup minimal, and avoid unnecessary abstraction.
- Aim for tests that fail usefully. When a test fails, it should clearly indicate what behaviour broke and why.
- Cover both the “happy path” and the realistically problematic paths. Validate expected success behaviour, but also validate error handling, edge cases, and degraded-mode behaviour when appropriate.
- Ensure tests are deterministic and reproducible. No randomness, timing dependencies, or environment-specific assumptions without explicit control.
- Avoid overfitting tests to current behaviour if future flexibility matters. Only assert what needs to be true, not incidental details.
- Use consistent patterns and helper utilities where they improve clarity. Prefer shared test utilities over copy-pasted setup code, but only when it actually reduces complexity.
- Tests should be fast enough to run continuously. Avoid long-running operations unless absolutely necessary; prefer lightweight fixtures and isolated units.
## UI Specific instructions
### React
- Use styled component's theme prop to manage CSS colors and not CSS variables when in the context of a styled component or any react component using the styled component
- Styled Components are used as wrappers to define both self and children components style, tailwind classes are used specifically for layout based styles.
- Styled Component CSS might also change layout but tailwind classes shouldn't define colors.
- MUST: Prefer custom hooks for business logic, data fetching, and side-effects.
- MUST: Avoid `useEffect` unless absolutely needed. Prefer derived state, event handlers.
- SHOULD: Memoize only when necessary (`useMemo`/`useCallback`), and prefer moving logic into hooks first.
- MUST: Do not use namespace access for hooks in app code (e.g., `React.useCallback`, `React.useMemo`, `React.useState`). Import hooks directly.
- Correct: `import { useCallback, useMemo, useState } from "react";`
- Avoid: `import * as React from "react";` then `React.useCallback(...)`
- Add `data-testid` to testable elements for Playwright
- Co-locate utilities that are truly component-specific next to the component, otherwise place shared items under a common folder
## Readability and Abstractions
- Avoid abstractions unless the exact same code is being used in more than 3 places.
- Names for functions need to be concise and descriptive.
- Add in JSDoc comments to add more details to the abstractions if needed.
- Follow functional programming but just enough to be readable, we don't need to go as deep as ADTs and Monads, we want to keep the code pipeline obvious and easy for everyone to read and contribute to.
- Avoid single line abstractions where all that's being done is increasing the call stack with one additional function.
- Add in meaningful comments instead of obvious ones where complex code flow is explained properly.

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 KiB

After

Width:  |  Height:  |  Size: 584 KiB

View File

@@ -16,6 +16,7 @@
| [日本語](docs/contributing/contributing_ja.md)
| [हिंदी](docs/contributing/contributing_hi.md)
| [Dutch](docs/contributing/contributing_nl.md)
| [فارسی](docs/contributing/contributing_fa.md)
## Let's make Bruno better, together!!
@@ -69,11 +70,13 @@ npm run build:bruno-query
npm run build:bruno-common
npm run build:bruno-converters
npm run build:bruno-requests
npm run build:schema-types
npm run build:bruno-filestore
# bundle js sandbox libraries
npm run sandbox:bundle-libraries --workspace=packages/bruno-js
```
##### Option 2
```bash
@@ -94,18 +97,22 @@ npm run dev:electron
```
##### Option 2
```bash
# run electron and react app concurrently
npm run dev
```
#### Customize Electron `userData` path
If `ELECTRON_USER_DATA_PATH` env-variable is present and its development mode, then `userData` path is modified accordingly.
e.g.
```sh
ELECTRON_USER_DATA_PATH=$(realpath ~/Desktop/bruno-test) npm run dev:electron
```
This will create a `bruno-test` folder on your Desktop and use it as the `userData` path.
### Troubleshooting

View File

@@ -0,0 +1,92 @@
[English](../../contributing.md)
## با هم، Bruno را بهتر می‌کنیم!
خوشحالم که قصد دارید Bruno را بهبود ببخشید. در ادامه قوانین و راهنماها برای راه‌اندازی Bruno روی سیستم شما آورده شده است.
### فناوری‌های استفاده‌شده
به فارسی برونو Bruno با استفاده از Next.js و React ساخته شده است. همچنین از Electron برای بسته‌بندی نسخه دسکتاپ (که امکان مجموعه‌های محلی را فراهم می‌کند) استفاده می‌کنیم.
کتابخانه‌هایی که استفاده می‌کنیم:
- CSS - Tailwind استایل
- Codemirror - ویرایشگر کد
- Redux - مدیریت وضعیت
- Tabler Icons - آیکون‌ها
- formik - فرم‌ها
- Yup اعتبارسنجی اسکیمـا
- axios - کلاینت درخواست
- chokidar - پایش‌گر سیستم فایل
### پیش‌نیازها
شما به [نود v20.x یا اخرین نسخه پایدار](https://nodejs.org/en/) و npm 8.x نیاز دارید. در این پروژه از فضای کاری npm (npm workspaces) استفاده می‌کنیم.
### شروع به کدنویسی
برای راه‌اندازی محیط توسعه محلی به فایل [مستندات توسعه](docs/development_fa.md) مراجعه کنید:
### ارسال Pull Request
1 - لطفاً Pull Requestها (PR) را کوتاه و متمرکز نگه دارید و تنها یک هدف مشخص را دنبال کنند. </br>
2 - لطفاً از فرمت نام‌گذاری شاخه‌ها استفاده کنید:
- feature/[name]: این شاخه باید شامل یک قابلیت مشخص باشد.
- feature/dark-mode : مثال
- bugfix/[name]: این شاخه باید تنها شامل رفع یک باگ مشخص باشد.
- bugfix/bug-1 : مثال
## توسعه
به فارسی برونو یا Bruno به‌صورت یک اپلیکیشن «سنگین» توسعه داده می‌شود. برای اجرا باید ابتدا Next.js را در یک پنجره ترمینال اجرا کنید و سپس اپلیکیشن Electron را در پنجره ترمینال دیگری راه‌اندازی نمایید.
### نیازمندی توسعه
- NodeJS v18
### اجرای محلی
```bash
# از ورژن NodeJS 18 استفاده کنید
nvm use
# نصب وابستگی‌ها
npm i --legacy-peer-deps
# ساخت مستندات GraphQL
npm run build:graphql-docs
# ساخت bruno-query
npm run build:bruno-query
# اجرای اپ Next (ترمینال 1)
npm run dev:web
# اجرای اپ Electron (ترمینال 2)
npm run dev:electron
```
### عیب‌یابی
ممکن است هنگام اجرای `npm install` خطای `Unsupported platform` ببینید. برای رفع این مشکل، پوشه `node_modules` و فایل `package-lock.json` را حذف کرده و سپس دوباره `npm install` را اجرا کنید. این کار معمولاً همه پکیج‌های لازم را نصب می‌کند.
```shell
# حذف پوشه node_modules در زیردایرکتوری‌ها
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
rm -rf "$dir"
done
# حذف فایل package-lock.json در زیردایرکتوری‌ها
find . -type f -name "package-lock.json" -delete
```
### تست‌ها
```bash
# اجرای تست‌های schema مربوط به bruno
npm test --workspace=packages/bruno-schema
# اجرای تست‌ها در همه فضاهای کاری (در صورت وجود)
npm test --workspaces --if-present
```

View File

@@ -0,0 +1,8 @@
[English](../../publishing.md)
### انتشار Bruno در یک پکیج منیجر جدید
اگرچه کد ما متن‌باز است و همه می‌توانند از آن استفاده کنند، لطفاً قبل از انتشار Bruno در مدیر بسته‌های جدید با ما تماس بگیرید. به عنوان سازنده Bruno، علامت تجاری `Bruno` را برای این پروژه دارم و مایلم توزیع آن را مدیریت کنم. اگر دوست دارید Bruno را در یک مدیر بسته جدید ببینید، لطفاً یک issue در گیت‌هاب ثبت کنید.
اگرچه بیشتر قابلیت‌های ما رایگان و متن‌باز هستند (شامل REST و GraphQL Apis)،
ما تلاش می‌کنیم بین اصول متن‌باز و توسعه پایدار تعادل مناسبی برقرار کنیم - https://github.com/usebruno/bruno/discussions/269

View File

@@ -3,7 +3,7 @@
### برونو - بيئة تطوير مفتوحة المصدر لاستكشاف واختبار واجهات برمجة التطبيقات (APIs).
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### ব্রুনো - API অন্বেষণ এবং পরীক্ষা করার জন্য ওপেনসোর্স IDE।
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - 开源 IDE用于探索和测试 API。
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - Opensource IDE zum Erkunden und Testen von APIs.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - IDE de código abierto para explorar y probar APIs.
[![Versión en Github](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![Versión en Github](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Actividad de Commits](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

143
docs/readme/readme_fa.md Normal file
View File

@@ -0,0 +1,143 @@
<br />
<img src="../../assets/images/logo-transparent.png" width="80"/>
### برونو یا Bruno - محیط توسعه متن باز برای تست و توسعه API ها
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)
[![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com)
[![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads)
[English](../../readme.md)
| [Українська](./readme_ua.md)
| [Русский](./readme_ru.md)
| [Türkçe](./readme_tr.md)
| [Deutsch](./readme_de.md)
| [Français](./readme_fr.md)
| [Português (BR)](./readme_pt_br.md)
| [한국어](./readme_kr.md)
| [বাংলা](./readme_bn.md)
| [Español](./readme_es.md)
| **فارسی**
| [Română](./readme_ro.md)
| [Polski](./readme_pl.md)
| [简体中文](./readme_cn.md)
| [正體中文](./readme_zhtw.md)
| [العربية](./readme_ar.md)
| [日本語](./readme_ja.md)
| [ქართული](./readme_ka.md)
برونو یک کلاینت API جدید و نوآورانه است که هدفش تغییر وضعیت فعلی ابزارهایی مانند Postman و سایر ابزارهای مشابه است.
برونو مجموعه‌های شما را مستقیماً در یک پوشه روی فایل‌سیستم شما ذخیره می‌کند. ما از یک زبان نشانه‌گذاری ساده به نام Bru برای ذخیره اطلاعات درخواست‌های API استفاده می‌کنیم.
شما می‌توانید برای همکاری روی مجموعه‌های API خود، از Git یا هر سیستم کنترل نسخه دلخواهتان استفاده کنید.
برونو فقط به صورت آفلاین کار می‌کند. هیچ برنامه‌ای برای اضافه کردن همگام‌سازی ابری به برونو در آینده وجود ندارد. ما به حریم خصوصی داده‌های شما اهمیت می‌دهیم و معتقدیم که باید روی دستگاه خودتان باقی بمانند. می‌توانید چشم‌انداز بلندمدت ما را مطالعه کنید. [اینجا (به انگلیسی)](https://github.com/usebruno/bruno/discussions/269)
📢 جدیدترین ارائه ما را در کنفرانس India FOSS 3.0 تماشا کنید.
[اینجا](https://www.youtube.com/watch?v=7bSMFpbcPiY)
![bruno](/assets/images/landing-2.png) <br /><br />
### نصب
برونو به صورت یک فایل باینری برای دانلود در دسترس است. [بر روی وبسایت ما](https://www.usebruno.com/downloads) برای مک لینکوس و ویندوز.
همچنین می‌توانید برونو را از طریق مدیر بسته‌هایی مانند Homebrew، Chocolatey، Snap و Apt نصب کنید.
```sh
# بر روی مک از طریق brew
brew install bruno
# بر روی ویندوز از طریق Chocolatey
choco install bruno
# بر روی لینوکس از طریق Snap
snap install bruno
# بر روی لینوکس از طریق Apt
sudo mkdir -p /etc/apt/keyrings
sudo apt update && sudo apt install gpg curl
curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x9FA6017ECABE0266" \
| gpg --dearmor \
| sudo tee /etc/apt/keyrings/bruno.gpg > /dev/null
sudo chmod 644 /etc/apt/keyrings/bruno.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" \
| sudo tee /etc/apt/sources.list.d/bruno.list
sudo apt update && sudo apt install bruno
```
### روی پلتفرم‌های مختلف کار می‌کند 🖥️
![bruno](/assets/images/run-anywhere.png) <br /><br />
### همکاری از طریق گیت 👩‍💻🧑‍💻
یا هر سیستم کنترل نسخه‌ای که ترجیح می‌دهید
![bruno](/assets/images/version-control.png) <br /><br />
### لینک‌های مهم 📌
- [آخرین نسخه پایدار ما](https://github.com/usebruno/bruno/discussions/269)
- [نقشه راه](https://github.com/usebruno/bruno/discussions/384)
- [مستندات](https://docs.usebruno.com)
- [وبسایت](https://www.usebruno.com)
- [اشتراک ها](https://www.usebruno.com/pricing)
- [دانلود](https://www.usebruno.com/downloads)
### ویدیوها 🎥
- [تجربه ها](https://github.com/usebruno/bruno/discussions/343)
- [مرکز دانش](https://github.com/usebruno/bruno/discussions/386)
- [اسکریپ مانیا](https://github.com/usebruno/bruno/discussions/385)
### حمایت ❤️
جوون! اگر این پروژه را دوست دارید، روی دکمه ⭐ کلیک کنید!
### تجربه‌های به اشتراک گذاشته‌شده 📣
اگر برونو به شما یا تیمتان کمک کرده است، لطفاً فراموش نکنید تجربه‌های خود را به اشتراک بگذارید. [تجربه‌های خود را در بحث گیت‌هاب ما به اشتراک بگذارید](https://github.com/usebruno/bruno/discussions/343).
### انتشار برونو در یک پکیچ منیجر جدید
لطفا چک بکنید [اینجارو](../../publishing.md) برای اطلاعات بیشتر.
### مشارکت 👩‍💻🧑‍💻
خوشحالم که می‌خواهید برونو را بهتر کنید. لطفا [راهنمای مشارکت را بررسی کنید](../contributing/contributing_fa.md).
حتی اگر نمی‌توانید از طریق کدنویسی مشارکت کنید، در گزارش باگ‌ها و درخواست قابلیت‌های جدید که به حل نیازهای شما کمک می‌کند تردید نکنید.
### نویسنده ها
<div align="center">
<a href="https://github.com/usebruno/bruno/graphs/contributors">
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
</a>
</div>
### در ارتباط باشید 🌐
[𝕏 (تویتر)](https://twitter.com/use_bruno) <br />
[وبسایت](https://www.usebruno.com) <br />
[دیسکورد](https://discord.com/invite/KgcZUncpjq) <br />
[لینکدین](https://www.linkedin.com/company/usebruno)
### برند
**نام**
به فارسی برونو - `Bruno` یک علامت تجاری ثبت‌شده متعلق به [Anoop M D](https://www.helloanoop.com/)
**لوگو**
لوگو توسط [OpenMoji](https://openmoji.org/library/emoji-1F436/) ساخته شده است. مجوز: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
### مجوز 📄
[MIT](../../license.md)

View File

@@ -3,7 +3,7 @@
### Bruno - IDE Opensource pour explorer et tester des APIs.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - Opensource IDE per esplorare e testare gli APIs.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - API の検証・動作テストのためのオープンソース IDE.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### ბრუნო - ღია წყაროების IDE API-ების შესწავლისა და ტესტირებისათვის.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - API 탐색 및 테스트를 위한 오픈소스 IDE.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - Open source IDE voor het verkennen en testen van API's.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - Otwartoźródłowe IDE do eksploracji i testów APIs.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - IDE de código aberto para explorar e testar APIs.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - Mediu integrat de dezvoltare cu sursă deschisă pentru explorarea și testarea API-urilor.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - IDE с открытым исходным кодом для изучения и тестирования API.
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - API'leri keşfetmek ve test etmek için açık kaynaklı IDE.
[![GitHub sürümü](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub sürümü](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - IDE із відкритим кодом для тестування та дослідження API
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -3,7 +3,7 @@
### Bruno - 探索和測試 API 的開源 IDE 工具
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%2Fbruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/actions/workflows/tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)

View File

@@ -1,6 +1,6 @@
// eslint.config.js
const { defineConfig } = require("eslint/config");
const globals = require("globals");
const { defineConfig } = require('eslint/config');
const globals = require('globals');
const { fixupPluginRules } = require('@eslint/compat');
const eslintPluginDiff = require('eslint-plugin-diff');
@@ -11,6 +11,18 @@ const runESMImports = async () => {
};
module.exports = runESMImports().then(() => defineConfig([
// Global ignores - must be a standalone object with ONLY ignores
{
ignores: [
'**/node_modules/**/*',
'**/dist/**/*',
'**/*.bru',
'packages/bruno-js/src/sandbox/bundle-browser-rollup.js',
'packages/bruno-app/public/static/**/*',
'packages/bruno-app/.next/**/*',
'packages/bruno-electron/web/**/*'
]
},
{
plugins: {
'diff': fixupPluginRules(eslintPluginDiff),
@@ -34,13 +46,13 @@ module.exports = runESMImports().then(() => defineConfig([
'packages/bruno-converters/**/*.js',
'packages/bruno-electron/**/*.js',
'packages/bruno-filestore/**/*.ts',
'packages/bruno-schema-types/**/*.ts',
'packages/bruno-js/**/*.js',
'packages/bruno-lang/**/*.js',
'packages/bruno-requests/**/*.ts',
'packages/bruno-requests/**/*.js',
'packages/bruno-tests/**/*.{js,ts}'
],
processor: 'diff/diff',
rules: {
...stylistic.configs.customize({
indent: 2,
@@ -56,7 +68,7 @@ module.exports = runESMImports().then(() => defineConfig([
minElements: 2,
consistent: true
}],
'@stylistic/function-paren-newline': ['error', 'never'],
'@stylistic/function-paren-newline': ['off'],
'@stylistic/array-bracket-spacing': ['error', 'never'],
'@stylistic/arrow-spacing': ['error', { before: true, after: true }],
'@stylistic/function-call-spacing': ['error', 'never'],
@@ -64,12 +76,14 @@ module.exports = runESMImports().then(() => defineConfig([
'@stylistic/padding-line-between-statements': ['off'],
'@stylistic/semi-style': ['error', 'last'],
'@stylistic/max-len': ['off'],
'@stylistic/jsx-one-expression-per-line': ['off']
'@stylistic/jsx-one-expression-per-line': ['off'],
'@stylistic/max-statements-per-line': ['off'],
'@stylistic/no-mixed-operators': ['off']
}
},
{
files: ["packages/bruno-app/**/*.{js,jsx,ts}"],
ignores: ["**/*.config.js", "**/public/**/*"],
files: ['packages/bruno-app/**/*.{js,jsx,ts}'],
ignores: ['**/*.config.js', '**/public/**/*'],
languageOptions: {
globals: {
...globals.browser,
@@ -82,114 +96,126 @@ module.exports = runESMImports().then(() => defineConfig([
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
jsx: true
}
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
// It prevents lint errors when using CommonJS exports (module.exports) in Jest mocks.
files: ["packages/bruno-app/src/test-utils/mocks/codemirror.js"],
files: ['packages/bruno-app/src/test-utils/mocks/codemirror.js'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
},
...globals.jest
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-cli/**/*.js"],
ignores: ["**/*.config.js"],
// Storybook config files use CommonJS with __dirname and module.exports
files: ['packages/bruno-app/storybook/**/*.js'],
languageOptions: {
globals: {
...globals.node
}
},
rules: {
'no-undef': 'error'
}
},
{
files: ['packages/bruno-cli/**/*.js'],
ignores: ['**/*.config.js'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parserOptions: {
ecmaVersion: "latest"
},
ecmaVersion: 'latest'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-common/**/*.ts"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-common/**/*.ts'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parser: require("@typescript-eslint/parser"),
parser: require('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./packages/bruno-common/tsconfig.json",
},
ecmaVersion: 'latest',
sourceType: 'module',
project: './packages/bruno-common/tsconfig.json'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-converters/**/*.js"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-converters/**/*.js'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
ecmaVersion: 'latest',
sourceType: 'module'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-electron/**/*.js"],
ignores: ["**/*.config.js", "**/web/**/*"],
files: ['packages/bruno-electron/**/*.js'],
ignores: ['**/*.config.js', '**/web/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
},
...globals.jest
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-filestore/**/*.ts"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-filestore/**/*.ts'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parser: require("@typescript-eslint/parser"),
parser: require('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./packages/bruno-filestore/tsconfig.json",
},
ecmaVersion: 'latest',
sourceType: 'module',
project: './packages/bruno-filestore/tsconfig.json'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-js/**/*.js"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-js/**/*.js'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
@@ -200,65 +226,65 @@ module.exports = runESMImports().then(() => defineConfig([
typeDetectGlobalObject: false
},
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
ecmaVersion: 'latest',
sourceType: 'module'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-lang/**/*.js"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-lang/**/*.js'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
ecmaVersion: 'latest',
sourceType: 'module'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-requests/**/*.ts"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-requests/**/*.ts'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parser: require("@typescript-eslint/parser"),
parser: require('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: "./packages/bruno-requests/tsconfig.json",
},
ecmaVersion: 'latest',
sourceType: 'module',
project: './packages/bruno-requests/tsconfig.json'
}
},
rules: {
"no-undef": "error",
},
'no-undef': 'error'
}
},
{
files: ["packages/bruno-requests/**/*.js"],
ignores: ["**/*.config.js", "**/dist/**/*"],
files: ['packages/bruno-requests/**/*.js'],
ignores: ['**/*.config.js', '**/dist/**/*'],
languageOptions: {
globals: {
...globals.node,
...globals.jest,
...globals.jest
},
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
},
ecmaVersion: 'latest',
sourceType: 'module'
}
},
rules: {
"no-undef": "error",
},
},
'no-undef': 'error'
}
}
]));

5589
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
"packages/bruno-common",
"packages/bruno-converters",
"packages/bruno-schema",
"packages/bruno-schema-types",
"packages/bruno-query",
"packages/bruno-js",
"packages/bruno-lang",
@@ -22,14 +23,20 @@
"@eslint/compat": "^1.3.2",
"@faker-js/faker": "^7.6.0",
"@jest/globals": "^29.2.0",
"@opencollection/types": "~0.7.0",
"@playwright/test": "^1.51.1",
"@rollup/plugin-json": "^6.1.0",
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
"@storybook/builder-webpack5": "^10.1.10",
"@storybook/react": "^10.1.10",
"@storybook/react-webpack5": "^10.1.10",
"@stylistic/eslint-plugin": "^5.3.1",
"@types/jest": "^29.5.11",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.14.1",
"@typescript-eslint/parser": "^8.39.0",
"concurrently": "^8.2.2",
"cross-env": "10.1.0",
"eslint": "^9.26.0",
"eslint-plugin-diff": "^2.0.3",
"fs-extra": "^11.1.1",
@@ -42,25 +49,27 @@
"pretty-quick": "^3.1.3",
"randomstring": "^1.2.2",
"rimraf": "^6.0.1",
"storybook": "^10.1.10",
"ts-jest": "^29.2.6"
},
"scripts": {
"setup": "node ./scripts/setup.js",
"watch:converters": "npm run watch --workspace=packages/bruno-converters",
"dev": "concurrently --kill-others \"npm run dev:web\" \"npm run dev:electron\"",
"dev": "node ./scripts/dev.js",
"watch": "npm run dev:watch",
"dev:watch": "node ./scripts/dev-hot-reload.js",
"dev:web": "npm run dev --workspace=packages/bruno-app",
"build:web": "npm run build --workspace=packages/bruno-app",
"prettier:web": "npm run prettier --workspace=packages/bruno-app",
"dev:electron": "npm run dev --workspace=packages/bruno-electron",
"dev:electron:debug": "npm run debug --workspace=packages/bruno-electron",
"storybook": "npm run storybook --workspace=packages/bruno-app",
"build:bruno-common": "npm run build --workspace=packages/bruno-common",
"build:bruno-requests": "npm run build --workspace=packages/bruno-requests",
"build:bruno-filestore": "npm run build --workspace=packages/bruno-filestore",
"build:bruno-converters": "npm run build --workspace=packages/bruno-converters",
"build:bruno-query": "npm run build --workspace=packages/bruno-query",
"build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs",
"build:schema-types": "npm run build --workspace=packages/bruno-schema-types",
"build:electron": "node ./scripts/build-electron.js",
"build:electron:mac": "./scripts/build-electron.sh mac",
"build:electron:win": "./scripts/build-electron.sh win",
@@ -69,12 +78,12 @@
"build:electron:rpm": "./scripts/build-electron.sh rpm",
"build:electron:snap": "./scripts/build-electron.sh snap",
"watch:common": "npm run watch --workspace=packages/bruno-common",
"watch:requests": "npm run watch --workspace=packages/bruno-requests",
"test:codegen": "node playwright/codegen.ts",
"test:e2e": "playwright test --project=default",
"test:e2e:ssl": "playwright test --project=ssl",
"test:prettier:web": "npm run test:prettier --workspace=packages/bruno-app",
"lint": "node --max_old_space_size=4096 $(npx which eslint)",
"lint:fix": "node --max_old_space_size=4096 $(npx which eslint) --fix",
"lint": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" npx eslint",
"lint:fix": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" npx eslint --fix",
"prepare": "husky"
},
"nano-staged": {
@@ -89,5 +98,8 @@
"json-schema-typed": "8.0.1"
}
}
},
"dependencies": {
"ajv": "^8.17.1"
}
}
}

View File

@@ -22,6 +22,7 @@ build
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
# local env files
.env.local
@@ -33,4 +34,5 @@ yarn-error.log*
.next/
dist/
.env
.env
storybook-static/

View File

@@ -1,7 +0,0 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 120
}

View File

@@ -6,4 +6,4 @@ module.exports = {
}]
],
plugins: ['babel-plugin-styled-components']
};
};

View File

@@ -1,10 +1,10 @@
module.exports = {
rootDir: '.',
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
'^.+\\.[jt]sx?$': 'babel-jest'
},
transformIgnorePatterns: [
"/node_modules/(?!strip-json-comments|nanoid|xml-formatter)/",
'/node_modules/(?!strip-json-comments|nanoid|xml-formatter)/'
],
moduleNameMapper: {
'^assets/(.*)$': '<rootDir>/src/assets/$1',
@@ -22,9 +22,9 @@ module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@testing-library/jest-dom'],
setupFiles: [
'<rootDir>/jest.setup.js',
'<rootDir>/jest.setup.js'
],
testMatch: [
'<rootDir>/src/**/*.spec.[jt]s?(x)'
]
};
};

View File

@@ -8,8 +8,8 @@
"build": "rsbuild build -m production",
"preview": "rsbuild preview",
"test": "jest",
"test:prettier": "prettier --check \"./src/**/*.{js,jsx,json,ts,tsx}\"",
"prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\""
"storybook": "storybook dev -p 6006 --config-dir storybook",
"build-storybook": "storybook build --config-dir storybook"
},
"dependencies": {
"@fontsource/inter": "^5.0.15",
@@ -21,6 +21,8 @@
"@usebruno/common": "0.1.0",
"@usebruno/graphql-docs": "0.1.0",
"@usebruno/schema": "0.7.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"classnames": "^2.3.1",
"codemirror": "5.65.2",
"codemirror-graphql": "2.1.1",
@@ -28,7 +30,7 @@
"dompurify": "^3.2.4",
"escape-html": "^1.0.3",
"fast-fuzzy": "^1.12.0",
"fast-json-format": "~0.3.0",
"fast-json-format": "~0.4.0",
"file": "^0.2.2",
"file-dialog": "^0.0.8",
"file-saver": "^2.0.5",
@@ -37,19 +39,24 @@
"graphiql": "3.7.1",
"graphql": "^16.6.0",
"graphql-request": "^3.7.0",
"hexy": "^0.3.5",
"httpsnippet": "^3.0.9",
"i18next": "24.1.2",
"idb": "^7.0.0",
"immer": "^9.0.15",
"js-yaml": "^4.1.0",
"jsesc": "^3.0.2",
"jshint": "^2.13.6",
"json5": "^2.2.3",
"jsonc-parser": "^3.2.1",
"jsonpath-plus": "^10.3.0",
"jsonschema": "^1.5.0",
"know-your-http-well": "^0.5.0",
"linkify-it": "^5.0.0",
"lodash": "^4.17.21",
"markdown-it": "^13.0.2",
"markdown-it-replace-link": "^1.2.0",
"mime-types": "^3.0.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.47",
"mousetrap": "^1.6.5",
@@ -57,9 +64,10 @@
"path": "^0.12.7",
"pdfjs-dist": "4.4.168",
"platform": "^1.3.6",
"polished": "^4.3.1",
"posthog-node": "4.2.1",
"prettier": "^2.7.1",
"qs": "^6.11.0",
"qs": "^6.14.1",
"query-string": "^7.0.1",
"react": "19.0.0",
"react-copy-to-clipboard": "^5.1.0",
@@ -74,14 +82,17 @@
"react-player": "^2.16.0",
"react-redux": "^7.2.9",
"react-tooltip": "^5.5.2",
"react-virtuoso": "^4.18.1",
"sass": "^1.46.0",
"semver": "^7.7.1",
"shell-quote": "^1.8.3",
"strip-json-comments": "^5.0.1",
"styled-components": "^5.3.3",
"swagger-ui-react": "5.17.12",
"system": "^2.0.1",
"url": "^0.11.3",
"xml-formatter": "^3.5.0",
"xml2js": "^0.6.2",
"yup": "^0.32.11"
},
"devDependencies": {
@@ -119,4 +130,4 @@
"form-data": "4.0.4"
}
}
}
}

View File

@@ -1,6 +1,6 @@
const darkTheme = {
brand: '#546de5',
text: 'rgb(52 52 52)',
'brand': '#546de5',
'text': 'rgb(52 52 52)',
'primary-text': '#ffffff',
'primary-theme': '#1e1e1e',
'secondary-text': '#929292',

View File

@@ -1,6 +1,6 @@
const lightTheme = {
brand: '#546de5',
text: 'rgb(52 52 52)',
'brand': '#546de5',
'text': 'rgb(52 52 52)',
'primary-text': 'rgb(52 52 52)',
'primary-theme': '#ffffff',
'secondary-text': '#929292',

View File

@@ -4,7 +4,7 @@ import { AccordionItem, AccordionHeader, AccordionContent } from './styledWrappe
const AccordionContext = createContext();
const Accordion = ({ children, defaultIndex }) => {
const Accordion = ({ children, defaultIndex, dataTestId }) => {
const [openIndex, setOpenIndex] = useState(defaultIndex);
const toggleItem = (index) => {
@@ -13,7 +13,7 @@ const Accordion = ({ children, defaultIndex }) => {
return (
<AccordionContext.Provider value={{ openIndex, toggleItem }}>
<div>{children}</div>
<div data-testid={dataTestId}>{children}</div>
</AccordionContext.Provider>
);
};

View File

@@ -0,0 +1,129 @@
const yamlPlugin = (cm) => {
cm.defineMode('yaml', function () {
var cons = ['true', 'false', 'on', 'off', 'yes', 'no'];
var keywordRegex = new RegExp('\\b((' + cons.join(')|(') + '))$', 'i');
return {
token: function (stream, state) {
var ch = stream.peek();
var esc = state.escaped;
state.escaped = false;
/* comments */
if (ch == '#' && (stream.pos == 0 || /\s/.test(stream.string.charAt(stream.pos - 1)))) {
stream.skipToEnd();
return 'comment';
}
if (stream.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/)) return 'string';
if (state.literal && stream.indentation() > state.keyCol) {
stream.skipToEnd();
return 'string';
} else if (state.literal) {
state.literal = false;
}
if (stream.sol()) {
state.keyCol = 0;
state.pair = false;
state.pairStart = false;
/* document start */
if (stream.match('---')) {
return 'def';
}
/* document end */
if (stream.match('...')) {
return 'def';
}
/* array list item */
if (stream.match(/\s*-\s+/)) {
return 'meta';
}
}
/* inline pairs/lists */
if (stream.match(/^(\{|\}|\[|\])/)) {
if (ch == '{') state.inlinePairs++;
else if (ch == '}') state.inlinePairs--;
else if (ch == '[') state.inlineList++;
else state.inlineList--;
return 'meta';
}
/* list separator */
if (state.inlineList > 0 && !esc && ch == ',') {
stream.next();
return 'meta';
}
/* pairs separator */
if (state.inlinePairs > 0 && !esc && ch == ',') {
state.keyCol = 0;
state.pair = false;
state.pairStart = false;
stream.next();
return 'meta';
}
/* start of value of a pair */
if (state.pairStart) {
/* block literals */
if (stream.match(/^\s*(\||\>)\s*/)) {
state.literal = true;
return 'meta';
}
/* references */
if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) {
return 'variable-2';
}
/* numbers */
if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) {
return 'number';
}
if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) {
return 'number';
}
/* keywords */
if (stream.match(keywordRegex)) {
return 'keyword';
}
}
/* pairs (associative arrays) -> key */
if (
!state.pair
&& stream.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^\s,\[\]{}#&*!|>'"%@`])[^#:]*(?=:($|\s))/)
) {
state.pair = true;
state.keyCol = stream.indentation();
return 'atom';
}
if (state.pair && stream.match(/^:\s*/)) {
state.pairStart = true;
return 'meta';
}
/* nothing found, continue */
state.pairStart = false;
state.escaped = ch == '\\';
stream.next();
return null;
},
startState: function () {
return {
pair: false,
pairStart: false,
keyCol: 0,
inlinePairs: 0,
inlineList: 0,
literal: false,
escaped: false
};
},
lineComment: '#',
fold: 'indent'
};
});
cm.defineMIME('text/x-yaml', 'yaml');
cm.defineMIME('text/yaml', 'yaml');
};
export default yamlPlugin;

View File

@@ -0,0 +1,65 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
div.CodeMirror {
height: calc(100vh - 4rem);
background: ${(props) => props.theme.codemirror.bg};
border: solid 1px ${(props) => props.theme.codemirror.border};
font-family: ${(props) => (props.font ? props.font : 'default')};
line-break: anywhere;
}
.CodeMirror-dialog {
overflow: visible;
input {
background: transparent;
border: 1px solid #d3d6db;
outline: none;
border-radius: 0px;
}
}
.CodeMirror-overlayscroll-horizontal div,
.CodeMirror-overlayscroll-vertical div {
background: #d2d7db;
}
textarea.cm-editor {
position: relative;
}
// Todo: dark mode temporary fix
// Clean this
.CodeMirror.cm-s-monokai {
.CodeMirror-overlayscroll-horizontal div,
.CodeMirror-overlayscroll-vertical div {
background: #444444;
}
}
.cm-s-monokai span.cm-property,
.cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
.cm-s-monokai span.cm-string {
color: #ce9178 !important;
}
.cm-s-monokai span.cm-number {
color: #b5cea8 !important;
}
.cm-s-monokai span.cm-atom {
color: #569cd6 !important;
}
.cm-variable-valid {
color: ${(props) => props.theme.codemirror.variable.valid};
}
.cm-variable-invalid {
color: ${(props) => props.theme.codemirror.variable.invalid};
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) 2021 GraphQL Contributors.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import StyledWrapper from './StyledWrapper';
import yamlPlugin from './Plugins/Yaml/index';
let CodeMirror;
const SERVER_RENDERED = typeof window === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
if (!SERVER_RENDERED) {
CodeMirror = require('codemirror');
}
export default class CodeEditor extends React.Component {
constructor(props) {
super(props);
this.cachedValue = props.value || '';
this.variables = {};
this.lintOptions = {
esversion: 11,
expr: true,
asi: true
};
}
componentWillMount() {
switch (this.props.mode) {
case 'yaml':
// YAML linting and hightlighting plugin
yamlPlugin(CodeMirror);
break;
default:
break;
}
}
componentDidMount() {
const editor = (this.editor = CodeMirror(this._node, {
value: this.props.value || '',
lineNumbers: true,
lineWrapping: true,
tabSize: 2,
mode: this.props.mode || 'application/text',
keyMap: 'sublime',
autoCloseBrackets: true,
matchBrackets: true,
showCursorWhenSelecting: true,
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
lint: this.lintOptions,
readOnly: this.props.readOnly,
scrollbarStyle: 'overlay',
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
extraKeys: {
'Cmd-S': () => {
if (this.props.onSave) {
this.props.onSave();
}
},
'Ctrl-S': () => {
if (this.props.onSave) {
this.props.onSave();
}
},
'Cmd-F': 'findPersistent',
'Ctrl-F': 'findPersistent',
'Cmd-H': 'replace',
'Ctrl-H': 'replace',
'Tab': function (cm) {
cm.getSelection().includes('\n') || editor.getLine(cm.getCursor().line) == cm.getSelection()
? cm.execCommand('indentMore')
: cm.replaceSelection(' ', 'end');
},
'Shift-Tab': 'indentLess',
'Ctrl-Space': 'autocomplete',
'Cmd-Space': 'autocomplete',
'Ctrl-Y': 'foldAll',
'Cmd-Y': 'foldAll',
'Ctrl-I': 'unfoldAll',
'Cmd-I': 'unfoldAll'
}
}));
if (editor) {
editor.setOption('lint', this.props.mode && editor.getValue().trim().length > 0 ? this.lintOptions : false);
editor.on('change', this._onEdit);
}
}
componentDidUpdate(prevProps) {
this.ignoreChangeEvent = true;
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue && this.editor) {
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
}
if (this.props.theme !== prevProps.theme && this.editor) {
this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default');
}
this.ignoreChangeEvent = false;
}
componentWillUnmount() {
if (this.editor) {
this.editor.off('change', this._onEdit);
this.editor = null;
}
}
render() {
if (this.editor) {
this.editor.refresh();
}
return (
<StyledWrapper
className="h-full w-full graphiql-container"
aria-label="Code Editor"
font={this.props.font}
ref={(node) => {
this._node = node;
}}
/>
);
}
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {
this.editor.setOption('lint', this.editor.getValue().trim().length > 0 ? this.lintOptions : false);
this.cachedValue = this.editor.getValue();
if (this.props.onEdit) {
this.props.onEdit(this.cachedValue);
}
}
};
}

View File

@@ -0,0 +1,51 @@
import get from 'lodash/get';
import { useTheme } from 'providers/Theme';
import { useDispatch, useSelector } from 'react-redux';
import CodeEditor from './CodeEditor/index';
import { IconDeviceFloppy } from '@tabler/icons';
import { saveApiSpecToFile } from 'providers/ReduxStore/slices/apiSpec';
import { useState } from 'react';
const FileEditor = ({ apiSpec }) => {
const dispatch = useDispatch();
const { displayedTheme, theme } = useTheme();
const preferences = useSelector((state) => state.app.preferences);
const [content, setContent] = useState(apiSpec?.raw);
const onEdit = (value) => {
setContent(value);
};
const onSave = () => {
dispatch(saveApiSpecToFile({ uid: apiSpec?.uid, content }));
};
const hasChanges = Boolean(content != apiSpec?.raw);
const editorMode = 'yaml';
return (
<div className="flex flex-grow relative">
<CodeEditor
theme={displayedTheme}
value={content}
onEdit={onEdit}
onSave={onSave}
mode={editorMode}
font={get(preferences, 'font.codeFont', 'default')}
/>
<IconDeviceFloppy
onClick={onSave}
color={hasChanges ? theme.draftColor : theme.requestTabs.icon.color}
strokeWidth={1.5}
size={22}
className={`absolute right-0 top-0 m-4 ${
hasChanges ? 'cursor-pointer oapcity-100' : 'cursor-default opacity-50'
}`}
/>
</div>
);
};
export default FileEditor;

View File

@@ -0,0 +1,19 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.swagger-root {
height: calc(100vh - 4rem);
border: solid 1px ${(props) => props.theme.codemirror.border};
&.dark {
.swagger-ui {
filter: invert(88%) hue-rotate(180deg);
}
.swagger-ui .microlight {
filter: invert(100%) hue-rotate(180deg);
}
}
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,19 @@
import SwaggerUI from 'swagger-ui-react';
import StyledWrapper from './StyledWrapper';
import { useTheme } from 'providers/Theme';
const Swagger = ({ string }) => {
const { displayedTheme } = useTheme();
console.log('string', string);
return (
<StyledWrapper>
<div className={`swagger-root w-full overflow-y-scroll ${displayedTheme}`}>
<SwaggerUI spec={string} />
</div>
</StyledWrapper>
);
};
export default Swagger;

View File

@@ -0,0 +1,22 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
.menu-icon {
cursor: pointer;
color: ${(props) => props.theme.sidebar.dropdownIcon.color};
}
div.dropdown-item.menu-item {
color: ${(props) => props.theme.colors.text.danger};
&:hover {
background-color: ${(props) => props.theme.colors.bg.danger};
color: white;
}
}
.react-tooltip {
z-index: 10;
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,97 @@
import React, { forwardRef, useRef } from 'react';
import find from 'lodash/find';
import { useSelector, useDispatch } from 'react-redux';
import { IconFileCode, IconDots } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
import FileEditor from './FileEditor';
import Dropdown from 'components/Dropdown';
import { openApiSpec } from 'providers/ReduxStore/slices/apiSpec';
import { useState } from 'react';
import CreateApiSpec from 'components/Sidebar/ApiSpecs/CreateApiSpec';
import { Suspense } from 'react';
import Swagger from './Renderers/Swagger';
import toast from 'react-hot-toast';
const ApiSpecPanel = () => {
const dispatch = useDispatch();
const [createApiSpecModalOpen, setCreateApiSpecModalOpen] = useState(false);
const { apiSpecs, activeApiSpecUid } = useSelector((state) => state.apiSpec);
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
let apiSpec = find(apiSpecs, (c) => c.uid === activeApiSpecUid);
const { filename, pathname, raw, uid } = apiSpec || {};
if (!uid) {
return <div className="p-4 opacity-50">API Spec not found!</div>;
}
const MenuIcon = forwardRef((props, ref) => {
return (
<div ref={ref}>
<IconDots size={22} />
</div>
);
});
const handleOpenApiSpec = () => {
dispatch(openApiSpec()).catch(
(err) => console.log(err) && toast.error('An error occurred while opening the API spec')
);
};
return (
<StyledWrapper className="flex flex-col flex-grow relative">
{createApiSpecModalOpen ? <CreateApiSpec onClose={() => setCreateApiSpecModalOpen(false)} /> : null}
<div className="p-3 mb-2 w-full flex flex-row justify-between grid grid-cols-3">
<div className="flex flex-row justify-start gap-x-4 col-span-1">
<div className="flex w-fit items-center cursor-pointer">
<IconFileCode size={18} strokeWidth={1.5} />
<span className="ml-2 mr-4 font-semibold">API Designer</span>
</div>
</div>
<div className="w-full col-span-1 flex justify-center" title={pathname}>
{filename}
</div>
<div className="menu-icon pr-2 col-span-1 flex justify-end">
<Dropdown onCreate={onDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setCreateApiSpecModalOpen(true);
}}
>
Create API Spec
</div>
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
handleOpenApiSpec();
}}
>
Open API Spec
</div>
</Dropdown>
</div>
</div>
<section className="main flex flex-grow px-4 relative">
<div className="w-full grid grid-cols-2">
<div className="col-span-1">
<FileEditor apiSpec={apiSpec} />
</div>
<div className="col-span-1">
<Suspense fallback="">
<Swagger string={raw} />
</Suspense>
</div>
</div>
</section>
</StyledWrapper>
);
};
export default ApiSpecPanel;

View File

@@ -0,0 +1,15 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
display: flex;
align-items: center;
height: 100%;
-webkit-app-region: no-drag;
.shortcut {
font-size: 11px;
color: ${(props) => props.theme.dropdown.mutedText};
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,154 @@
import React, { useState } from 'react';
import { IconMenu2 } from '@tabler/icons';
import MenuDropdown from 'ui/MenuDropdown';
import ActionIcon from 'ui/ActionIcon';
import StyledWrapper from './StyledWrapper';
const AppMenu = () => {
const [isOpen, setIsOpen] = useState(false);
const { ipcRenderer } = window;
const menuItems = [
{
id: 'file',
label: 'File',
submenu: [
{
id: 'open-collection',
label: 'Open Collection',
onClick: () => ipcRenderer?.invoke('renderer:open-collection')
},
{ type: 'divider', id: 'file-div-1' },
{
id: 'preferences',
label: 'Preferences',
rightSection: <span className="shortcut">Ctrl+,</span>,
onClick: () => ipcRenderer?.invoke('renderer:open-preferences')
},
{ type: 'divider', id: 'file-div-2' },
{
id: 'quit',
label: 'Quit',
rightSection: <span className="shortcut">Alt+F4</span>,
onClick: () => ipcRenderer?.send('renderer:window-close')
}
]
},
{
id: 'edit',
label: 'Edit',
submenu: [
{
id: 'undo',
label: 'Undo',
rightSection: <span className="shortcut">Ctrl+Z</span>,
onClick: () => document.execCommand('undo')
},
{
id: 'redo',
label: 'Redo',
rightSection: <span className="shortcut">Ctrl+Y</span>,
onClick: () => document.execCommand('redo')
},
{ type: 'divider', id: 'edit-div-1' },
{
id: 'cut',
label: 'Cut',
rightSection: <span className="shortcut">Ctrl+X</span>,
onClick: () => document.execCommand('cut')
},
{
id: 'copy',
label: 'Copy',
rightSection: <span className="shortcut">Ctrl+C</span>,
onClick: () => document.execCommand('copy')
},
{
id: 'paste',
label: 'Paste',
rightSection: <span className="shortcut">Ctrl+V</span>,
onClick: () => document.execCommand('paste')
},
{ type: 'divider', id: 'edit-div-2' },
{
id: 'select-all',
label: 'Select All',
rightSection: <span className="shortcut">Ctrl+A</span>,
onClick: () => document.execCommand('selectAll')
}
]
},
{
id: 'view',
label: 'View',
submenu: [
{
id: 'toggle-devtools',
label: 'Developer Tools',
rightSection: <span className="shortcut">Ctrl+Shift+I</span>,
onClick: () => ipcRenderer?.invoke('renderer:toggle-devtools')
},
{ type: 'divider', id: 'view-div-1' },
{
id: 'reset-zoom',
label: 'Reset Zoom',
rightSection: <span className="shortcut">Ctrl+0</span>,
onClick: () => ipcRenderer?.invoke('renderer:reset-zoom')
},
{
id: 'zoom-in',
label: 'Zoom In',
rightSection: <span className="shortcut">Ctrl++</span>,
onClick: () => ipcRenderer?.invoke('renderer:zoom-in')
},
{
id: 'zoom-out',
label: 'Zoom Out',
rightSection: <span className="shortcut">Ctrl+-</span>,
onClick: () => ipcRenderer?.invoke('renderer:zoom-out')
},
{ type: 'divider', id: 'view-div-2' },
{
id: 'toggle-fullscreen',
label: 'Full Screen',
rightSection: <span className="shortcut">F11</span>,
onClick: () => ipcRenderer?.invoke('renderer:toggle-fullscreen')
}
]
},
{
id: 'help',
label: 'Help',
submenu: [
{
id: 'about',
label: 'About Bruno',
onClick: () => ipcRenderer?.invoke('renderer:open-about')
},
{
id: 'documentation',
label: 'Documentation',
onClick: () => ipcRenderer?.invoke('renderer:open-docs')
}
]
}
];
return (
<StyledWrapper>
<MenuDropdown
opened={isOpen}
onChange={setIsOpen}
placement="bottom-start"
showTickMark={false}
items={menuItems}
>
<ActionIcon label="Menu" size="lg">
<IconMenu2 size={16} stroke={1.5} />
</ActionIcon>
</MenuDropdown>
</StyledWrapper>
);
};
export default AppMenu;

View File

@@ -0,0 +1,255 @@
import styled from 'styled-components';
const Wrapper = styled.div`
height: 36px;
display: flex;
align-items: center;
background: ${(props) => props.theme.sidebar.bg};
-webkit-app-region: drag;
user-select: none;
.titlebar-content {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
padding: 0 12px;
padding-left: 70px; /* Space for macOS window controls */
transition: padding-left 0.15s ease;
}
/* When in full screen, no traffic lights so reduce padding */
&.fullscreen .titlebar-content {
padding-left: 6px;
}
/* Remove drag region from interactive elements */
.workspace-name-container,
.dropdown-item,
.home-button,
.dropdown,
button {
-webkit-app-region: no-drag;
}
/* Left section */
.titlebar-left {
display: flex;
align-items: center;
flex-shrink: 0;
margin-left: 10px;
-webkit-app-region: no-drag;
}
/* When in full screen, no traffic lights so remove margin-left */
&.fullscreen .titlebar-left {
margin-left: 0px;
}
/* Workspace Name Dropdown Trigger */
.workspace-name-container {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s ease;
&:hover {
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
}
.workspace-name {
font-size: 13px;
font-weight: 500;
color: ${(props) => props.theme.sidebar.color};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.chevron-icon {
flex-shrink: 0;
color: ${(props) => props.theme.sidebar.muted};
transition: transform 0.2s ease;
}
}
/* Center section - Bruno branding */
.titlebar-center {
position: absolute;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 6px;
pointer-events: none;
.bruno-text {
font-size: 13px;
font-weight: 600;
color: ${(props) => props.theme.text};
letter-spacing: 0.5px;
}
}
/* Right section */
.titlebar-right {
display: flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
-webkit-app-region: no-drag;
}
/* App action buttons container */
.titlebar-actions {
display: flex;
align-items: center;
}
/* Workspace Dropdown Styles */
.workspace-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 10px !important;
margin: 0 !important;
&.active {
.check-icon {
opacity: 1;
}
}
&:hover {
.pin-btn:not(.pinned) {
opacity: 1;
}
}
.workspace-name {
flex: 1;
min-width: 0;
font-size: 13px;
font-weight: 400;
color: ${(props) => props.theme.dropdown.color};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.workspace-actions {
display: flex;
align-items: center;
gap: 4px;
margin-left: 8px;
flex-shrink: 0;
pointer-events: none;
> * {
pointer-events: auto;
}
}
.check-icon {
color: ${(props) => props.theme.workspace?.accent || props.theme.colors?.text?.yellow};
flex-shrink: 0;
}
.pin-btn {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
border: none;
background: transparent;
border-radius: 4px;
cursor: pointer;
color: ${(props) => props.theme.dropdown.mutedText};
transition: background 0.15s ease, color 0.15s ease, opacity 0.15s ease;
opacity: 0;
&.pinned {
opacity: 1;
}
&:hover {
background: ${(props) => props.theme.dropdown.hoverBg};
color: ${(props) => props.theme.dropdown.mutedText};
}
}
}
/* Adjust for non-macOS platforms */
&:not(.os-mac) .titlebar-content {
padding-left: 12px;
}
/* Windows-specific styles */
&.os-windows .titlebar-content {
padding-right: 0px;
padding-left: 0px;
}
&.os-windows .titlebar-left {
margin-left: 6px;
}
&.os-linux .titlebar-content {
padding-right: 0px;
padding-left: 0px;
}
&.os-linux .titlebar-left {
margin-left: 6px;
}
.app-menu {
margin-left: 8px;
}
/* Custom window control buttons for Windows - always interactive, above modal overlay */
.window-controls {
display: flex;
align-items: stretch;
height: 36px;
margin-left: 8px;
position: relative;
z-index: 1000;
}
.window-control-btn {
display: flex;
align-items: center;
justify-content: center;
width: 46px;
height: 100%;
border: none;
background: transparent;
color: ${(props) => props.theme.text};
cursor: pointer;
transition: background-color 0.1s ease;
-webkit-app-region: no-drag;
&:hover {
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
}
&:active {
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
}
&.close:hover {
background: #e81123;
color: white;
}
}
`;
export default Wrapper;

View File

@@ -0,0 +1,332 @@
import React from 'react';
import { IconCheck, IconChevronDown, IconFolder, IconHome, IconPin, IconPinned, IconPlus, IconDownload, IconSettings, IconMinus, IconSquare, IconX, IconCopy } from '@tabler/icons';
import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
import toast from 'react-hot-toast';
import { useDispatch, useSelector } from 'react-redux';
import { savePreferences, showHomePage, showManageWorkspacePage, toggleSidebarCollapse } from 'providers/ReduxStore/slices/app';
import { closeConsole, openConsole } from 'providers/ReduxStore/slices/logs';
import { openWorkspaceDialog, switchWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
import { sortWorkspaces, toggleWorkspacePin } from 'utils/workspaces';
import Bruno from 'components/Bruno';
import MenuDropdown from 'ui/MenuDropdown';
import ActionIcon from 'ui/ActionIcon';
import IconSidebarToggle from 'components/Icons/IconSidebarToggle';
import CreateWorkspace from 'components/WorkspaceSidebar/CreateWorkspace';
import ImportWorkspace from 'components/WorkspaceSidebar/ImportWorkspace';
import IconBottombarToggle from 'components/Icons/IconBottombarToggle/index';
import AppMenu from './AppMenu';
import StyledWrapper from './StyledWrapper';
import ResponseLayoutToggle from 'components/ResponsePane/ResponseLayoutToggle';
import { isMacOS, isWindowsOS, isLinuxOS } from 'utils/common/platform';
import classNames from 'classnames';
const getOsClass = () => {
if (isMacOS()) return 'os-mac';
if (isWindowsOS()) return 'os-windows';
if (isLinuxOS()) return 'os-linux';
return 'os-other';
};
// Helper to get display name for workspace
export const getWorkspaceDisplayName = (name) => {
if (!name) return 'Untitled Workspace';
return name;
};
const AppTitleBar = () => {
const dispatch = useDispatch();
const [isFullScreen, setIsFullScreen] = useState(false);
const [isMaximized, setIsMaximized] = useState(false);
const osClass = getOsClass();
const isWindows = osClass === 'os-windows';
const isLinux = osClass === 'os-linux';
const showWindowControls = isWindows || isLinux;
// Listen for fullscreen changes
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(() => {
if (!showWindowControls) return;
const { ipcRenderer } = window;
if (!ipcRenderer) return;
ipcRenderer.invoke('renderer:window-is-maximized')
.then((maximized) => {
setIsMaximized(maximized);
})
.catch((error) => {
console.error('Error getting initial maximized state:', error);
});
const removeMaximizedListener = ipcRenderer.on('main:window-maximized', () => {
setIsMaximized(true);
});
const removeUnmaximizedListener = ipcRenderer.on('main:window-unmaximized', () => {
setIsMaximized(false);
});
return () => {
removeMaximizedListener();
removeUnmaximizedListener();
};
}, [showWindowControls]);
const handleMinimize = useCallback(() => {
window.ipcRenderer?.send('renderer:window-minimize');
}, []);
const handleMaximize = useCallback(() => {
window.ipcRenderer?.send('renderer:window-maximize');
// State will be updated via IPC events from main process (main:window-maximized/main:window-unmaximized)
}, []);
const handleClose = useCallback(() => {
window.ipcRenderer?.send('renderer:window-close');
}, []);
// Get workspace info
const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces);
const preferences = useSelector((state) => state.app.preferences);
const sidebarCollapsed = useSelector((state) => state.app.sidebarCollapsed);
const isConsoleOpen = useSelector((state) => state.logs.isConsoleOpen);
const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
// Sort workspaces according to preferences
const sortedWorkspaces = useMemo(() => {
return sortWorkspaces(workspaces, preferences);
}, [workspaces, preferences]);
const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false);
const [importWorkspaceModalOpen, setImportWorkspaceModalOpen] = useState(false);
const WorkspaceName = forwardRef((props, ref) => {
return (
<div ref={ref} className="workspace-name-container" {...props}>
<span data-testid="workspace-name" className={classNames('workspace-name', { 'italic text-muted': !activeWorkspace?.name })}>{getWorkspaceDisplayName(activeWorkspace?.name)}</span>
<IconChevronDown size={14} stroke={1.5} className="chevron-icon" />
</div>
);
});
const handleHomeClick = () => {
dispatch(showHomePage());
};
const handleWorkspaceSwitch = (workspaceUid) => {
dispatch(switchWorkspace(workspaceUid));
toast.success(`Switched to ${getWorkspaceDisplayName(workspaces.find((w) => w.uid === workspaceUid)?.name)}`);
};
const handleOpenWorkspace = async () => {
try {
await dispatch(openWorkspaceDialog());
toast.success('Workspace opened successfully');
} catch (error) {
toast.error(error.message || 'Failed to open workspace');
}
};
const handleCreateWorkspace = () => {
setCreateWorkspaceModalOpen(true);
};
const handleManageWorkspaces = () => {
dispatch(showManageWorkspacePage());
};
const handleImportWorkspace = () => {
setImportWorkspaceModalOpen(true);
};
const handlePinWorkspace = useCallback((workspaceUid, e) => {
e.preventDefault();
e.stopPropagation();
const newPreferences = toggleWorkspacePin(workspaceUid, preferences);
dispatch(savePreferences(newPreferences));
}, [dispatch, preferences]);
const handleToggleSidebar = () => {
dispatch(toggleSidebarCollapse());
};
const handleToggleDevtools = () => {
if (isConsoleOpen) {
dispatch(closeConsole());
} else {
dispatch(openConsole());
}
};
// Build workspace menu items
const workspaceMenuItems = useMemo(() => {
const items = sortedWorkspaces.map((workspace) => {
const isActive = workspace.uid === activeWorkspaceUid;
const isPinned = preferences?.workspaces?.pinnedWorkspaceUids?.includes(workspace.uid);
return {
id: workspace.uid,
label: getWorkspaceDisplayName(workspace.name),
onClick: () => handleWorkspaceSwitch(workspace.uid),
className: `workspace-item ${isActive ? 'active' : ''}`,
rightSection: (
<div className="workspace-actions">
{workspace.type !== 'default' && (
<ActionIcon
className={`pin-btn ${isPinned ? 'pinned' : ''}`}
onClick={(e) => handlePinWorkspace(workspace.uid, e)}
label={isPinned ? 'Unpin workspace' : 'Pin workspace'}
size="sm"
>
{isPinned ? <IconPinned size={14} stroke={1.5} /> : <IconPin size={14} stroke={1.5} />}
</ActionIcon>
)}
{isActive && <IconCheck size={16} stroke={1.5} className="check-icon" />}
</div>
)
};
});
// Add label and action items
items.push(
{ type: 'label', label: 'Workspaces' },
{
id: 'create-workspace',
leftSection: IconPlus,
label: 'Create workspace',
onClick: handleCreateWorkspace
},
{
id: 'open-workspace',
leftSection: IconFolder,
label: 'Open workspace',
onClick: handleOpenWorkspace
},
{
id: 'import-workspace',
leftSection: IconDownload,
label: 'Import workspace',
onClick: handleImportWorkspace
},
{
id: 'manage-workspaces',
leftSection: IconSettings,
label: 'Manage workspaces',
onClick: handleManageWorkspaces
}
);
return items;
}, [sortedWorkspaces, activeWorkspaceUid, preferences, handlePinWorkspace]);
return (
<StyledWrapper className={`app-titlebar ${osClass} ${isFullScreen ? 'fullscreen' : ''}`}>
{createWorkspaceModalOpen && (
<CreateWorkspace onClose={() => setCreateWorkspaceModalOpen(false)} />
)}
{importWorkspaceModalOpen && (
<ImportWorkspace onClose={() => setImportWorkspaceModalOpen(false)} />
)}
<div className="titlebar-content">
<div className="titlebar-left">
{showWindowControls && <AppMenu />}
<ActionIcon onClick={handleHomeClick} label="Home" size="lg" className="home-button">
<IconHome size={16} stroke={1.5} />
</ActionIcon>
{/* Workspace Dropdown */}
<MenuDropdown
data-testid="workspace-menu"
items={workspaceMenuItems}
placement="bottom-start"
selectedItemId={activeWorkspaceUid}
>
<WorkspaceName />
</MenuDropdown>
</div>
{/* Center section: Bruno logo + text */}
<div className="titlebar-center">
<Bruno width={18} />
<span className="bruno-text">Bruno</span>
</div>
{/* Right section: Action buttons */}
<div className="titlebar-right">
<div className="titlebar-actions">
{/* Toggle sidebar */}
<ActionIcon
onClick={handleToggleSidebar}
label={sidebarCollapsed ? 'Show sidebar' : 'Hide sidebar'}
size="lg"
data-testid="toggle-sidebar-button"
>
<IconSidebarToggle collapsed={sidebarCollapsed} size={16} strokeWidth={1.5} />
</ActionIcon>
{/* Toggle devtools */}
<ActionIcon
onClick={handleToggleDevtools}
label={isConsoleOpen ? 'Hide devtools' : 'Show devtools'}
size="lg"
data-testid="toggle-devtools-button"
>
<IconBottombarToggle collapsed={!isConsoleOpen} size={16} strokeWidth={1.5} />
</ActionIcon>
<ResponseLayoutToggle />
</div>
{showWindowControls && (
<div className="window-controls">
<button
className="window-control-btn minimize"
onClick={handleMinimize}
aria-label="Minimize"
>
<IconMinus size={16} stroke={1} />
</button>
<button
className="window-control-btn maximize"
onClick={handleMaximize}
aria-label={isMaximized ? 'Restore' : 'Maximize'}
>
{isMaximized ? <IconCopy size={14} stroke={1} /> : <IconSquare size={14} stroke={1} />}
</button>
<button
className="window-control-btn close"
onClick={handleClose}
aria-label="Close"
>
<IconX size={16} stroke={1} />
</button>
</div>
)}
</div>
</div>
</StyledWrapper>
);
};
export default AppTitleBar;

View File

@@ -0,0 +1,38 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
font-size: ${(props) => props.theme.font.size.base};
.body-mode-selector {
background: transparent;
border-radius: 3px;
.dropdown-item {
padding: 0.2rem 0.6rem !important;
padding-left: 1.5rem !important;
display: flex;
align-items: center;
}
.label-item {
padding: 0.2rem 0.6rem !important;
}
.selected-body-mode {
color: ${(props) => props.theme.primary.text};
}
.dropdown-icon {
display: flex;
align-items: center;
margin-right: 0.5rem;
}
}
.caret {
color: ${(props) => props.theme.colors.text.muted};
fill: ${(props) => props.theme.colors.text.muted};
}
`;
export default StyledWrapper;

View File

@@ -1,17 +1,33 @@
import React, { useRef, forwardRef } from 'react';
import { IconCaretDown } from '@tabler/icons';
import Dropdown from 'components/Dropdown';
import React, { useMemo } from 'react';
import { IconCaretDown, IconForms, IconBraces, IconCode, IconFileText, IconDatabase, IconFile, IconX } from '@tabler/icons';
import MenuDropdown from 'ui/MenuDropdown';
import { humanizeRequestBodyMode } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
const DEFAULT_MODES = [
{ key: 'multipartForm', label: 'Multipart Form', category: 'Form' },
{ key: 'formUrlEncoded', label: 'Form URL Encoded', category: 'Form' },
{ key: 'json', label: 'JSON', category: 'Raw' },
{ key: 'xml', label: 'XML', category: 'Raw' },
{ key: 'text', label: 'TEXT', category: 'Raw' },
{ key: 'sparql', label: 'SPARQL', category: 'Raw' },
{ key: 'file', label: 'File / Binary', category: 'Other' },
{ key: 'none', label: 'None', category: 'Other' }
{
name: 'Form',
options: [
{ id: 'multipartForm', label: 'Multipart Form', leftSection: IconForms },
{ id: 'formUrlEncoded', label: 'Form URL Encoded', leftSection: IconForms }
]
},
{
name: 'Raw',
options: [
{ id: 'json', label: 'JSON', leftSection: IconBraces },
{ id: 'xml', label: 'XML', leftSection: IconCode },
{ id: 'text', label: 'TEXT', leftSection: IconFileText },
{ id: 'sparql', label: 'SPARQL', leftSection: IconDatabase }
]
},
{
name: 'Other',
options: [
{ id: 'file', label: 'File / Binary', leftSection: IconFile },
{ id: 'none', label: 'No Body', leftSection: IconX }
]
}
];
const BodyModeSelector = ({
@@ -21,62 +37,39 @@ const BodyModeSelector = ({
disabled = false,
className = '',
wrapperClassName = '',
showCategories = true,
placement = 'bottom-end'
}) => {
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center pl-3 py-1 select-none selected-body-mode">
{humanizeRequestBodyMode(currentMode)}
{' '}
<IconCaretDown className="caret ml-2" size={14} strokeWidth={2} />
</div>
);
});
const onModeSelect = (mode) => {
dropdownTippyRef.current.hide();
onModeChange(mode);
};
// Group modes by category for rendering
const groupedModes = modes.reduce((acc, mode) => {
const category = mode.category || 'Other';
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(mode);
return acc;
}, {});
// Add onClick handlers to mode options
const menuItems = useMemo(() => {
return modes.map((group) => ({
...group,
options: group.options.map((option) => ({
...option,
onClick: () => onModeChange(option.id)
}))
}));
}, [modes, onModeChange]);
return (
<div className={`inline-flex items-center body-mode-selector ${disabled ? 'cursor-default' : 'cursor-pointer'} ${wrapperClassName}`}>
<Dropdown
onCreate={onDropdownCreate}
icon={<Icon />}
placement={placement}
disabled={disabled}
className={className}
>
{Object.entries(groupedModes).map(([category, categoryModes]) => (
<React.Fragment key={category}>
{showCategories && <div className="label-item font-medium">{category}</div>}
{categoryModes.map((mode) => (
<div
key={mode.key}
className="dropdown-item"
onClick={() => onModeSelect(mode.key)}
>
{mode.label}
</div>
))}
</React.Fragment>
))}
</Dropdown>
</div>
<StyledWrapper className={wrapperClassName}>
<div className={`inline-flex items-center body-mode-selector ${disabled ? 'cursor-default' : 'cursor-pointer'}`}>
<MenuDropdown
items={menuItems}
placement={placement}
disabled={disabled}
className={className}
selectedItemId={currentMode}
showGroupDividers={false}
groupStyle="select"
>
<div className="flex items-center justify-center pl-3 py-1 select-none selected-body-mode">
{humanizeRequestBodyMode(currentMode)}
{' '}
<IconCaretDown className="caret ml-2" size={14} strokeWidth={2} />
</div>
</MenuDropdown>
</div>
</StyledWrapper>
);
};

View File

@@ -6,7 +6,7 @@ import StyledWrapper from './StyledWrapper';
const BrunoSupport = ({ onClose }) => {
return (
<StyledWrapper>
<Modal size="sm" title={'Support'} handleCancel={onClose} hideFooter={true}>
<Modal size="sm" title="Support" handleCancel={onClose} hideFooter={true}>
<div className="collection-options">
<div className="mt-2">
<a href="https://docs.usebruno.com" target="_blank" className="flex items-end">

View File

@@ -1,4 +1,5 @@
import React, { useMemo } from 'react';
import get from 'lodash/get';
import CodeEditor from 'components/CodeEditor';
import { useTheme } from 'providers/Theme';
import { useSelector } from 'react-redux';
@@ -21,7 +22,8 @@ const BulkEditor = ({ params, onChange, onToggle, onSave, onRun }) => {
<CodeEditor
mode="text/plain"
theme={displayedTheme}
font={preferences.codeFont || 'default'}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
value={parsedParams}
onEdit={handleEdit}
onSave={onSave}

View File

@@ -18,6 +18,33 @@ const StyledWrapper = styled.div`
flex-direction: column-reverse;
}
.CodeMirror-placeholder {
color: ${(props) => props.theme.text} !important;
opacity: 0.5 !important;
}
.CodeMirror-linenumber {
text-align: left !important;
padding-left: 3px !important;
}
/* Override default lint highlight background when emphasizing the gutter */
.CodeMirror-lint-line-error,
.CodeMirror-lint-line-warning {
background: none !important;
}
/* Style line numbers when there's a lint issue */
.CodeMirror-lint-line-error .CodeMirror-linenumber {
color: ${(props) => props.theme.colors.text.danger} !important;
text-decoration: underline;
}
.CodeMirror-lint-line-warning .CodeMirror-linenumber {
color: ${(props) => props.theme.colors.text.warning} !important;
text-decoration: underline;
}
/* Removes the glow outline around the folded json */
.CodeMirror-foldmarker {
text-shadow: none;
@@ -73,41 +100,54 @@ const StyledWrapper = styled.div`
}
}
.cm-s-monokai span.cm-property,
.cm-s-monokai span.cm-attribute {
color: #9cdcfe !important;
}
.cm-s-monokai span.cm-string {
color: #ce9178 !important;
}
.cm-s-monokai span.cm-number {
color: #b5cea8 !important;
}
.cm-s-monokai span.cm-atom {
color: #569cd6 !important;
.cm-s-default, .cm-s-monokai {
span.cm-def {
color: ${(props) => props.theme.codemirror.tokens.definition} !important;
}
span.cm-property {
color: ${(props) => props.theme.codemirror.tokens.property} !important;
}
span.cm-string {
color: ${(props) => props.theme.codemirror.tokens.string} !important;
}
span.cm-number {
color: ${(props) => props.theme.codemirror.tokens.number} !important;
}
span.cm-atom {
color: ${(props) => props.theme.codemirror.tokens.atom} !important;
}
span.cm-variable, span.cm-variable-2 {
color: ${(props) => props.theme.codemirror.tokens.variable} !important;
}
span.cm-keyword {
color: ${(props) => props.theme.codemirror.tokens.keyword} !important;
}
span.cm-comment {
color: ${(props) => props.theme.codemirror.tokens.comment} !important;
}
span.cm-operator {
color: ${(props) => props.theme.codemirror.tokens.operator} !important;
}
span.cm-tag {
color: ${(props) => props.theme.codemirror.tokens.tag} !important;
}
span.cm-tag.cm-bracket {
color: ${(props) => props.theme.codemirror.tokens.tagBracket} !important;
}
}
/* Variable validation colors */
.cm-variable-valid {
color: green;
color: ${(props) => props.theme.codemirror.variable.valid} !important;
}
.cm-variable-invalid {
color: red;
color: ${(props) => props.theme.codemirror.variable.invalid} !important;
}
.CodeMirror-search-hint {
display: inline;
}
.cm-s-default span.cm-property {
color: #1f61a0 !important;
}
.cm-s-default span.cm-variable {
color: #397d13 !important;
}
//matching bracket fix
.CodeMirror-matchingbracket {
@@ -126,6 +166,31 @@ const StyledWrapper = styled.div`
.cm-search-current {
background: rgba(255, 193, 7, 0.4);
}
.lint-error-tooltip {
position: fixed;
z-index: 10000;
background: ${(props) => props.theme.codemirror.bg};
border-radius: ${(props) => props.theme.border.radius.base};
padding: 8px 12px;
max-width: 400px;
box-shadow: ${(props) => props.theme.shadow.sm};
font-size: ${(props) => props.theme.font.size.xs};
line-height: 1.5;
pointer-events: none;
.lint-tooltip-message {
padding: 2px 0;
}
.lint-tooltip-message.error {
color: ${(props) => props.theme.colors.text.danger};
}
.lint-tooltip-message.warning {
color: ${(props) => props.theme.colors.text.warning};
}
}
`;
export default StyledWrapper;

View File

@@ -5,16 +5,18 @@
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import React, { createRef } from 'react';
import { isEqual, escapeRegExp } from 'lodash';
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
import { setupAutoComplete } from 'utils/codemirror/autocomplete';
import { setupAutoComplete, showRootHints } from 'utils/codemirror/autocomplete';
import StyledWrapper from './StyledWrapper';
import * as jsonlint from '@prantlf/jsonlint';
import { JSHINT } from 'jshint';
import stripJsonComments from 'strip-json-comments';
import { getAllVariables } from 'utils/collections';
import CodeMirrorSearch from 'components/CodeMirrorSearch';
import { setupLinkAware } from 'utils/codemirror/linkAware';
import { setupLintErrorTooltip } from 'utils/codemirror/lint-errors';
import CodeMirrorSearch from 'components/CodeMirrorSearch/index';
const CodeMirror = require('codemirror');
window.jsonlint = jsonlint;
@@ -32,11 +34,13 @@ export default class CodeEditor extends React.Component {
this.cachedValue = props.value || '';
this.variables = {};
this.searchResultsCountElementId = 'search-results-count';
this.searchBarRef = createRef();
this.lintOptions = {
esversion: 11,
expr: true,
asi: true
asi: true,
highlightLines: true
};
this.state = {
@@ -49,19 +53,22 @@ export default class CodeEditor extends React.Component {
const editor = (this.editor = CodeMirror(this._node, {
value: this.props.value || '',
placeholder: '...',
lineNumbers: true,
lineWrapping: this.props.enableLineWrapping ?? true,
tabSize: TAB_SIZE,
mode: this.props.mode || 'application/ld+json',
brunoVarInfo: {
variables
},
brunoVarInfo: this.props.enableBrunoVarInfo !== false ? {
variables,
collection: this.props.collection,
item: this.props.item
} : false,
keyMap: 'sublime',
autoCloseBrackets: true,
matchBrackets: true,
showCursorWhenSelecting: true,
foldGutter: true,
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
lint: this.lintOptions,
readOnly: this.props.readOnly,
scrollbarStyle: 'overlay',
@@ -88,25 +95,29 @@ export default class CodeEditor extends React.Component {
}
},
'Cmd-F': (cm) => {
if (!this.state.searchBarVisible) {
this.setState({ searchBarVisible: true });
}
this.setState({ searchBarVisible: true }, () => {
this.searchBarRef.current?.focus();
});
},
'Ctrl-F': (cm) => {
if (!this.state.searchBarVisible) {
this.setState({ searchBarVisible: true });
}
this.setState({ searchBarVisible: true }, () => {
this.searchBarRef.current?.focus();
});
},
'Cmd-H': 'replace',
'Ctrl-H': 'replace',
Tab: function (cm) {
'Tab': function (cm) {
cm.getSelection().includes('\n') || editor.getLine(cm.getCursor().line) == cm.getSelection()
? cm.execCommand('indentMore')
: cm.replaceSelection(' ', 'end');
},
'Shift-Tab': 'indentLess',
'Ctrl-Space': 'autocomplete',
'Cmd-Space': 'autocomplete',
'Ctrl-Space': (cm) => {
showRootHints(cm, this.props.showHintsFor);
},
'Cmd-Space': (cm) => {
showRootHints(cm, this.props.showHintsFor);
},
'Ctrl-Y': 'foldAll',
'Cmd-Y': 'foldAll',
'Ctrl-I': 'unfoldAll',
@@ -145,7 +156,7 @@ export default class CodeEditor extends React.Component {
} else if (this.props.mode == 'application/xml') {
var doc = new DOMParser();
try {
//add header element and remove prefix namespaces for DOMParser
// add header element and remove prefix namespaces for DOMParser
var dcm = doc.parseFromString(
'<a> ' + internal.replace(/(?<=\<|<\/)\w+:/g, '') + '</a>',
'application/xml'
@@ -182,16 +193,15 @@ export default class CodeEditor extends React.Component {
}
return found;
});
if (editor) {
editor.setOption('lint', this.props.mode && editor.getValue().trim().length > 0 ? this.lintOptions : false);
editor.on('change', this._onEdit);
editor.on('scroll', this.onScroll);
editor.scrollTo(null, this.props.initialScroll);
this.addOverlay();
const getAllVariablesHandler = () => getAllVariables(this.props.collection, this.props.item);
// Setup AutoComplete Helper for all modes
const autoCompleteOptions = {
showHintsFor: this.props.showHintsFor,
@@ -202,6 +212,11 @@ export default class CodeEditor extends React.Component {
editor,
autoCompleteOptions
);
setupLinkAware(editor);
// Setup lint error tooltip on line number hover
this.cleanupLintErrorTooltip = setupLintErrorTooltip(editor);
}
}
@@ -218,8 +233,10 @@ export default class CodeEditor extends React.Component {
CodeMirror.signal(this.editor, 'change', this.editor);
}
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue && this.editor) {
const cursor = this.editor.getCursor();
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
this.editor.setCursor(cursor);
}
if (this.editor) {
@@ -227,6 +244,16 @@ export default class CodeEditor extends React.Component {
if (!isEqual(variables, this.variables)) {
this.addOverlay();
}
// Update collection and item when they change
if (this.props.enableBrunoVarInfo !== false && this.editor.options.brunoVarInfo) {
if (!isEqual(this.props.collection, this.editor.options.brunoVarInfo.collection)) {
this.editor.options.brunoVarInfo.collection = this.props.collection;
}
if (!isEqual(this.props.item, this.editor.options.brunoVarInfo.item)) {
this.editor.options.brunoVarInfo.item = this.props.item;
}
}
}
if (this.props.theme !== prevProps.theme && this.editor) {
@@ -254,8 +281,19 @@ export default class CodeEditor extends React.Component {
componentWillUnmount() {
if (this.editor) {
if (this.props.onScroll) {
this.props.onScroll(this.editor);
}
this.editor?._destroyLinkAware?.();
this.editor.off('change', this._onEdit);
this.editor.off('scroll', this.onScroll);
// Clean up lint error tooltip
this.cleanupLintErrorTooltip?.();
const wrapper = this.editor.getWrapperElement();
wrapper?.parentNode?.removeChild(wrapper);
this.editor = null;
}
}
@@ -272,6 +310,10 @@ export default class CodeEditor extends React.Component {
fontSize={this.props.fontSize}
>
<CodeMirrorSearch
ref={(node) => {
if (!node) return;
this.searchBarRef.current = node;
}}
visible={this.state.searchBarVisible}
editor={this.editor}
onClose={() => this.setState({ searchBarVisible: false })}
@@ -290,12 +332,15 @@ export default class CodeEditor extends React.Component {
let variables = getAllVariables(this.props.collection, this.props.item);
this.variables = variables;
// Update brunoVarInfo with latest variables
if (this.props.enableBrunoVarInfo !== false && this.editor.options.brunoVarInfo) {
this.editor.options.brunoVarInfo.variables = variables;
}
defineCodeMirrorBrunoVariablesMode(variables, mode, false, this.props.enableVariableHighlighting);
this.editor.setOption('mode', 'brunovariables');
};
onScroll = (event) => this.props.onScroll?.(event);
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {
this.editor.setOption('lint', this.editor.getValue().trim().length > 0 ? this.lintOptions : false);

View File

@@ -10,10 +10,10 @@ jest.mock('codemirror', () => {
const MOCK_THEME = {
codemirror: {
bg: "#1e1e1e",
border: "#333",
bg: '#1e1e1e',
border: '#333'
},
textLink: "#007acc",
textLink: '#007acc'
};
const setupEditorState = (editor, { value, cursorPosition }) => {
@@ -27,8 +27,8 @@ const setupEditorState = (editor, { value, cursorPosition }) => {
});
editor.state = {
completionActive: null,
}
completionActive: null
};
};
const setupEditorWithRef = () => {
@@ -47,5 +47,5 @@ describe('CodeEditor', () => {
jest.resetModules();
});
it("add CodeEditor related tests here", () => {});
});
it('add CodeEditor related tests here', () => {});
});

View File

@@ -1,4 +1,5 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
.bruno-search-bar {
@@ -9,15 +10,15 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
flex-wrap: nowrap;
padding: 0 2px;
min-height: 36px;
background: ${(props) => props.theme.sidebar.search.bg} !important;
border-radius: 4px;
border: 1px solid ${(props) => props.theme.sidebar.search.bg} !important;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
gap: 0;
padding: 1px 3px;
width: auto;
min-width: 180px;
max-width: 320px;
min-height: 22px;
background: ${(props) => props.theme.background.base};
color: ${(props) => props.theme.text.base};
border: solid 1px ${(props) => props.theme.border.border2};
border-radius: ${(props) => props.theme.border.radius.sm};
}
.bruno-search-bar input {
@@ -27,7 +28,7 @@ const StyledWrapper = styled.div`
border: none;
outline: none;
padding: 1px 2px;
font-size: 13px;
font-size: ${(props) => props.theme.font.size.base};
margin: 0 1px;
height: 28px;
}
@@ -38,7 +39,7 @@ const StyledWrapper = styled.div`
padding: 0 1px;
margin: 0 1px;
cursor: pointer;
color: #aaa;
color: ${(props) => props.theme.colors.text.subtext1};
border-radius: 3px;
height: 18px;
width: 18px;
@@ -50,31 +51,18 @@ const StyledWrapper = styled.div`
.searchbar-result-count {
min-width: 28px;
text-align: center;
font-size: 11px;
color: #aaa;
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.subtext1};
margin: 0 8px 0 1px;
white-space: nowrap;
}
.bruno-search-bar.compact {
background: ${(props) => props.theme.codemirror.bg};
color: ${(props) => props.theme.codemirror.text || props.theme.text};
border: none;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border-radius: 4px;
padding: 1px 3px;
min-height: 22px;
display: flex;
align-items: center;
gap: 0;
}
.bruno-search-bar input {
background: transparent;
color: inherit;
color: ${(props) => props.theme.colors.text.subtext2};
border: none;
outline: none;
font-size: 13px;
font-size: ${(props) => props.theme.font.size.base};
padding: 1px 2px;
min-width: 80px;
}
@@ -92,7 +80,9 @@ const StyledWrapper = styled.div`
}
.searchbar-icon-btn.active {
color: #f39c12 !important;
color: ${(props) => props.theme.brand};
background-color: ${(props) => rgba(props.theme.brand, 0.1)};
font-weight: 500;
}
`;

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import React, { useState, useEffect, useRef, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
import { IconRegex, IconArrowUp, IconArrowDown, IconX, IconLetterCase, IconLetterW } from '@tabler/icons';
import ToolHint from 'components/ToolHint';
import StyledWrapper from './StyledWrapper';
@@ -8,7 +8,7 @@ function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');
}
const CodeMirrorSearch = ({ visible, editor, onClose }) => {
const CodeMirrorSearch = forwardRef(({ visible, editor, onClose }, ref) => {
const [searchText, setSearchText] = useState('');
const [regex, setRegex] = useState(false);
const [caseSensitive, setCaseSensitive] = useState(false);
@@ -19,6 +19,7 @@ const CodeMirrorSearch = ({ visible, editor, onClose }) => {
const searchMarks = useRef([]);
const searchLineHighlight = useRef(null);
const searchMatches = useRef([]);
const inputRef = useRef(null);
const debouncedSearchText = useDebounce(searchText, 150);
@@ -106,6 +107,14 @@ const CodeMirrorSearch = ({ visible, editor, onClose }) => {
}
}, [debouncedSearchText, regex, caseSensitive, wholeWord, editor, memoizedMatches]);
useImperativeHandle(ref, () => ({
focus: () => {
if (inputRef.current) {
inputRef.current.focus();
}
}
}));
useEffect(() => {
doSearch(0, debouncedSearchText);
}, [debouncedSearchText, doSearch]);
@@ -166,8 +175,9 @@ const CodeMirrorSearch = ({ visible, editor, onClose }) => {
return (
<StyledWrapper>
<div className="bruno-search-bar compact">
<div className="bruno-search-bar">
<input
ref={inputRef}
autoFocus
type="text"
value={searchText}
@@ -196,6 +206,6 @@ const CodeMirrorSearch = ({ visible, editor, onClose }) => {
</div>
</StyledWrapper>
);
};
});
export default CodeMirrorSearch;

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {
@@ -13,7 +14,8 @@ const Wrapper = styled.div`
}
.auth-placement-selector {
padding: 0.5rem 0px;
font-size: ${(props) => props.theme.font.size.sm};
padding: 0.2rem 0px;
border-radius: 3px;
border: solid 1px ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.input.bg};
@@ -37,7 +39,6 @@ const Wrapper = styled.div`
.auth-type-label {
width: fit-content;
color: ${(props) => props.theme.colors.text.yellow};
justify-content: space-between;
padding: 0 0.5rem;
}

View File

@@ -6,7 +6,7 @@ import Dropdown from 'components/Dropdown';
import { useTheme } from 'providers/Theme';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import { humanizeRequestAPIKeyPlacement } from 'utils/collections';
@@ -16,9 +16,9 @@ const ApiKeyAuth = ({ collection }) => {
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const apikeyAuth = get(collection, 'root.request.auth.apikey', {});
const apikeyAuth = collection.draft?.root ? get(collection, 'draft.root.request.auth.apikey', {}) : get(collection, 'root.request.auth.apikey', {});
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const Icon = forwardRef((props, ref) => {
return (
@@ -43,43 +43,45 @@ const ApiKeyAuth = ({ collection }) => {
};
useEffect(() => {
!apikeyAuth?.placement &&
dispatch(
updateCollectionAuth({
mode: 'apikey',
collectionUid: collection.uid,
content: {
placement: 'header'
}
})
);
!apikeyAuth?.placement
&& dispatch(
updateCollectionAuth({
mode: 'apikey',
collectionUid: collection.uid,
content: {
placement: 'header'
}
})
);
}, [apikeyAuth]);
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Key</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Key</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={apikeyAuth.key || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAuthChange('key', val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Value</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Value</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={apikeyAuth.value || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAuthChange('value', val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Add To</label>
<label className="block mb-1">Add To</label>
<div className="inline-flex items-center cursor-pointer auth-placement-selector w-fit">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div

View File

@@ -1,27 +1,19 @@
import styled from 'styled-components';
const Wrapper = styled.div`
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.base};
.auth-mode-selector {
background: transparent;
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};
}
color: ${(props) => props.theme.primary.text};
.dropdown-item {
padding: 0.2rem 0.6rem !important;
.caret {
color: rgb(140, 140, 140);
fill: rgb(140, 140, 140);
}
}
.label-item {
padding: 0.2rem 0.6rem !important;
}
}
.caret {
color: rgb(140, 140, 140);
fill: rgb(140 140 140);
}
`;

View File

@@ -1,7 +1,7 @@
import React, { useRef, forwardRef } from 'react';
import React, { useMemo, useCallback } from 'react';
import get from 'lodash/get';
import { IconCaretDown } from '@tabler/icons';
import Dropdown from 'components/Dropdown';
import MenuDropdown from 'ui/MenuDropdown';
import { useDispatch } from 'react-redux';
import { updateCollectionAuthMode } from 'providers/ReduxStore/slices/collections';
import { humanizeRequestAuthMode } from 'utils/collections';
@@ -9,113 +9,77 @@ import StyledWrapper from './StyledWrapper';
const AuthMode = ({ collection }) => {
const dispatch = useDispatch();
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const authMode = get(collection, 'root.request.auth.mode');
const authMode = collection.draft?.root ? get(collection, 'draft.root.request.auth.mode') : get(collection, 'root.request.auth.mode');
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center auth-mode-label select-none">
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1 mr-1" size={14} strokeWidth={2} />
</div>
);
});
const onModeChange = (value) => {
const onModeChange = useCallback((value) => {
dispatch(
updateCollectionAuthMode({
collectionUid: collection.uid,
mode: value
})
);
};
}, [dispatch, collection.uid]);
const menuItems = useMemo(() => [
{
id: 'awsv4',
label: 'AWS Sig v4',
onClick: () => onModeChange('awsv4')
},
{
id: 'basic',
label: 'Basic Auth',
onClick: () => onModeChange('basic')
},
{
id: 'wsse',
label: 'WSSE Auth',
onClick: () => onModeChange('wsse')
},
{
id: 'bearer',
label: 'Bearer Token',
onClick: () => onModeChange('bearer')
},
{
id: 'digest',
label: 'Digest Auth',
onClick: () => onModeChange('digest')
},
{
id: 'ntlm',
label: 'NTLM Auth',
onClick: () => onModeChange('ntlm')
},
{
id: 'oauth2',
label: 'OAuth 2.0',
onClick: () => onModeChange('oauth2')
},
{
id: 'apikey',
label: 'API Key',
onClick: () => onModeChange('apikey')
},
{
id: 'none',
label: 'No Auth',
onClick: () => onModeChange('none')
}
], [onModeChange]);
return (
<StyledWrapper>
<div className="inline-flex items-center cursor-pointer auth-mode-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('awsv4');
}}
>
AWS Sig v4
<MenuDropdown
items={menuItems}
placement="bottom-end"
selectedItemId={authMode}
>
<div className="flex items-center justify-center auth-mode-label select-none">
{humanizeRequestAuthMode(authMode)} <IconCaretDown className="caret ml-1" size={14} strokeWidth={2} />
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('basic');
}}
>
Basic Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('wsse');
}}
>
WSSE Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('bearer');
}}
>
Bearer Token
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('digest');
}}
>
Digest Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('ntlm');
}}
>
NTLM Auth
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('oauth2');
}}
>
OAuth 2.0
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('apikey');
}}
>
API Key
</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('none');
}}
>
No Auth
</div>
</Dropdown>
</MenuDropdown>
</div>
</StyledWrapper>
);

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,18 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const AwsV4Auth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const awsv4Auth = get(collection, 'root.request.auth.awsv4', {});
const awsv4Auth = collection.draft?.root ? get(collection, 'draft.root.request.auth.awsv4', {}) : get(collection, 'root.request.auth.awsv4', {});
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(awsv4Auth?.secretAccessKey);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleAccessKeyIdChange = (accessKeyId) => {
dispatch(
@@ -123,19 +123,20 @@ const AwsV4Auth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Access Key ID</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Access Key ID</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={awsv4Auth.accessKeyId || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAccessKeyIdChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Secret Access Key</label>
<div className="single-line-editor-wrapper mb-2 flex items-center">
<label className="block mb-1">Secret Access Key</label>
<div className="single-line-editor-wrapper mb-3 flex items-center">
<SingleLineEditor
value={awsv4Auth.secretAccessKey || ''}
theme={storedTheme}
@@ -143,51 +144,56 @@ const AwsV4Auth = ({ collection }) => {
onChange={(val) => handleSecretAccessKeyChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="awsv4-secret-access-key" warningMessage={warningMessage} />}
</div>
<label className="block font-medium mb-2">Session Token</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Session Token</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={awsv4Auth.sessionToken || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleSessionTokenChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Service</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Service</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={awsv4Auth.service || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleServiceChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Region</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Region</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={awsv4Auth.region || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleRegionChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Profile Name</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Profile Name</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={awsv4Auth.profileName || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleProfileNameChange(val)}
collection={collection}
isCompact
/>
</div>
</StyledWrapper>

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,18 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const BasicAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const basicAuth = get(collection, 'root.request.auth.basic', {});
const basicAuth = collection.draft?.root ? get(collection, 'draft.root.request.auth.basic', {}) : get(collection, 'root.request.auth.basic', {});
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(basicAuth?.password);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleUsernameChange = (username) => {
dispatch(
@@ -47,18 +47,19 @@ const BasicAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={basicAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Password</label>
<label className="block mb-1">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={basicAuth.password || ''}
@@ -67,6 +68,7 @@ const BasicAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="basic-password" warningMessage={warningMessage} />}
</div>

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,18 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const BearerAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const bearerToken = get(collection, 'root.request.auth.bearer.token', '');
const bearerToken = collection.draft?.root ? get(collection, 'draft.root.request.auth.bearer.token', '') : get(collection, 'root.request.auth.bearer.token', '');
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(bearerToken);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleTokenChange = (token) => {
dispatch(
@@ -33,7 +33,7 @@ const BearerAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Token</label>
<label className="block mb-1">Token</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={bearerToken}
@@ -42,6 +42,7 @@ const BearerAuth = ({ collection }) => {
onChange={(val) => handleTokenChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="bearer-token" warningMessage={warningMessage} />}
</div>

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,18 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const DigestAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const digestAuth = get(collection, 'root.request.auth.digest', {});
const digestAuth = collection.draft?.root ? get(collection, 'draft.root.request.auth.digest', {}) : get(collection, 'root.request.auth.digest', {});
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(digestAuth?.password);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleUsernameChange = (username) => {
dispatch(
@@ -47,18 +47,19 @@ const DigestAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={digestAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Password</label>
<label className="block mb-1">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={digestAuth.password || ''}
@@ -67,6 +68,7 @@ const DigestAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="digest-password" warningMessage={warningMessage} />}
</div>

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,25 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const NTLMAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const ntlmAuth = get(collection, 'root.request.auth.ntlm', {});
const ntlmAuth = collection.draft?.root ? get(collection, 'draft.root.request.auth.ntlm', {}) : get(collection, 'root.request.auth.ntlm', {});
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(ntlmAuth?.password);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleUsernameChange = (username) => {
dispatch(
@@ -67,26 +60,24 @@ const NTLMAuth = ({ collection }) => {
}
})
);
};
};
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={ntlmAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<label className="block mb-1">Password</label>
<div className="single-line-editor-wrapper mb-3 flex items-center">
<SingleLineEditor
value={ntlmAuth.password || ''}
theme={storedTheme}
@@ -94,11 +85,12 @@ const NTLMAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="ntlm-password" warningMessage={warningMessage} />}
</div>
<label className="block font-medium mb-2">Domain</label>
<label className="block mb-1">Domain</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.domain || ''}
@@ -106,6 +98,7 @@ const NTLMAuth = ({ collection }) => {
onSave={handleSave}
onChange={(val) => handleDomainChange(val)}
collection={collection}
isCompact
/>
</div>
</StyledWrapper>

View File

@@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.base};
}
.single-line-editor-wrapper {
max-width: 400px;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import get from 'lodash/get';
import StyledWrapper from './StyledWrapper';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import OAuth2AuthorizationCode from 'components/RequestPane/Auth/OAuth2/AuthorizationCode/index';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections/index';
import { useDispatch } from 'react-redux';
@@ -10,14 +10,14 @@ import OAuth2ClientCredentials from 'components/RequestPane/Auth/OAuth2/ClientCr
import OAuth2Implicit from 'components/RequestPane/Auth/OAuth2/Implicit/index';
import GrantTypeSelector from 'components/RequestPane/Auth/OAuth2/GrantTypeSelector/index';
const GrantTypeComponentMap = ({collection }) => {
const GrantTypeComponentMap = ({ collection }) => {
const dispatch = useDispatch();
const save = () => {
dispatch(saveCollectionRoot(collection.uid));
dispatch(saveCollectionSettings(collection.uid));
};
let request = collection.draft ? get(collection, 'draft.request', {}) : get(collection, 'root.request', {});
let request = collection.draft?.root ? get(collection, 'draft.root.request', {}) : get(collection, 'root.request', {});
const grantType = get(request, 'auth.oauth2.grantType', {});
switch (grantType) {
@@ -40,7 +40,7 @@ const GrantTypeComponentMap = ({collection }) => {
};
const OAuth2 = ({ collection }) => {
let request = collection.draft ? get(collection, 'draft.request', {}) : get(collection, 'root.request', {});
let request = collection.draft?.root ? get(collection, 'draft.root.request', {}) : get(collection, 'root.request', {});
return (
<StyledWrapper className="mt-2 w-full">

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -6,18 +6,18 @@ import { useTheme } from 'providers/Theme';
import { useDispatch } from 'react-redux';
import SingleLineEditor from 'components/SingleLineEditor';
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
const WsseAuth = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const wsseAuth = get(collection, 'root.request.auth.wsse', {});
const wsseAuth = collection.draft?.root ? get(collection, 'draft.root.request.auth.wsse', {}) : get(collection, 'root.request.auth.wsse', {});
const { isSensitive } = useDetectSensitiveField(collection);
const { showWarning, warningMessage } = isSensitive(wsseAuth?.password);
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleUserChange = (username) => {
dispatch(
@@ -47,18 +47,19 @@ const WsseAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<SingleLineEditor
value={wsseAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUserChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block font-medium mb-2">Password</label>
<label className="block mb-1">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={wsseAuth.password || ''}
@@ -67,6 +68,7 @@ const WsseAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="wsse-password" warningMessage={warningMessage} />}
</div>

View File

@@ -8,17 +8,17 @@ import BasicAuth from './BasicAuth';
import DigestAuth from './DigestAuth';
import WsseAuth from './WsseAuth';
import ApiKeyAuth from './ApiKeyAuth/';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import OAuth2 from './OAuth2';
import NTLMAuth from './NTLMAuth';
import Button from 'ui/Button';
const Auth = ({ collection }) => {
const authMode = get(collection, 'root.request.auth.mode');
const authMode = collection.draft?.root ? get(collection, 'draft.root.request.auth.mode') : get(collection, 'root.request.auth.mode');
const dispatch = useDispatch();
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const getAuthView = () => {
switch (authMode) {
@@ -36,7 +36,7 @@ const Auth = ({ collection }) => {
}
case 'ntlm': {
return <NTLMAuth collection={collection} />;
}
}
case 'oauth2': {
return <OAuth2 collection={collection} />;
}
@@ -60,9 +60,9 @@ const Auth = ({ collection }) => {
</div>
{getAuthView()}
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
<Button type="submit" size="sm" onClick={handleSave}>
Save
</button>
</Button>
</div>
</StyledWrapper>
);

View File

@@ -30,11 +30,11 @@ const StyledWrapper = styled.div`
box-shadow: none;
transition: border-color ease-in-out 0.1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
background-color: ${(props) => props.theme.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
outline: none !important;
}
}
@@ -48,32 +48,37 @@ const StyledWrapper = styled.div`
}
.protocol-https,
.protocol-grpcs {
.protocol-grpcs,
.protocol-wss {
position: absolute;
right: 8px;
top: 0;
bottom: 0;
transition: transform 0.3s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.protocol-https {
animation: slideUpDown 6s infinite;
animation: slideUpDown 9s infinite;
transform: translateY(0);
}
.protocol-grpcs {
animation: slideUpDown 6s infinite 3s;
animation: slideUpDown 9s infinite 3s;
transform: translateY(100%);
}
.protocol-wss {
animation: slideUpDown 9s infinite 6s;
transform: translateY(100%);
}
@keyframes slideUpDown {
0%, 45% {
0%, 30% {
transform: translateY(0);
}
50%, 95% {
33.33%, 97% {
transform: translateY(100%);
}
100% {

View File

@@ -9,8 +9,19 @@ import SensitiveFieldWarning from 'components/SensitiveFieldWarning/index';
import SingleLineEditor from 'components/SingleLineEditor/index';
import { useDetectSensitiveField } from 'hooks/useDetectSensitiveField/index';
import { useTheme } from 'styled-components';
import { useDispatch } from 'react-redux';
import { updateCollectionClientCertificates } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import get from 'lodash/get';
import Button from 'ui/Button';
const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }) => {
const ClientCertSettings = ({ collection }) => {
const dispatch = useDispatch();
// Get client certs from draft if exists, otherwise from brunoConfig
const clientCertConfig = collection.draft?.brunoConfig
? get(collection, 'draft.brunoConfig.clientCertificates.certs', [])
: get(collection, 'brunoConfig.clientCertificates.certs', []);
const certFilePathInputRef = useRef();
const keyFilePathInputRef = useRef();
const pfxFilePathInputRef = useRef();
@@ -29,7 +40,7 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
domain: Yup.string()
.required()
.trim()
.test('not-empty-after-trim', 'Domain is required', value => value && value.trim().length > 0),
.test('not-empty-after-trim', 'Domain is required', (value) => value && value.trim().length > 0),
type: Yup.string().required().oneOf(['cert', 'pfx']),
certFilePath: Yup.string().when('type', {
is: (type) => type == 'cert',
@@ -63,7 +74,19 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
passphrase: values.passphrase
};
}
onUpdate(relevantValues);
// Add the new cert to the existing certs in draft
const updatedCerts = [...clientCertConfig, relevantValues];
const clientCertificates = {
enabled: true,
certs: updatedCerts
};
dispatch(updateCollectionClientCertificates({
collectionUid: collection.uid,
clientCertificates
}));
formik.resetForm();
resetFileInputFields();
}
@@ -81,9 +104,15 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
};
const resetFileInputFields = () => {
certFilePathInputRef.current.value = '';
keyFilePathInputRef.current.value = '';
pfxFilePathInputRef.current.value = '';
if (certFilePathInputRef.current) {
certFilePathInputRef.current.value = '';
}
if (keyFilePathInputRef.current) {
keyFilePathInputRef.current.value = '';
}
if (pfxFilePathInputRef.current) {
pfxFilePathInputRef.current.value = '';
}
};
const handleTypeChange = (e) => {
@@ -99,34 +128,49 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
}
};
const handleRemove = (indexToRemove) => {
const updatedCerts = clientCertConfig.filter((cert, index) => index !== indexToRemove);
const clientCertificates = {
enabled: true,
certs: updatedCerts
};
dispatch(updateCollectionClientCertificates({
collectionUid: collection.uid,
clientCertificates
}));
};
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
return (
<StyledWrapper className="w-full h-full">
<div className="text-xs mb-4 text-muted">Add client certificates to be used for specific domains.</div>
<h1 className="font-semibold">Client Certificates</h1>
<h1 className="font-medium">Client Certificates</h1>
<ul className="mt-4">
{!clientCertConfig.length
? 'No client certificates added'
: clientCertConfig.map((clientCert, index) => (
<li key={`client-cert-${index}`} className="flex items-center available-certificates p-2 rounded-lg mb-2">
<div className="flex items-center w-full justify-between">
<div className="flex w-full items-center">
<IconWorld className="mr-2" size={18} strokeWidth={1.5} />
{clientCert.domain}
<li key={`client-cert-${index}`} className="flex items-center available-certificates p-2 rounded-lg mb-2">
<div className="flex items-center w-full justify-between">
<div className="flex w-full items-center">
<IconWorld className="mr-2" size={18} strokeWidth={1.5} />
{clientCert.domain}
</div>
<div className="flex w-full items-center">
<IconCertificate className="mr-2 flex-shrink-0" size={18} strokeWidth={1.5} />
{clientCert.type === 'cert' ? clientCert.certFilePath : clientCert.pfxFilePath}
</div>
<button onClick={() => handleRemove(index)} className="remove-certificate ml-2">
<IconTrash size={18} strokeWidth={1.5} />
</button>
</div>
<div className="flex w-full items-center">
<IconCertificate className="mr-2 flex-shrink-0" size={18} strokeWidth={1.5} />
{clientCert.type === 'cert' ? clientCert.certFilePath : clientCert.pfxFilePath}
</div>
<button onClick={() => onRemove(clientCert)} className="remove-certificate ml-2">
<IconTrash size={18} strokeWidth={1.5} />
</button>
</div>
</li>
))}
</li>
))}
</ul>
<h1 className="font-semibold mt-8 mb-2">Add Client Certificate</h1>
<h1 className="font-medium mt-8 mb-2">Add Client Certificate</h1>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="domain">
@@ -137,6 +181,7 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
<span className="protocol-placeholder">
<span className="protocol-https">https://</span>
<span className="protocol-grpcs">grpcs://</span>
<span className="protocol-wss">wss://</span>
</span>
</div>
<input
@@ -329,10 +374,14 @@ const ClientCertSettings = ({ collection, clientCertConfig, onUpdate, onRemove }
<div className="ml-1 text-red-500">{formik.errors.passphrase}</div>
) : null}
</div>
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary">
<div className="mt-6 flex flex-row gap-2 items-center">
<Button type="submit" size="sm" data-testid="add-client-cert">
Add
</button>
</Button>
<div className="h-4 border-l border-gray-600"></div>
<Button type="button" size="sm" onClick={handleSave}>
Save
</Button>
</div>
</form>
</StyledWrapper>

View File

@@ -1,20 +1,22 @@
import 'github-markdown-css/github-markdown.css';
import get from 'lodash/get';
import { updateCollectionDocs } from 'providers/ReduxStore/slices/collections';
import { updateCollectionDocs, deleteCollectionDraft } from 'providers/ReduxStore/slices/collections';
import { useTheme } from 'providers/Theme';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import Markdown from 'components/MarkDown';
import CodeEditor from 'components/CodeEditor';
import StyledWrapper from './StyledWrapper';
import { IconEdit, IconX, IconFileText } from '@tabler/icons';
import Button from 'ui/Button/index';
import ActionIcon from 'ui/ActionIcon/index';
const Docs = ({ collection }) => {
const dispatch = useDispatch();
const { displayedTheme } = useTheme();
const [isEditing, setIsEditing] = useState(false);
const docs = get(collection, 'root.docs', '');
const docs = collection.draft?.root ? get(collection, 'draft.root.docs', '') : get(collection, 'root.docs', '');
const preferences = useSelector((state) => state.app.preferences);
const toggleViewMode = () => {
@@ -31,41 +33,41 @@ const Docs = ({ collection }) => {
};
const handleDiscardChanges = () => {
dispatch(
dispatch((
updateCollectionDocs({
collectionUid: collection.uid,
docs: docs
})
}))
);
toggleViewMode();
}
};
const onSave = () => {
dispatch(saveCollectionRoot(collection.uid));
dispatch(saveCollectionSettings(collection.uid));
toggleViewMode();
}
};
return (
<StyledWrapper className="h-full w-full relative flex flex-col">
<div className='flex flex-row w-full justify-between items-center mb-4'>
<div className='text-lg font-medium flex items-center gap-2'>
<div className="flex flex-row w-full justify-between items-center mb-4">
<div className="text-lg font-medium flex items-center gap-2">
<IconFileText size={20} strokeWidth={1.5} />
Documentation
</div>
<div className='flex flex-row gap-2 items-center justify-center'>
<div className="flex flex-row gap-2 items-center justify-center">
{isEditing ? (
<>
<div className="editing-mode" role="tab" onClick={handleDiscardChanges}>
<IconX className="cursor-pointer" size={20} strokeWidth={1.5} />
</div>
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={onSave}>
<Button type="button" color="secondary" onClick={handleDiscardChanges}>
Cancel
</Button>
<Button type="button" onClick={onSave}>
Save
</button>
</Button>
</>
) : (
<div className="editing-mode" role="tab" onClick={toggleViewMode}>
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} />
</div>
<ActionIcon className="editing-mode" onClick={toggleViewMode}>
<IconEdit className="cursor-pointer" size={16} strokeWidth={1.5} />
</ActionIcon>
)}
</div>
</div>
@@ -81,14 +83,13 @@ const Docs = ({ collection }) => {
fontSize={get(preferences, 'font.codeFontSize')}
/>
) : (
<div className='h-full overflow-auto pl-1'>
<div className='h-[1px] min-h-[500px]'>
<div className="h-full overflow-auto pl-1">
<div className="h-[1px] min-h-[500px]">
{
docs?.length > 0 ?
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
:
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={documentationPlaceholder} />
}
docs?.length > 0
? <Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
: <Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={documentationPlaceholder} />
}
</div>
</div>
)}
@@ -98,7 +99,6 @@ const Docs = ({ collection }) => {
export default Docs;
const documentationPlaceholder = `
Welcome to your collection documentation! This space is designed to help you document your API collection effectively.

View File

@@ -6,7 +6,7 @@ const Wrapper = styled.div`
table {
width: 100%;
border-collapse: collapse;
font-weight: 600;
font-weight: 500;
table-layout: fixed;
thead,
@@ -16,7 +16,7 @@ const Wrapper = styled.div`
thead {
color: ${(props) => props.theme.table.thead.color};
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.base};
user-select: none;
}
td {
@@ -33,7 +33,7 @@ const Wrapper = styled.div`
}
.btn-add-header {
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.base};
}
input[type='text'] {

View File

@@ -1,77 +1,95 @@
import React, { useState } from 'react';
import React, { useState, useCallback } from 'react';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import { IconTrash } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { useTheme } from 'providers/Theme';
import {
addCollectionHeader,
updateCollectionHeader,
deleteCollectionHeader,
setCollectionHeaders
} from 'providers/ReduxStore/slices/collections';
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
import { setCollectionHeaders } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import SingleLineEditor from 'components/SingleLineEditor';
import EditableTable from 'components/EditableTable';
import StyledWrapper from './StyledWrapper';
import { headers as StandardHTTPHeaders } from 'know-your-http-well';
import { MimeTypes } from 'utils/codemirror/autocompleteConstants';
import BulkEditor from 'components/BulkEditor/index';
import Button from 'ui/Button';
import { headerNameRegex, headerValueRegex } from 'utils/common/regex';
const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
const Headers = ({ collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const headers = get(collection, 'root.request.headers', []);
const headers = collection.draft?.root
? get(collection, 'draft.root.request.headers', [])
: get(collection, 'root.request.headers', []);
const [isBulkEditMode, setIsBulkEditMode] = useState(false);
const toggleBulkEditMode = () => {
setIsBulkEditMode(!isBulkEditMode);
};
const handleBulkHeadersChange = (newHeaders) => {
dispatch(setCollectionHeaders({ collectionUid: collection.uid, headers: newHeaders }));
};
const handleHeadersChange = useCallback((updatedHeaders) => {
dispatch(setCollectionHeaders({ collectionUid: collection.uid, headers: updatedHeaders }));
}, [dispatch, collection.uid]);
const addHeader = () => {
dispatch(
addCollectionHeader({
collectionUid: collection.uid
})
);
};
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
const handleHeaderValueChange = (e, _header, type) => {
const header = cloneDeep(_header);
switch (type) {
case 'name': {
header.name = e.target.value;
break;
}
case 'value': {
header.value = e.target.value;
break;
}
case 'enabled': {
header.enabled = e.target.checked;
break;
const getRowError = useCallback((row, index, key) => {
if (key === 'name') {
if (!row.name || row.name.trim() === '') return null;
if (!headerNameRegex.test(row.name)) {
return 'Header name cannot contain spaces or newlines';
}
}
dispatch(
updateCollectionHeader({
header: header,
collectionUid: collection.uid
})
);
};
if (key === 'value') {
if (!row.value) return null;
if (!headerValueRegex.test(row.value)) {
return 'Header value cannot contain newlines';
}
}
return null;
}, []);
const handleRemoveHeader = (header) => {
dispatch(
deleteCollectionHeader({
headerUid: header.uid,
collectionUid: collection.uid
})
);
const columns = [
{
key: 'name',
name: 'Name',
isKeyField: true,
placeholder: 'Name',
width: '30%',
render: ({ value, onChange }) => (
<SingleLineEditor
value={value || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(newValue) => onChange(newValue.replace(/[\r\n]/g, ''))}
autocomplete={headerAutoCompleteList}
collection={collection}
placeholder={!value ? 'Name' : ''}
/>
)
},
{
key: 'value',
name: 'Value',
placeholder: 'Value',
render: ({ value, onChange }) => (
<SingleLineEditor
value={value || ''}
theme={storedTheme}
onSave={handleSave}
onChange={onChange}
collection={collection}
autocomplete={MimeTypes}
placeholder={!value ? 'Value' : ''}
/>
)
}
];
const defaultRow = {
name: '',
value: '',
description: ''
};
if (isBulkEditMode) {
@@ -82,7 +100,7 @@ const Headers = ({ collection }) => {
</div>
<BulkEditor
params={headers}
onChange={handleBulkHeadersChange}
onChange={handleHeadersChange}
onToggle={toggleBulkEditMode}
onSave={handleSave}
/>
@@ -95,94 +113,25 @@ const Headers = ({ collection }) => {
<div className="text-xs mb-4 text-muted">
Add request headers that will be sent with every request in this collection.
</div>
<table>
<thead>
<tr>
<td>Name</td>
<td>Value</td>
<td></td>
</tr>
</thead>
<tbody>
{headers && headers.length
? headers.map((header) => {
return (
<tr key={header.uid}>
<td>
<SingleLineEditor
value={header.name}
theme={storedTheme}
onSave={handleSave}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'name'
)
}
autocomplete={headerAutoCompleteList}
collection={collection}
/>
</td>
<td>
<SingleLineEditor
value={header.value}
theme={storedTheme}
onSave={handleSave}
onChange={(newValue) =>
handleHeaderValueChange(
{
target: {
value: newValue
}
},
header,
'value'
)
}
collection={collection}
autocomplete={MimeTypes}
/>
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={header.enabled}
tabIndex="-1"
className="mr-3 mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'enabled')}
/>
<button tabIndex="-1" onClick={() => handleRemoveHeader(header)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
</div>
</td>
</tr>
);
})
: null}
</tbody>
</table>
<div className="flex justify-between mt-2">
<button className="btn-add-header text-link pr-2 py-3 select-none" onClick={addHeader}>
+ Add Header
</button>
<EditableTable
columns={columns}
rows={headers}
onChange={handleHeadersChange}
defaultRow={defaultRow}
getRowError={getRowError}
/>
<div className="flex justify-end mt-2">
<button className="text-link select-none" onClick={toggleBulkEditMode}>
Bulk Edit
</button>
</div>
<div className="mt-6">
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
<Button type="submit" size="sm" onClick={handleSave}>
Save
</button>
</Button>
</div>
</StyledWrapper>
);
};
export default Headers;

View File

@@ -0,0 +1,53 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
.icon-box {
&.location {
background-color: ${(props) => rgba(props.theme.textLink, 0.08)};
border: 1px solid ${(props) => rgba(props.theme.textLink, 0.09)};
svg {
color: ${(props) => props.theme.textLink};
}
}
&.environments {
background-color: ${(props) => rgba(props.theme.colors.text.green, 0.08)};
border: 1px solid ${(props) => rgba(props.theme.colors.text.green, 0.09)};
svg {
color: ${(props) => props.theme.colors.text.green};
}
}
&.requests {
background-color: ${(props) => rgba(props.theme.colors.text.purple, 0.08)};
border: 1px solid ${(props) => rgba(props.theme.colors.text.purple, 0.09)};
svg {
color: ${(props) => props.theme.colors.text.purple};
}
}
&.share {
background-color: ${(props) => rgba(props.theme.textLink, 0.08)};
border: 1px solid ${(props) => rgba(props.theme.textLink, 0.09)};
svg {
color: ${(props) => props.theme.textLink};
}
}
&.generate-docs {
background-color: ${(props) => rgba(props.theme.accents.primary, 0.08)};
border: 1px solid ${(props) => rgba(props.theme.accents.primary, 0.09)};
svg {
color: ${(props) => props.theme.accents.primary};
}
}
}
`;
export default StyledWrapper;

View File

@@ -1,33 +1,44 @@
import React from "react";
import React from 'react';
import { getTotalRequestCountInCollection } from 'utils/collections/';
import { IconFolder, IconWorld, IconApi, IconShare } from '@tabler/icons';
import { areItemsLoading, getItemsLoadStats } from "utils/collections/index";
import { useState } from "react";
import ShareCollection from "components/ShareCollection/index";
import { IconFolder, IconWorld, IconApi, IconShare, IconBook } from '@tabler/icons';
import { areItemsLoading, getItemsLoadStats } from 'utils/collections/index';
import { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import ShareCollection from 'components/ShareCollection/index';
import GenerateDocumentation from 'components/Sidebar/Collections/Collection/GenerateDocumentation';
import { addTab } from 'providers/ReduxStore/slices/tabs';
import StyledWrapper from './StyledWrapper';
const Info = ({ collection }) => {
const dispatch = useDispatch();
const totalRequestsInCollection = getTotalRequestCountInCollection(collection);
const isCollectionLoading = areItemsLoading(collection);
const { loading: itemsLoadingCount, total: totalItems } = getItemsLoadStats(collection);
const [showShareCollectionModal, toggleShowShareCollectionModal] = useState(false);
const [showGenerateDocumentationModal, setShowGenerateDocumentationModal] = useState(false);
const globalEnvironments = useSelector((state) => state.globalEnvironments.globalEnvironments);
const collectionEnvironmentCount = collection.environments?.length || 0;
const globalEnvironmentCount = globalEnvironments?.length || 0;
const handleToggleShowShareCollectionModal = (value) => (e) => {
toggleShowShareCollectionModal(value);
}
};
return (
<div className="w-full flex flex-col h-fit">
<StyledWrapper className="w-full flex flex-col h-fit">
<div className="rounded-lg py-6">
<div className="grid gap-6">
<div className="grid gap-5">
{/* Location Row */}
<div className="flex items-start">
<div className="flex-shrink-0 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<IconFolder className="w-5 h-5 text-blue-500" stroke={1.5} />
<div className="icon-box location flex-shrink-0 p-3 rounded-lg">
<IconFolder className="w-5 h-5" stroke={1.5} />
</div>
<div className="ml-4">
<div className="font-semibold text-sm">Location</div>
<div className="mt-1 text-sm text-muted break-all">
<div className="font-medium">Location</div>
<div className="mt-1 text-muted break-all">
{collection.pathname}
</div>
</div>
@@ -35,48 +46,90 @@ const Info = ({ collection }) => {
{/* Environments Row */}
<div className="flex items-start">
<div className="flex-shrink-0 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<IconWorld className="w-5 h-5 text-green-500" stroke={1.5} />
<div className="icon-box environments flex-shrink-0 p-3 rounded-lg">
<IconWorld className="w-5 h-5" stroke={1.5} />
</div>
<div className="ml-4">
<div className="font-semibold text-sm">Environments</div>
<div className="mt-1 text-sm text-muted">
{collection.environments?.length || 0} environment{collection.environments?.length !== 1 ? 's' : ''} configured
<div className="font-medium">Environments</div>
<div className="mt-1 flex flex-col gap-1">
<button
type="button"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
uid: `${collection.uid}-environment-settings`,
collectionUid: collection.uid,
type: 'environment-settings'
})
);
}}
>
{collectionEnvironmentCount} collection environment{collectionEnvironmentCount !== 1 ? 's' : ''}
</button>
<button
type="button"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
uid: `${collection.uid}-global-environment-settings`,
collectionUid: collection.uid,
type: 'global-environment-settings'
})
);
}}
>
{globalEnvironmentCount} global environment{globalEnvironmentCount !== 1 ? 's' : ''}
</button>
</div>
</div>
</div>
{/* Requests Row */}
<div className="flex items-start">
<div className="flex-shrink-0 p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
<IconApi className="w-5 h-5 text-purple-500" stroke={1.5} />
<div className="icon-box requests flex-shrink-0 p-3 rounded-lg">
<IconApi className="w-5 h-5" stroke={1.5} />
</div>
<div className="ml-4">
<div className="font-semibold text-sm">Requests</div>
<div className="mt-1 text-sm text-muted">
<div className="font-medium">Requests</div>
<div className="mt-1 text-muted">
{
isCollectionLoading? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the collection loaded` : `${totalRequestsInCollection} request${totalRequestsInCollection !== 1 ? 's' : ''} in collection`
isCollectionLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the collection loaded` : `${totalRequestsInCollection} request${totalRequestsInCollection !== 1 ? 's' : ''} in collection`
}
</div>
</div>
</div>
<div className="flex items-start group cursor-pointer" onClick={handleToggleShowShareCollectionModal(true)}>
<div className="flex-shrink-0 p-3 bg-indigo-50 dark:bg-indigo-900/20 rounded-lg">
<IconShare className="w-5 h-5 text-indigo-500" stroke={1.5} />
<div className="icon-box share flex-shrink-0 p-3 rounded-lg">
<IconShare className="w-5 h-5" stroke={1.5} />
</div>
<div className="ml-4 h-full flex flex-col justify-start">
<div className="font-semibold text-sm h-fit my-auto">Share</div>
<div className="mt-1 text-sm group-hover:underline text-link">
<div className="font-medium h-fit my-auto">Share</div>
<div className="group-hover:underline text-link">
Share Collection
</div>
</div>
</div>
{showShareCollectionModal && <ShareCollection collectionUid={collection.uid} onClose={handleToggleShowShareCollectionModal(false)} />}
<div className="flex items-start group cursor-pointer" onClick={() => setShowGenerateDocumentationModal(true)}>
<div className="icon-box generate-docs flex-shrink-0 p-3 rounded-lg">
<IconBook className="w-5 h-5" stroke={1.5} />
</div>
<div className="ml-4 h-full flex flex-col justify-start">
<div className="font-medium h-fit my-auto">Documentation</div>
<div className="group-hover:underline text-link">
Generate Docs
</div>
</div>
</div>
{showGenerateDocumentationModal && <GenerateDocumentation collectionUid={collection.uid} onClose={() => setShowGenerateDocumentationModal(false)} />}
</div>
</div>
</div>
</StyledWrapper>
);
};
export default Info;
export default Info;

View File

@@ -1,22 +1,29 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
&.card {
background-color: ${(props) => props.theme.requestTabPanel.card.bg};
.title {
border-top: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
border-left: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
border-right: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
border-top: 1px solid ${(props) => props.theme.table.border};
border-left: 1px solid ${(props) => props.theme.table.border};
border-right: 1px solid ${(props) => props.theme.table.border};
border-top-left-radius: 3px;
border-top-right-radius: 3px;
background-color: ${(props) => props.theme.status.warning.background};
}
.warning-icon {
color: ${(props) => props.theme.status.warning.text};
}
.table {
thead {
background-color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.bg};
color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.color};
color: ${(props) => props.theme.table.thead.color} !important;
background: ${(props) => props.theme.sidebar.bg};
}
}
}

View File

@@ -1,27 +1,25 @@
import React from 'react';
import { flattenItems } from "utils/collections";
import { flattenItems } from 'utils/collections';
import { IconAlertTriangle } from '@tabler/icons';
import StyledWrapper from "./StyledWrapper";
import StyledWrapper from './StyledWrapper';
import { useDispatch, useSelector } from 'react-redux';
import { isItemARequest, itemIsOpenedInTabs } from 'utils/tabs/index';
import { getDefaultRequestPaneTab } from 'utils/collections/index';
import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs';
import { hideHomePage } from 'providers/ReduxStore/slices/app';
const RequestsNotLoaded = ({ collection }) => {
const dispatch = useDispatch();
const tabs = useSelector((state) => state.tabs.tabs);
const flattenedItems = flattenItems(collection.items);
const itemsFailedLoading = flattenedItems?.filter(item => item?.partial && !item?.loading);
const itemsFailedLoading = flattenedItems?.filter((item) => item?.partial && !item?.loading);
if (!itemsFailedLoading?.length) {
return null;
}
const handleRequestClick = (item) => e => {
const handleRequestClick = (item) => (e) => {
e.preventDefault();
if (isItemARequest(item)) {
dispatch(hideHomePage());
if (itemIsOpenedInTabs(item, tabs)) {
dispatch(
focusTab({
@@ -39,12 +37,12 @@ const RequestsNotLoaded = ({ collection }) => {
);
return;
}
}
};
return (
<StyledWrapper className="w-full card my-2">
<div className="flex items-center gap-2 px-3 py-2 title bg-yellow-50 dark:bg-yellow-900/20">
<IconAlertTriangle size={16} className="text-yellow-500" />
<div className="flex items-center gap-2 px-3 py-2 title">
<IconAlertTriangle size={16} className="warning-icon" />
<span className="font-medium">Following requests were not loaded</span>
</div>
<table className="w-full border-collapse">
@@ -61,7 +59,7 @@ const RequestsNotLoaded = ({ collection }) => {
<tbody>
{flattenedItems?.map((item, index) => (
item?.partial && !item?.loading ? (
<tr key={index} className='cursor-pointer' onClick={handleRequestClick(item)}>
<tr key={index} className="cursor-pointer" onClick={handleRequestClick(item)}>
<td className="py-1.5 px-3">
{item?.pathname?.split(`${collection?.pathname}/`)?.[1]}
</td>

View File

@@ -1,16 +1,16 @@
import StyledWrapper from "./StyledWrapper";
import Docs from "../Docs";
import Info from "./Info";
import StyledWrapper from './StyledWrapper';
import Docs from '../Docs';
import Info from './Info';
import { IconBox } from '@tabler/icons';
import RequestsNotLoaded from "./RequestsNotLoaded";
import RequestsNotLoaded from './RequestsNotLoaded';
const Overview = ({ collection }) => {
return (
<div className="h-full">
<div className="grid grid-cols-5 gap-4 h-full">
<div className="grid grid-cols-5 gap-5 h-full">
<div className="col-span-2">
<div className="text-xl font-semibold flex items-center gap-2">
<IconBox size={24} stroke={1.5} />
<div className="text-lg font-medium flex items-center gap-2">
<IconBox size={20} stroke={1.5} />
{collection?.name}
</div>
<Info collection={collection} />
@@ -22,6 +22,6 @@ const Overview = ({ collection }) => {
</div>
</div>
);
}
};
export default Overview;
export default Overview;

View File

@@ -16,11 +16,11 @@ const StyledWrapper = styled.div`
box-shadow: none;
transition: border-color ease-in-out 0.1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
background-color: ${(props) => props.theme.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
outline: none !important;
}
}

Some files were not shown because too many files have changed in this diff Show More