Compare commits

..

104 Commits

Author SHA1 Message Date
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
473 changed files with 20483 additions and 5305 deletions

210
package-lock.json generated
View File

@@ -22,11 +22,14 @@
"packages/bruno-requests",
"packages/bruno-filestore"
],
"dependencies": {
"ajv": "^8.17.1"
},
"devDependencies": {
"@eslint/compat": "^1.3.2",
"@faker-js/faker": "^7.6.0",
"@jest/globals": "^29.2.0",
"@opencollection/types": "0.3.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",
@@ -1604,7 +1607,7 @@
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
"integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -1622,7 +1625,7 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
"integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.22.6",
@@ -1639,7 +1642,7 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1657,7 +1660,7 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/@babel/helper-globals": {
@@ -1737,7 +1740,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz",
"integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -1812,7 +1815,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz",
"integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.25.9",
@@ -1855,7 +1858,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz",
"integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1872,7 +1875,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz",
"integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -1888,7 +1891,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -1904,7 +1907,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz",
"integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1922,7 +1925,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz",
"integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1957,7 +1960,7 @@
"version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -2056,7 +2059,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz",
"integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2072,7 +2075,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
"integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2254,7 +2257,7 @@
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
@@ -2271,7 +2274,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz",
"integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2287,7 +2290,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
"integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2305,7 +2308,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
"integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.25.9",
@@ -2323,7 +2326,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
"integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2339,7 +2342,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz",
"integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2371,7 +2374,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz",
"integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.25.9",
@@ -2388,7 +2391,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz",
"integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -2409,7 +2412,7 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -2419,7 +2422,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz",
"integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2436,7 +2439,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz",
"integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2452,7 +2455,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz",
"integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2469,7 +2472,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz",
"integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2485,7 +2488,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz",
"integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2502,7 +2505,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2518,7 +2521,7 @@
"version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
"integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2534,7 +2537,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz",
"integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2566,7 +2569,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
"integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2583,7 +2586,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz",
"integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.25.9",
@@ -2601,7 +2604,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz",
"integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2617,7 +2620,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz",
"integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2633,7 +2636,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz",
"integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2649,7 +2652,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz",
"integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2665,7 +2668,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz",
"integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2698,7 +2701,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz",
"integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2717,7 +2720,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz",
"integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2734,7 +2737,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz",
"integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2751,7 +2754,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz",
"integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2782,7 +2785,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
"integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2798,7 +2801,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
"integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.25.9",
@@ -2816,7 +2819,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
"integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2833,7 +2836,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
"integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2865,7 +2868,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz",
"integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2897,7 +2900,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz",
"integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -2915,7 +2918,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz",
"integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3000,7 +3003,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz",
"integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -3017,7 +3020,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz",
"integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3034,7 +3037,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz",
"integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3050,7 +3053,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz",
"integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3066,7 +3069,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz",
"integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -3083,7 +3086,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz",
"integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3099,7 +3102,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
"integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3115,7 +3118,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
"integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3150,7 +3153,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz",
"integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3166,7 +3169,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz",
"integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3183,7 +3186,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz",
"integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3200,7 +3203,7 @@
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz",
"integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3217,7 +3220,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
"integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.26.0",
@@ -3318,7 +3321,7 @@
"version": "0.1.6-no-external-plugins",
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -6097,9 +6100,9 @@
}
},
"node_modules/@opencollection/types": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.3.0.tgz",
"integrity": "sha512-kw+co3sM4ATDQI85lgy5UmOilEHFVdNYvOjZXmnSw6PUDUTAGBiaZNdwdSXwp//o4IwKbTQb8/0I3UdjLKh+qA==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.7.0.tgz",
"integrity": "sha512-CSwdaHNPa2bNNBAOy++t6W9gBTExUJZW3aPkWyhAjasusThbvjymD/0uCLR50gCXSs0ezv61jsd19m9x+2DMtQ==",
"dev": true,
"license": "MIT"
},
@@ -9547,6 +9550,7 @@
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
@@ -9566,6 +9570,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -9578,6 +9583,7 @@
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1",
@@ -9592,6 +9598,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT"
},
"node_modules/@testing-library/jest-dom": {
@@ -9702,6 +9709,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/babel__core": {
@@ -9994,6 +10002,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/lodash": {
@@ -10016,6 +10025,7 @@
"version": "12.2.3",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
"integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/linkify-it": "*",
@@ -10026,6 +10036,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/ms": {
@@ -11400,6 +11411,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"dequal": "^2.0.3"
@@ -11844,7 +11856,7 @@
"version": "0.4.12",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
"integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.6",
@@ -11859,7 +11871,7 @@
"version": "0.10.6",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
"integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.2",
@@ -11873,7 +11885,7 @@
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz",
"integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.3"
@@ -13763,7 +13775,7 @@
"version": "3.39.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
"integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"browserslist": "^4.24.2"
@@ -14912,6 +14924,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT"
},
"node_modules/dom-converter": {
@@ -15984,7 +15997,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"devOptional": true,
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -18860,7 +18873,7 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -20870,6 +20883,15 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonschema": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz",
"integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/jsprim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
@@ -21317,6 +21339,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"bin": {
"lz-string": "bin/bin.js"
@@ -22931,7 +22954,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/path-scurry": {
@@ -25458,14 +25481,14 @@
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/regenerate-unicode-properties": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
"integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2"
@@ -25484,7 +25507,7 @@
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
"integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.4"
@@ -25494,7 +25517,7 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2",
@@ -25512,14 +25535,14 @@
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/regjsparser": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
"integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
"devOptional": true,
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"jsesc": "~3.0.2"
@@ -25532,7 +25555,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
"devOptional": true,
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -25728,7 +25751,7 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -28046,7 +28069,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -29233,7 +29256,7 @@
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"devOptional": true,
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -29294,7 +29317,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
"integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -29304,7 +29327,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
"integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"unicode-canonical-property-names-ecmascript": "^2.0.0",
@@ -29318,7 +29341,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
"integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -29328,7 +29351,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
"integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -30437,6 +30460,7 @@
"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",
@@ -33057,6 +33081,7 @@
"@rollup/plugin-typescript": "^12.1.2",
"@types/jest": "^29.5.14",
"babel-jest": "^29.7.0",
"form-data": "^4.0.0",
"is-ip": "^5.0.1",
"moment": "^2.29.4",
"rollup": "3.29.5",
@@ -33603,7 +33628,7 @@
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@opencollection/types": "0.3.0",
"@opencollection/types": "~0.5.0",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-node-resolve": "^15.0.1",
@@ -33619,6 +33644,13 @@
"typescript": "^4.8.4"
}
},
"packages/bruno-converters/node_modules/@opencollection/types": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.5.0.tgz",
"integrity": "sha512-9rpu5agMrMLcMVU2UgyV+PYV3Zf/sHBJDHMQoq8XiMEUH8lt9f7yGtlerm/9dS3SHMpGX4A8ik0OFtc0dX4r1Q==",
"dev": true,
"license": "MIT"
},
"packages/bruno-converters/node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",

View File

@@ -20,17 +20,16 @@
],
"homepage": "https://usebruno.com",
"devDependencies": {
"@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",
"storybook": "^10.1.10",
"@eslint/compat": "^1.3.2",
"@faker-js/faker": "^7.6.0",
"@jest/globals": "^29.2.0",
"@opencollection/types": "0.3.0",
"@playwright/test": "^1.51.1",
"@rollup/plugin-json": "^6.1.0",
"@stylistic/eslint-plugin": "^5.3.1",
"@types/jest": "^29.5.11",
"@types/lodash-es": "^4.17.12",
@@ -49,12 +48,13 @@
"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",
@@ -62,6 +62,7 @@
"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",
@@ -97,5 +98,8 @@
"json-schema-typed": "8.0.1"
}
}
},
"dependencies": {
"ajv": "^8.17.1"
}
}

View File

@@ -34,4 +34,5 @@ yarn-error.log*
.next/
dist/
.env
.env
storybook-static/

View File

@@ -52,6 +52,7 @@
"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",

View File

@@ -55,10 +55,10 @@ const StyledWrapper = styled.div`
}
.cm-variable-valid {
color: green;
color: ${(props) => props.theme.codemirror.variable.valid};
}
.cm-variable-invalid {
color: red;
color: ${(props) => props.theme.codemirror.variable.invalid};
}
`;

View File

@@ -37,7 +37,7 @@ const FileEditor = ({ apiSpec }) => {
/>
<IconDeviceFloppy
onClick={onSave}
color={hasChanges ? theme.colors.text.yellow : theme.requestTabs.icon.color}
color={hasChanges ? theme.draftColor : theme.requestTabs.icon.color}
strokeWidth={1.5}
size={22}
className={`absolute right-0 top-0 m-4 ${

View File

@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
}
div.dropdown-item.menu-item {
color: ${(props) => props.theme.colors.danger};
color: ${(props) => props.theme.colors.text.danger};
&:hover {
background-color: ${(props) => props.theme.colors.bg.danger};
color: white;

View File

@@ -246,12 +246,6 @@ const Wrapper = styled.div`
color: white;
}
}
.dropdown-item-active {
font-weight: 400 !important;
background-color: ${(props) => props.theme.dropdown.selectedBg} !important;
color: ${(props) => props.theme.dropdown.selectedColor} !important;
}
`;
export default Wrapper;

View File

@@ -19,7 +19,7 @@ const StyledWrapper = styled.div`
}
.selected-body-mode {
color: ${(props) => props.theme.colors.text.yellow};
color: ${(props) => props.theme.primary.text};
}
.dropdown-icon {

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

@@ -36,12 +36,12 @@ const StyledWrapper = styled.div`
/* Style line numbers when there's a lint issue */
.CodeMirror-lint-line-error .CodeMirror-linenumber {
color: #d32f2f !important;
color: ${(props) => props.theme.colors.text.danger} !important;
text-decoration: underline;
}
.CodeMirror-lint-line-warning .CodeMirror-linenumber {
color: #f57c00 !important;
color: ${(props) => props.theme.colors.text.warning} !important;
text-decoration: underline;
}
@@ -116,7 +116,7 @@ const StyledWrapper = styled.div`
span.cm-atom {
color: ${(props) => props.theme.codemirror.tokens.atom} !important;
}
span.cm-variable {
span.cm-variable, span.cm-variable-2 {
color: ${(props) => props.theme.codemirror.tokens.variable} !important;
}
span.cm-keyword {
@@ -128,14 +128,20 @@ const StyledWrapper = styled.div`
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: #5fad89 !important; /* Soft sage */
color: ${(props) => props.theme.codemirror.variable.valid} !important;
}
.cm-variable-invalid {
color: #d17b7b !important; /* Soft coral */
color: ${(props) => props.theme.codemirror.variable.invalid} !important;
}
.CodeMirror-search-hint {

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 {
@@ -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;
@@ -51,27 +52,14 @@ const StyledWrapper = styled.div`
min-width: 28px;
text-align: center;
font-size: ${(props) => props.theme.font.size.xs};
color: #aaa;
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: ${(props) => props.theme.font.size.base};
@@ -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

@@ -166,7 +166,7 @@ const CodeMirrorSearch = ({ visible, editor, onClose }) => {
return (
<StyledWrapper>
<div className="bruno-search-bar compact">
<div className="bruno-search-bar">
<input
autoFocus
type="text"

View File

@@ -2,7 +2,8 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: ${(props) => props.theme.font.size.base};
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

@@ -57,29 +57,31 @@ const ApiKeyAuth = ({ collection }) => {
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

@@ -7,7 +7,7 @@ const Wrapper = styled.div`
background: transparent;
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};
color: ${(props) => props.theme.primary.text};
.caret {
color: rgb(140, 140, 140);

View File

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

View File

@@ -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: ${(props) => props.theme.font.size.base};
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -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: ${(props) => props.theme.font.size.base};
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -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: ${(props) => props.theme.font.size.base};
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -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: ${(props) => props.theme.font.size.base};
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
}
.single-line-editor-wrapper {

View File

@@ -64,19 +64,20 @@ 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}
@@ -84,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 || ''}
@@ -96,6 +98,7 @@ const NTLMAuth = ({ collection }) => {
onSave={handleSave}
onChange={(val) => handleDomainChange(val)}
collection={collection}
isCompact
/>
</div>
</StyledWrapper>

View File

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

View File

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

@@ -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;
}
}

View File

@@ -9,6 +9,8 @@ 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();
@@ -55,17 +57,17 @@ const Docs = ({ collection }) => {
<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>

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,11 +1,13 @@
import React from 'react';
import { getTotalRequestCountInCollection } from 'utils/collections/';
import { IconFolder, IconWorld, IconApi, IconShare } from '@tabler/icons';
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();
@@ -14,6 +16,7 @@ const Info = ({ 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);
@@ -25,17 +28,17 @@ const Info = ({ collection }) => {
};
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-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-medium">Location</div>
<div className="mt-1 text-muted break-all text-xs">
<div className="mt-1 text-muted break-all">
{collection.pathname}
</div>
</div>
@@ -43,15 +46,15 @@ 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-medium text-sm">Environments</div>
<div className="font-medium">Environments</div>
<div className="mt-1 flex flex-col gap-1">
<button
type="button"
className="text-sm text-link cursor-pointer hover:underline text-left bg-transparent"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
@@ -66,7 +69,7 @@ const Info = ({ collection }) => {
</button>
<button
type="button"
className="text-sm text-link cursor-pointer hover:underline text-left bg-transparent"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
@@ -85,12 +88,12 @@ const Info = ({ collection }) => {
{/* 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-medium">Requests</div>
<div className="mt-1 text-muted text-xs">
<div className="mt-1 text-muted">
{
isCollectionLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the collection loaded` : `${totalRequestsInCollection} request${totalRequestsInCollection !== 1 ? 's' : ''} in collection`
}
@@ -99,20 +102,33 @@ const Info = ({ collection }) => {
</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-medium h-fit my-auto">Share</div>
<div className="group-hover:underline text-link text-xs">
<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>
);
};

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

@@ -41,8 +41,8 @@ const RequestsNotLoaded = ({ collection }) => {
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">

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

View File

@@ -36,7 +36,7 @@ const PresetsSettings = ({ collection }) => {
return (
<StyledWrapper className="h-full w-full">
<div className="text-xs mb-4 mt-4 text-muted">
<div className="text-xs mb-4 text-muted">
These presets will be used as the default values for new requests in this collection.
</div>
<div className="bruno-form">

View File

@@ -8,6 +8,151 @@ const StyledWrapper = styled.div`
color: ${(props) => props.theme.colors.text.danger};
}
}
/* Section labels */
label {
color: ${(props) => props.theme.text};
}
/* Tooltip icon */
.tooltip-icon {
color: ${(props) => props.theme.colors.text.muted};
cursor: pointer;
}
/* Error messages */
.error-message {
color: ${(props) => props.theme.colors.text.danger};
background-color: ${(props) => props.theme.bg};
border-radius: ${(props) => props.theme.border.radius.base};
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
thead {
th {
text-align: left;
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: ${(props) => props.theme.table.thead.color};
border: 1px solid ${(props) => props.theme.table.border};
padding: 0.5rem 0.75rem;
&.text-right {
text-align: right;
}
}
}
tbody {
td {
border: 1px solid ${(props) => props.theme.table.border};
padding: 0.5rem 0.75rem;
&.text-center {
text-align: center;
}
&.text-right {
text-align: right;
}
}
}
}
/* File/Directory icons */
.file-icon,
.folder-icon {
color: ${(props) => props.theme.colors.text.muted};
}
/* File/Directory names */
.file-name,
.directory-name {
font-weight: 500;
color: ${(props) => props.theme.text};
}
/* Path text */
.path-text {
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.muted};
font-family: monospace;
}
/* Empty state */
.empty-state {
.empty-icon {
color: ${(props) => props.theme.colors.text.muted};
}
.empty-text {
color: ${(props) => props.theme.colors.text.muted};
}
}
/* Invalid file indicator */
.invalid-indicator {
color: ${(props) => props.theme.colors.text.danger};
}
/* Action buttons */
.action-button {
padding: 0.25rem;
border-radius: ${(props) => props.theme.border.radius.base};
transition: all 0.2s;
&.replace-button {
color: ${(props) => props.theme.colors.text.danger};
&:hover {
color: ${(props) => props.theme.colors.text.danger};
background-color: ${(props) => props.theme.colors.bg.danger}20;
}
}
&.remove-button {
color: ${(props) => props.theme.colors.text.muted};
&:hover {
color: ${(props) => props.theme.text};
background-color: ${(props) => props.theme.dropdown.hoverBg};
}
}
}
/* Checkbox */
input[type='checkbox'] {
cursor: pointer;
accent-color: ${(props) => props.theme.colors.accent};
border-color: ${(props) => props.theme.table.border};
&:focus {
outline: none;
border-color: ${(props) => props.theme.primary.solid};
}
}
/* Add button */
.btn-add-param {
color: ${(props) => props.theme.textLink};
padding-right: 0.5rem;
padding-top: 0.75rem;
padding-bottom: 0.75rem;
margin-top: 0.5rem;
user-select: none;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: ${(props) => props.theme.primary.solid};
}
}
`;
export default StyledWrapper;

View File

@@ -113,12 +113,12 @@ const ProtobufSettings = ({ collection }) => {
<div className="mb-6" data-testid="protobuf-proto-files-section">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center">
<label className="font-medium flex items-center" htmlFor="protoFiles">
<label className="flex items-center" htmlFor="protoFiles">
Proto Files (
{protoFiles.length}
)
<span id="proto-files-tooltip" className="ml-2">
<IconAlertCircle size={16} className="text-gray-500 cursor-pointer" />
<IconAlertCircle size={16} className="tooltip-icon" />
</span>
<Tooltip
anchorId="proto-files-tooltip"
@@ -131,7 +131,7 @@ const ProtobufSettings = ({ collection }) => {
<div>
{protoFiles.some((file) => !file.exists) && (
<div className="text-xs text-red-600 dark:text-red-400 mb-2 flex items-center p-2 rounded" data-testid="protobuf-invalid-files-message">
<div className="error-message text-xs mb-2 flex items-center p-2" data-testid="protobuf-invalid-files-message">
<IconAlertCircle size={14} className="mr-1" />
Some proto files cannot be found. Use the replace option to update their locations.
</div>
@@ -140,13 +140,13 @@ const ProtobufSettings = ({ collection }) => {
<table className="w-full border-collapse" data-testid="protobuf-proto-files-table">
<thead>
<tr>
<th className="text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th>
File
</th>
<th className="text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th>
Path
</th>
<th className="text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th className="text-right">
Actions
</th>
</tr>
@@ -154,10 +154,10 @@ const ProtobufSettings = ({ collection }) => {
<tbody>
{protoFiles.length === 0 ? (
<tr>
<td colSpan="3" className="border border-gray-200 dark:border-gray-700 px-3 py-8 text-center">
<div className="flex flex-col items-center">
<IconFile size={24} className="text-gray-400 mb-2" />
<span className="text-gray-500 dark:text-gray-400">No proto files added</span>
<td colSpan="3" className="text-center">
<div className="empty-state flex flex-col items-center">
<IconFile size={24} className="empty-icon mb-2" />
<span className="empty-text">No proto files added</span>
</div>
</td>
</tr>
@@ -167,27 +167,27 @@ const ProtobufSettings = ({ collection }) => {
return (
<tr key={index}>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<td>
<div className="flex items-center">
<IconFile size={16} className="text-gray-500 dark:text-gray-400 mr-2" />
<span className="font-medium text-gray-900 dark:text-gray-100" data-testid="protobuf-proto-file-name">
<IconFile size={16} className="file-icon mr-2" />
<span className="file-name" data-testid="protobuf-proto-file-name">
{getBasename(collection.pathname, file.path)}
</span>
{!isValid && <IconAlertCircle size={12} className="text-red-600 dark:text-red-400 ml-2" />}
{!isValid && <IconAlertCircle size={12} className="invalid-indicator ml-2" />}
</div>
</td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<div className="text-xs text-gray-600 dark:text-gray-400 font-mono">
<td>
<div className="path-text">
{file.path}
</div>
</td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2 text-right">
<td className="text-right">
<div className="flex items-center justify-end space-x-1">
{!isValid && (
<button
type="button"
onClick={() => handleReplaceProtoFile(index)}
className="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 p-1 rounded"
className="action-button replace-button"
title="Replace file"
>
<IconFileImport size={14} />
@@ -196,7 +196,7 @@ const ProtobufSettings = ({ collection }) => {
<button
type="button"
onClick={() => handleRemoveProtoFile(index)}
className="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 p-1 rounded"
className="action-button remove-button"
title="Remove file"
data-testid="protobuf-remove-file-button"
>
@@ -220,12 +220,12 @@ const ProtobufSettings = ({ collection }) => {
<div className="mb-6" data-testid="protobuf-import-paths-section">
<div className="flex items-center justify-between mb-3">
<div className="flex items-center">
<label className="font-medium flex items-center" htmlFor="importPaths">
<label className="flex items-center" htmlFor="importPaths">
Import Paths (
{importPaths.length}
)
<span id="import-paths-tooltip" className="ml-2">
<IconAlertCircle size={16} className="text-gray-500 cursor-pointer" />
<IconAlertCircle size={16} className="tooltip-icon" />
</span>
<Tooltip
anchorId="import-paths-tooltip"
@@ -238,7 +238,7 @@ const ProtobufSettings = ({ collection }) => {
<div>
{importPaths.some((path) => !path.exists) && (
<div className="text-xs text-red-600 dark:text-red-400 mb-2 flex items-center p-2 rounded" data-testid="protobuf-invalid-import-paths-message">
<div className="error-message text-xs mb-2 flex items-center p-2" data-testid="protobuf-invalid-import-paths-message">
<IconAlertCircle size={14} className="mr-1" />
Some import paths cannot be found at their specified locations.
</div>
@@ -247,15 +247,15 @@ const ProtobufSettings = ({ collection }) => {
<table className="w-full border-collapse" data-testid="protobuf-import-paths-table">
<thead>
<tr>
<th className="text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th>
</th>
<th className="text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th>
Directory
</th>
<th className="text-left text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th>
Path
</th>
<th className="text-right text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider border border-gray-200 dark:border-gray-700 px-3 py-2">
<th className="text-right">
Actions
</th>
</tr>
@@ -263,10 +263,10 @@ const ProtobufSettings = ({ collection }) => {
<tbody>
{importPaths.length === 0 ? (
<tr>
<td colSpan="4" className="border border-gray-200 dark:border-gray-700 px-3 py-8 text-center">
<div className="flex flex-col items-center">
<IconFolder size={24} className="text-gray-400 mb-2" />
<span className="text-gray-500 dark:text-gray-400">No import paths added</span>
<td colSpan="4" className="text-center">
<div className="empty-state flex flex-col items-center">
<IconFolder size={24} className="empty-icon mb-2" />
<span className="empty-text">No import paths added</span>
</div>
</td>
</tr>
@@ -276,37 +276,37 @@ const ProtobufSettings = ({ collection }) => {
return (
<tr key={index}>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<td>
<input
type="checkbox"
checked={importPath.enabled}
onChange={() => handleToggleImportPath(index)}
className="h-4 w-4 text-gray-600 focus:ring-gray-500 border-gray-300 dark:border-gray-600 rounded"
className="h-4 w-4"
title={importPath.enabled ? 'Disable this import path' : 'Enable this import path'}
data-testid="protobuf-import-path-checkbox"
/>
</td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<td>
<div className="flex items-center">
<IconFolder size={16} className="text-gray-500 dark:text-gray-400 mr-2" />
<span className="font-medium text-gray-900 dark:text-gray-100">
<IconFolder size={16} className="folder-icon mr-2" />
<span className="directory-name">
{getBasename(collection.pathname, importPath.path)}
</span>
{!isValid && <IconAlertCircle size={12} className="text-red-600 dark:text-red-400 ml-2" />}
{!isValid && <IconAlertCircle size={12} className="invalid-indicator ml-2" />}
</div>
</td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<div className="text-xs text-gray-600 dark:text-gray-400 font-mono">
<td>
<div className="path-text">
{importPath.path}
</div>
</td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2 text-right">
<td className="text-right">
<div className="flex items-center justify-end space-x-1">
{!isValid && (
<button
type="button"
onClick={() => handleReplaceImportPath(index)}
className="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 p-1 rounded"
className="action-button replace-button"
title="Replace directory"
>
<IconFileImport size={14} />
@@ -315,7 +315,7 @@ const ProtobufSettings = ({ collection }) => {
<button
type="button"
onClick={() => handleRemoveImportPath(index)}
className="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 p-1 rounded"
className="action-button remove-button"
title="Remove import path"
data-testid="protobuf-remove-import-path-button"
>

View File

@@ -14,11 +14,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;
}
}

View File

@@ -12,7 +12,19 @@ import Button from 'ui/Button';
const ProxySettings = ({ collection }) => {
const dispatch = useDispatch();
const initialProxyConfig = { enabled: 'global', protocol: 'http', hostname: '', port: '', auth: { enabled: false, username: '', password: '' }, bypassProxy: '' };
const initialProxyConfig = {
inherit: true,
config: {
protocol: 'http',
hostname: '',
port: '',
auth: {
username: '',
password: ''
},
bypassProxy: ''
}
};
// Get proxy from draft.brunoConfig if it exists, otherwise from brunoConfig
const currentProxyConfig = collection.draft?.brunoConfig
@@ -82,34 +94,57 @@ const ProxySettings = ({ collection }) => {
const handleEnabledChange = (e) => {
const value = e.target.value;
// Convert string to boolean or keep as 'global'
const enabled = value === 'true' ? true : value === 'false' ? false : 'global';
updateProxy({ enabled });
// Map UI values to new format
if (value === 'inherit') {
updateProxy({ disabled: false, inherit: true });
} else if (value === 'true') {
updateProxy({ disabled: false, inherit: false });
} else {
updateProxy({ disabled: true, inherit: false });
}
};
const handleProtocolChange = (e) => {
updateProxy({ protocol: e.target.value });
updateProxy({
config: {
...currentProxyConfig.config,
protocol: e.target.value
}
});
};
const handleHostnameChange = (e) => {
const hostname = e.target.value;
if (validateHostnameOnChange(hostname)) {
updateProxy({ hostname });
updateProxy({
config: {
...currentProxyConfig.config,
hostname
}
});
}
};
const handlePortChange = (e) => {
const port = e.target.value ? Number(e.target.value) : '';
if (validatePortOnChange(port)) {
updateProxy({ port });
updateProxy({
config: {
...currentProxyConfig.config,
port
}
});
}
};
const handleAuthEnabledChange = (e) => {
updateProxy({
auth: {
...currentProxyConfig.auth,
enabled: e.target.checked
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
disabled: !e.target.checked
}
}
});
};
@@ -118,9 +153,12 @@ const ProxySettings = ({ collection }) => {
const username = e.target.value;
if (validateAuthUsernameOnChange(username)) {
updateProxy({
auth: {
...currentProxyConfig.auth,
username
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
username
}
}
});
}
@@ -130,9 +168,12 @@ const ProxySettings = ({ collection }) => {
const password = e.target.value;
if (validateAuthPasswordOnChange(password)) {
updateProxy({
auth: {
...currentProxyConfig.auth,
password
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
password
}
}
});
}
@@ -141,11 +182,19 @@ const ProxySettings = ({ collection }) => {
const handleBypassProxyChange = (e) => {
const bypassProxy = e.target.value;
if (validateBypassProxyOnChange(bypassProxy)) {
updateProxy({ bypassProxy });
updateProxy({
config: {
...currentProxyConfig.config,
bypassProxy
}
});
}
};
const enabledValue = currentProxyConfig.enabled === true ? 'true' : currentProxyConfig.enabled === false ? 'false' : 'global';
// Map new format to UI values
const disabled = currentProxyConfig.disabled || false;
const inherit = currentProxyConfig.inherit !== undefined ? currentProxyConfig.inherit : true;
const enabledValue = disabled ? 'false' : (inherit ? 'inherit' : 'true');
return (
<StyledWrapper className="h-full w-full">
@@ -157,9 +206,9 @@ const ProxySettings = ({ collection }) => {
<InfoTip infotipId="request-var">
<div>
<ul>
<li><span style={{ width: '50px', display: 'inline-block' }}>global</span> - use global proxy config</li>
<li><span style={{ width: '50px', display: 'inline-block' }}>enabled</span> - use collection proxy config</li>
<li><span style={{ width: '50px', display: 'inline-block' }}>disable</span> - disable proxy</li>
<li><span style={{ width: '50px', display: 'inline-block' }}>inherit</span> - inherit from global preferences</li>
<li><span style={{ width: '50px', display: 'inline-block' }}>enabled</span> - use collection-specific proxy config</li>
<li><span style={{ width: '50px', display: 'inline-block' }}>disabled</span> - disable proxy for this collection</li>
</ul>
</div>
</InfoTip>
@@ -169,12 +218,12 @@ const ProxySettings = ({ collection }) => {
<input
type="radio"
name="enabled"
value="global"
checked={enabledValue === 'global'}
value="inherit"
checked={enabledValue === 'inherit'}
onChange={handleEnabledChange}
className="mr-1"
/>
global
inherit
</label>
<label className="flex items-center ml-4">
<input
@@ -200,164 +249,168 @@ const ProxySettings = ({ collection }) => {
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="protocol">
Protocol
</label>
<div className="flex items-center">
<label className="flex items-center">
{enabledValue === 'true' && (
<>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="protocol">
Protocol
</label>
<div className="flex items-center">
<label className="flex items-center">
<input
type="radio"
name="protocol"
value="http"
checked={(currentProxyConfig.config?.protocol || 'http') === 'http'}
onChange={handleProtocolChange}
className="mr-1"
/>
HTTP
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="https"
checked={(currentProxyConfig.config?.protocol || 'http') === 'https'}
onChange={handleProtocolChange}
className="mr-1"
/>
HTTPS
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="socks4"
checked={(currentProxyConfig.config?.protocol || 'http') === 'socks4'}
onChange={handleProtocolChange}
className="mr-1"
/>
SOCKS4
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="socks5"
checked={(currentProxyConfig.config?.protocol || 'http') === 'socks5'}
onChange={handleProtocolChange}
className="mr-1"
/>
SOCKS5
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="hostname">
Hostname
</label>
<input
type="radio"
name="protocol"
value="http"
checked={(currentProxyConfig.protocol || 'http') === 'http'}
onChange={handleProtocolChange}
className="mr-1"
/>
HTTP
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="https"
checked={(currentProxyConfig.protocol || 'http') === 'https'}
onChange={handleProtocolChange}
className="mr-1"
/>
HTTPS
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="socks4"
checked={(currentProxyConfig.protocol || 'http') === 'socks4'}
onChange={handleProtocolChange}
className="mr-1"
/>
SOCKS4
</label>
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
value="socks5"
checked={(currentProxyConfig.protocol || 'http') === 'socks5'}
onChange={handleProtocolChange}
className="mr-1"
/>
SOCKS5
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="hostname">
Hostname
</label>
<input
id="hostname"
type="text"
name="hostname"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handleHostnameChange}
value={currentProxyConfig.hostname || ''}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="port">
Port
</label>
<input
id="port"
type="number"
name="port"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handlePortChange}
value={currentProxyConfig.port || ''}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.enabled">
Auth
</label>
<input
type="checkbox"
name="auth.enabled"
checked={currentProxyConfig.auth?.enabled || false}
onChange={handleAuthEnabledChange}
/>
</div>
<div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.username">
Username
</label>
<input
id="auth.username"
type="text"
name="auth.username"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={currentProxyConfig.auth?.username || ''}
onChange={handleAuthUsernameChange}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[1.70rem] relative">
<input
id="auth.password"
type={passwordVisible ? 'text' : 'password'}
name="auth.password"
className="outline-none bg-transparent w-[10.5rem]"
id="hostname"
type="text"
name="hostname"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={currentProxyConfig.auth?.password || ''}
onChange={handleAuthPasswordChange}
onChange={handleHostnameChange}
value={currentProxyConfig.config?.hostname || ''}
/>
<button
type="button"
className="btn btn-sm absolute right-0"
onClick={() => setPasswordVisible(!passwordVisible)}
>
{passwordVisible ? <IconEyeOff size={18} strokeWidth={1.5} /> : <IconEye size={18} strokeWidth={1.5} />}
</button>
</div>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="bypassProxy">
Proxy Bypass
</label>
<input
id="bypassProxy"
type="text"
name="bypassProxy"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handleBypassProxyChange}
value={currentProxyConfig.bypassProxy || ''}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="port">
Port
</label>
<input
id="port"
type="number"
name="port"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handlePortChange}
value={currentProxyConfig.config?.port || ''}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.disabled">
Auth
</label>
<input
type="checkbox"
name="auth.disabled"
checked={!currentProxyConfig.config?.auth?.disabled}
onChange={handleAuthEnabledChange}
/>
</div>
<div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.username">
Username
</label>
<input
id="auth.username"
type="text"
name="auth.username"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={currentProxyConfig.config?.auth?.username || ''}
onChange={handleAuthUsernameChange}
/>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[1.70rem] relative">
<input
id="auth.password"
type={passwordVisible ? 'text' : 'password'}
name="auth.password"
className="outline-none bg-transparent w-[10.5rem]"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={currentProxyConfig.config?.auth?.password || ''}
onChange={handleAuthPasswordChange}
/>
<button
type="button"
className="btn btn-sm absolute right-0"
onClick={() => setPasswordVisible(!passwordVisible)}
>
{passwordVisible ? <IconEyeOff size={18} strokeWidth={1.5} /> : <IconEye size={18} strokeWidth={1.5} />}
</button>
</div>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="bypassProxy">
Proxy Bypass
</label>
<input
id="bypassProxy"
type="text"
name="bypassProxy"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handleBypassProxyChange}
value={currentProxyConfig.config?.bypassProxy || ''}
/>
</div>
</>
)}
<div className="mt-6">
<Button type="submit" size="sm" onClick={handleSave}>
Save

View File

@@ -8,7 +8,7 @@ const StyledWrapper = styled.div`
}
div.title {
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
}
`;

View File

@@ -56,7 +56,7 @@ const Script = ({ collection }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<StyledWrapper className="w-full flex flex-col h-full">
<div className="text-xs mb-4 text-muted">
Write pre and post-request scripts that will run before and after any request in this collection is sent.
</div>

View File

@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
border: none;
border-bottom: solid 2px transparent;
margin-right: ${(props) => props.theme.tabs.marginRight};
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
cursor: pointer;
&:focus,
@@ -19,6 +19,10 @@ const StyledWrapper = styled.div`
box-shadow: none !important;
}
&:hover {
color: ${(props) => props.theme.tabs.active.color} !important;
}
&.active {
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
color: ${(props) => props.theme.tabs.active.color} !important;
@@ -40,6 +44,11 @@ const StyledWrapper = styled.div`
.muted {
color: ${(props) => props.theme.colors.text.muted};
}
input[type='radio'] {
cursor: pointer;
accent-color: ${(props) => props.theme.primary.solid};
}
`;
export default StyledWrapper;

View File

@@ -4,7 +4,7 @@ const StyledWrapper = styled.div`
max-width: 800px;
div.title {
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
}
`;

View File

@@ -14,7 +14,7 @@ const Vars = ({ collection }) => {
return (
<StyledWrapper className="w-full flex flex-col">
<div className="flex-1 mt-2">
<div className="flex-1">
<div className="mb-3 title text-xs">Pre Request</div>
<VarsTable collection={collection} vars={requestVars} varType="request" />
</div>

View File

@@ -1,5 +1,26 @@
import styled from 'styled-components';
const StyledWrapper = styled.div``;
const StyledWrapper = styled.div`
/* Info icon */
.info-icon {
color: ${(props) => props.theme.colors.text.muted};
}
/* Required field asterisk */
.required-asterisk {
color: ${(props) => props.theme.colors.text.danger};
}
/* Error messages */
.error-message {
color: ${(props) => props.theme.colors.text.danger};
}
/* Checkbox */
input[type='checkbox'] {
cursor: pointer;
accent-color: ${(props) => props.theme.primary.solid};
}
`;
export default StyledWrapper;

View File

@@ -11,6 +11,7 @@ import moment from 'moment';
import 'moment-timezone';
import { Tooltip } from 'react-tooltip';
import { isEmpty } from 'lodash';
import StyledWrapper from './StyledWrapper';
const removeEmptyValues = (obj) => {
return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== null && value !== undefined));
@@ -225,145 +226,147 @@ const ModifyCookieModal = ({ onClose, domain, cookie }) => {
</div>
)}
>
<form onSubmit={(e) => e.preventDefault()} className="px-2">
{isRawMode ? (
<div>
<div className="flex items-center gap-2 mb-1">
<label className="block">Set-Cookie String</label>
<IconInfoCircle id="cookie-raw-info" size={16} strokeWidth={1.5} className="text-gray-400" />
<Tooltip
anchorId="cookie-raw-info"
className="tooltip-mod"
html="Key, Path, and Domain are immutable properties and cannot be modified for existing cookies"
<StyledWrapper>
<form onSubmit={(e) => e.preventDefault()} className="px-2">
{isRawMode ? (
<div>
<div className="flex items-center gap-2 mb-1">
<label className="block">Set-Cookie String</label>
<IconInfoCircle id="cookie-raw-info" size={16} strokeWidth={1.5} className="info-icon" />
<Tooltip
anchorId="cookie-raw-info"
className="tooltip-mod"
html="Key, Path, and Domain are immutable properties and cannot be modified for existing cookies"
/>
</div>
<textarea
value={cookieString}
onChange={(e) => setCookieString(e.target.value)}
className="block textbox w-full h-24"
placeholder="key=value; key2=value2"
/>
</div>
<textarea
value={cookieString}
onChange={(e) => setCookieString(e.target.value)}
className="block textbox w-full h-24"
placeholder="key=value; key2=value2"
/>
</div>
) : (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block mb-1">
Domain<span className="text-red-600">*</span>{' '}
</label>
<input
type="text"
name="domain"
// Auto-focus if its add-new i.e. when domain prop is empty
autoFocus={!domain && !formik.values.domain}
value={formik.values.domain}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.domain && formik.errors.domain && (
<div className="text-red-500 mt-1">{formik.errors.domain}</div>
)}
</div>
<div>
<label className="block mb-1">Path</label>
<input
type="text"
name="path"
value={formik.values.path}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.path && formik.errors.path && (
<div className="text-red-500 mt-1">{formik.errors.path}</div>
)}
</div>
<div>
<label className="block mb-1">
Key<span className="text-red-600">*</span>{' '}
</label>
<input
type="text"
name="key"
// Auto focus when add-for-domain i.e. if domain is already prefilled
autoFocus={!!domain && !formik.values.key}
value={formik.values.key}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.key && formik.errors.key && (
<div className="text-red-500 mt-1">{formik.errors.key}</div>
)}
) : (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block mb-1">
Domain<span className="required-asterisk">*</span>{' '}
</label>
<input
type="text"
name="domain"
// Auto-focus if its add-new i.e. when domain prop is empty
autoFocus={!domain && !formik.values.domain}
value={formik.values.domain}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.domain && formik.errors.domain && (
<div className="error-message mt-1">{formik.errors.domain}</div>
)}
</div>
<div>
<label className="block mb-1">Path</label>
<input
type="text"
name="path"
value={formik.values.path}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.path && formik.errors.path && (
<div className="error-message mt-1">{formik.errors.path}</div>
)}
</div>
<div>
<label className="block mb-1">
Key<span className="required-asterisk">*</span>{' '}
</label>
<input
type="text"
name="key"
// Auto focus when add-for-domain i.e. if domain is already prefilled
autoFocus={!!domain && !formik.values.key}
value={formik.values.key}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.key && formik.errors.key && (
<div className="error-message mt-1">{formik.errors.key}</div>
)}
</div>
<div>
<label className="block mb-1">
Value<span className="required-asterisk">*</span>{' '}
</label>
<input
type="text"
name="value"
// Auto-focus when its in edit mode i.e. cookie prop is present
autoFocus={!!cookie}
value={formik.values.value}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full"
/>
{formik.touched.value && formik.errors.value && (
<div className="error-message mt-1">{formik.errors.value}</div>
)}
</div>
</div>
<div>
<label className="block mb-1">
Value<span className="text-red-600">*</span>{' '}
</label>
<input
type="text"
name="value"
// Auto-focus when its in edit mode i.e. cookie prop is present
autoFocus={!!cookie}
value={formik.values.value}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full"
/>
{formik.touched.value && formik.errors.value && (
<div className="text-red-500 mt-1">{formik.errors.value}</div>
)}
{/* Date Picker */}
<div className="w-full flex items-end">
<div>
<label className="block mb-1">Expiration ({moment.tz.guess()})</label>
<input
type="datetime-local"
name="expires"
value={formik.values.expires}
onChange={(e) => {
formik.handleChange(e);
}}
className="block textbox non-passphrase-input w-full"
min={moment().format(moment.HTML5_FMT.DATETIME_LOCAL)}
/>
{formik.touched.expires && formik.errors.expires && (
<div className="error-message mt-1">{formik.errors.expires}</div>
)}
</div>
{/* Checkboxes */}
<div className="flex space-x-4 ml-auto">
<label className="flex items-center">
<input
type="checkbox"
name="secure"
checked={formik.values.secure}
onChange={formik.handleChange}
className="mr-2"
/>
<span>Secure</span>
</label>
<label className="flex items-center">
<input
type="checkbox"
name="httpOnly"
checked={formik.values.httpOnly}
onChange={formik.handleChange}
className="mr-2"
/>
<span>HTTP Only</span>
</label>
</div>
</div>
</div>
{/* Date Picker */}
<div className="w-full flex items-end">
<div>
<label className="block mb-1">Expiration ({moment.tz.guess()})</label>
<input
type="datetime-local"
name="expires"
value={formik.values.expires}
onChange={(e) => {
formik.handleChange(e);
}}
className="block textbox non-passphrase-input w-full"
min={moment().format(moment.HTML5_FMT.DATETIME_LOCAL)}
/>
{formik.touched.expires && formik.errors.expires && (
<div className="text-red-500 mt-1">{formik.errors.expires}</div>
)}
</div>
{/* Checkboxes */}
<div className="flex space-x-4 ml-auto">
<label className="flex items-center">
<input
type="checkbox"
name="secure"
checked={formik.values.secure}
onChange={formik.handleChange}
className="mr-2"
/>
<span>Secure</span>
</label>
<label className="flex items-center">
<input
type="checkbox"
name="httpOnly"
checked={formik.values.httpOnly}
onChange={formik.handleChange}
className="mr-2"
/>
<span>HTTP Only</span>
</label>
</div>
</div>
</div>
)}
</form>
)}
</form>
</StyledWrapper>
</Modal>
);
};

View File

@@ -28,11 +28,11 @@ const Wrapper = 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;
}
}
@@ -70,6 +70,92 @@ const Wrapper = styled.div`
background-size: 100% 30px, 100% 30px, 100% 10px, 100% 10px;
background-attachment: local, local, scroll, scroll;
}
/* Warning icon */
.warning-icon {
color: ${(props) => props.theme.colors.text.warning};
}
/* Empty state */
.empty-icon {
color: ${(props) => props.theme.colors.text.muted};
}
.empty-text {
color: ${(props) => props.theme.colors.text.muted};
}
/* Domain count text */
.domain-count {
color: ${(props) => props.theme.colors.text.muted};
}
/* Action buttons */
.action-button {
color: ${(props) => props.theme.colors.text.muted};
transition: color 0.2s;
cursor: pointer;
&:hover {
color: ${(props) => props.theme.text};
}
}
.action-button-danger {
color: ${(props) => props.theme.text};
transition: color 0.2s;
cursor: pointer;
&:hover {
color: ${(props) => props.theme.colors.text.danger};
}
}
/* Table styles */
table {
thead {
tr {
border-bottom: 1px solid ${(props) => props.theme.table.border};
color: ${(props) => props.theme.table.thead.color};
th {
color: ${(props) => props.theme.table.thead.color};
}
}
}
tbody {
tr {
border-bottom: 1px solid ${(props) => props.theme.table.border};
&:last-child {
border-bottom: none;
}
}
}
}
/* Edit button */
.edit-button {
color: ${(props) => props.theme.text};
transition: color 0.2s;
cursor: pointer;
&:hover {
color: ${(props) => props.theme.colors.text.muted};
}
}
/* Delete button */
.delete-button {
color: ${(props) => props.theme.text};
transition: color 0.2s;
cursor: pointer;
&:hover {
color: ${(props) => props.theme.colors.text.danger};
}
}
`;
export default Wrapper;

View File

@@ -14,7 +14,7 @@ import Button from 'ui/Button';
const ClearDomainCookiesModal = ({ onClose, domain, onClear }) => (
<Modal onClose={onClose} handleCancel={onClose} title="Clear Domain Cookies" hideFooter={true}>
<div className="flex items-center font-normal">
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<IconAlertTriangle size={32} strokeWidth={1.5} className="warning-icon" />
<h1 className="ml-2 text-lg font-medium">Hold on..</h1>
</div>
<div className="font-normal mt-4">
@@ -23,12 +23,12 @@ const ClearDomainCookiesModal = ({ onClose, domain, onClear }) => (
<div className="flex justify-between mt-6">
<div>
<Button size="sm" color="secondary" variant="ghost" onClick={onClose}>
<Button color="secondary" variant="ghost" onClick={onClose}>
Close
</Button>
</div>
<div>
<Button size="sm" color="danger" onClick={onClear}>
<Button color="danger" onClick={onClear}>
Clear All
</Button>
</div>
@@ -39,7 +39,7 @@ const ClearDomainCookiesModal = ({ onClose, domain, onClear }) => (
const DeleteCookieModal = ({ onClose, cookieName, onDelete }) => (
<Modal onClose={onClose} handleCancel={onClose} title="Delete Cookie" hideFooter={true}>
<div className="flex items-center font-normal">
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<IconAlertTriangle size={32} strokeWidth={1.5} className="warning-icon" />
<h1 className="ml-2 text-lg font-medium">Hold on..</h1>
</div>
<div className="font-normal mt-4">
@@ -48,12 +48,12 @@ const DeleteCookieModal = ({ onClose, cookieName, onDelete }) => (
<div className="flex justify-between mt-6">
<div>
<Button size="sm" color="secondary" variant="ghost" onClick={onClose}>
<Button color="secondary" variant="ghost" onClick={onClose}>
Close
</Button>
</div>
<div>
<Button size="sm" color="danger" onClick={onDelete}>
<Button color="danger" onClick={onDelete}>
Delete
</Button>
</div>
@@ -159,27 +159,28 @@ const CollectionProperties = ({ onClose }) => {
{!cookies || !cookies.length ? (
// No cookies found
<div className="flex items-center justify-center flex-col">
<IconCookieOff size={48} strokeWidth={1.5} className="text-gray-500" />
<IconCookieOff size={48} strokeWidth={1.5} className="empty-icon" />
<h2 className="text-lg font-medium mt-4">No cookies found</h2>
<p className="text-gray-500 mt-2">Add cookies to get started</p>
<button
<p className="empty-text mt-2">Add cookies to get started</p>
<Button
type="submit"
className="submit btn btn-sm btn-secondary flex items-center gap-1 mt-8"
size="sm"
className="mt-8"
icon={<IconCirclePlus strokeWidth={1.5} size={16} />}
onClick={(e) => {
e.stopPropagation();
handleAddCookie();
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add Cookie</span>
</button>
Add Cookie
</Button>
</div>
) : cookies.length && !filteredCookies.length ? (
// No search results
<div className="flex items-center justify-center flex-col">
<IconSearch size={48} />
<h2 className="text-lg font-medium mt-4">No search results</h2>
<p className="text-gray-500 mt-2">Try a different search term</p>
<p className="empty-text mt-2">Try a different search term</p>
</div>
) : (
// Show cookies list
@@ -190,14 +191,14 @@ const CollectionProperties = ({ onClose }) => {
<Accordion.Header index={i} className="flex items-center">
<div className="flex items-center">
<span>{domainWithCookies.domain}</span>
<span className="ml-2 text-xs dark:text-gray-300 text-gray-500">
<span className="domain-count ml-2 text-xs">
({domainWithCookies.cookies.length}{' '}
{domainWithCookies.cookies.length === 1 ? 'cookie' : 'cookies'})
</span>
<div className="ml-auto flex items-center gap-2">
<button
type="submit"
className="flex items-center gap-1 text-gray-500 hover:text-gray-950 dark:text-white dark:hover:text-gray-300"
className="action-button flex items-center gap-1"
onClick={(e) => {
e.stopPropagation();
handleAddCookie(domainWithCookies.domain);
@@ -210,7 +211,7 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleClearDomainCookies(domainWithCookies.domain);
}}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600 mr-2"
className="action-button-danger mr-2"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
@@ -221,7 +222,7 @@ const CollectionProperties = ({ onClose }) => {
<div className="flex items-center justify-between">
<table className="w-full">
<thead>
<tr className="text-left border-b border-gray-200 dark:border-neutral-600 text-gray-700 dark:text-gray-300">
<tr className="text-left">
<th className="py-2 px-4 font-medium w-32">Name</th>
<th className="py-2 px-4 font-medium w-52">Value</th>
<th className="py-2 px-4 font-medium">Path</th>
@@ -233,7 +234,7 @@ const CollectionProperties = ({ onClose }) => {
</thead>
<tbody>
{domainWithCookies.cookies.map((cookie) => (
<tr key={cookie.key} className="border-b border-gray-200 dark:border-neutral-600 last:border-none">
<tr key={cookie.key}>
<td className="py-2 px-4 truncate">
<span id={`cookie-key-${cookie.key}`}>{cookie.key}</span>
<Tooltip
@@ -274,8 +275,7 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleEditCookie(domainWithCookies.domain, cookie);
}}
className="text-gray-700 hover:text-gray-950
dark:text-white dark:hover:text-gray-300"
className="edit-button"
>
<IconEdit strokeWidth={1.5} size={16} />
</button>
@@ -284,7 +284,7 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleDeleteCookie(domainWithCookies.domain, cookie.path, cookie.key);
}}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600"
className="delete-button"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>

View File

@@ -38,20 +38,6 @@ const StyledWrapper = styled.div`
gap: 8px;
}
.control-button {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
background: transparent;
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 4px;
color: ${(props) => props.theme.console.buttonColor};
cursor: pointer;
transition: all 0.2s ease;
}
.debug-content {
flex: 1;
overflow: hidden;

View File

@@ -32,12 +32,6 @@ const StyledWrapper = styled.div`
}
}
.network-controls {
display: flex;
align-items: center;
gap: 8px;
}
.network-content {
flex: 1;
overflow: hidden;
@@ -81,11 +75,10 @@ const StyledWrapper = styled.div`
display: grid;
grid-template-columns: 80px 80px 150px 1fr 100px 80px 80px;
gap: 12px;
padding: 8px 16px;
padding: 4px 16px;
background: ${(props) => props.theme.console.headerBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
font-size: 10px;
color: ${(props) => props.theme.console.titleColor};
text-transform: uppercase;
letter-spacing: 0.5px;
@@ -103,8 +96,7 @@ const StyledWrapper = styled.div`
display: grid;
grid-template-columns: 80px 80px 150px 1fr 100px 80px 80px;
gap: 12px;
padding: 6px 16px;
border-bottom: 1px solid ${(props) => props.theme.console.border};
padding: 2px 16px;
cursor: pointer;
transition: background-color 0.1s ease;
font-size: ${(props) => props.theme.font.size.sm};
@@ -115,7 +107,8 @@ const StyledWrapper = styled.div`
}
&.selected {
background: ${(props) => props.theme.console.buttonHoverBg};
padding-left: 13px;
background: ${(props) => props.theme.console.logHoverBg};
border-left: 3px solid ${(props) => props.theme.console.checkboxColor};
}
}
@@ -123,25 +116,19 @@ const StyledWrapper = styled.div`
.method-badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
border-radius: 4px;
justify-content: start;
font-size: 10px;
font-weight: 500;
color: white;
text-transform: uppercase;
letter-spacing: 0.5px;
min-width: 45px;
}
.status-badge {
font-weight: 500;
font-size: ${(props) => props.theme.font.size.sm};
}
.request-domain {
color: ${(props) => props.theme.console.messageColor};
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -174,120 +161,6 @@ const StyledWrapper = styled.div`
font-size: ${(props) => props.theme.font.size.xs};
text-align: right;
}
.filter-dropdown {
position: relative;
}
.filter-dropdown-trigger {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
background: transparent;
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 4px;
color: ${(props) => props.theme.console.buttonColor};
cursor: pointer;
transition: all 0.2s ease;
font-size: ${(props) => props.theme.font.size.sm};
&:hover {
background: ${(props) => props.theme.console.buttonHoverBg};
color: ${(props) => props.theme.console.buttonHoverColor};
}
.filter-summary {
font-weight: 500;
min-width: 24px;
text-align: center;
}
}
.filter-dropdown-menu {
position: absolute;
top: calc(100% + 4px);
right: 0;
min-width: 200px;
max-width: 250px;
background: ${(props) => props.theme.console.dropdownBg};
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
z-index: 1000;
overflow: hidden;
}
.filter-dropdown-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: ${(props) => props.theme.console.dropdownHeaderBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
font-size: ${(props) => props.theme.font.size.sm};
font-weight: 500;
color: ${(props) => props.theme.console.titleColor};
}
.filter-toggle-all {
background: transparent;
border: none;
color: ${(props) => props.theme.console.buttonColor};
cursor: pointer;
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
padding: 2px 4px;
border-radius: 2px;
transition: all 0.2s ease;
&:hover {
background: ${(props) => props.theme.console.buttonHoverBg};
}
}
.filter-dropdown-options {
padding: 4px 0;
}
.filter-option {
display: flex;
align-items: center;
padding: 6px 12px;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background: ${(props) => props.theme.console.optionHoverBg};
}
input[type="checkbox"] {
margin: 0 8px 0 0;
width: 14px;
height: 14px;
accent-color: ${(props) => props.theme.console.checkboxColor};
}
}
.filter-option-content {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
}
.filter-option-label {
color: ${(props) => props.theme.console.optionLabelColor};
font-size: ${(props) => props.theme.font.size.sm};
font-weight: 400;
}
.filter-option-count {
color: ${(props) => props.theme.console.optionCountColor};
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 400;
margin-left: auto;
}
`;
export default StyledWrapper;

View File

@@ -1,128 +1,33 @@
import React, { useState, useRef, useEffect, useMemo } from 'react';
import React, { useMemo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import {
IconFilter,
IconChevronDown,
IconNetwork
} from '@tabler/icons';
import {
updateNetworkFilter,
toggleAllNetworkFilters,
setSelectedRequest
} from 'providers/ReduxStore/slices/logs';
import StyledWrapper from './StyledWrapper';
const MethodBadge = ({ method }) => {
const getMethodColor = (method) => {
switch (method?.toUpperCase()) {
case 'GET': return '#10b981';
case 'POST': return '#8b5cf6';
case 'PUT': return '#f59e0b';
case 'DELETE': return '#ef4444';
case 'PATCH': return '#06b6d4';
case 'HEAD': return '#6b7280';
case 'OPTIONS': return '#84cc16';
default: return '#6b7280';
}
};
const methodLower = method?.toLowerCase() || 'get';
return (
<span
className="method-badge"
style={{ backgroundColor: getMethodColor(method) }}
>
<span className={`method-badge ${methodLower}`}>
{method?.toUpperCase() || 'GET'}
</span>
);
};
const StatusBadge = ({ status, statusCode }) => {
const getStatusColor = (code) => {
if (code >= 200 && code < 300) return '#10b981';
if (code >= 300 && code < 400) return '#f59e0b';
if (code >= 400 && code < 500) return '#ef4444';
if (code >= 500) return '#dc2626';
return '#6b7280';
};
const displayStatus = statusCode || status;
return (
<span
className="status-badge"
style={{ color: getStatusColor(statusCode) }}
>
<span className="status-badge">
{displayStatus}
</span>
);
};
const NetworkFilterDropdown = ({ filters, requestCounts, onFilterToggle, onToggleAll }) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef(null);
const allFiltersEnabled = Object.values(filters).every((f) => f);
const activeFilters = Object.entries(filters).filter(([_, enabled]) => enabled);
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
return (
<div className="filter-dropdown" ref={dropdownRef}>
<button
className="filter-dropdown-trigger"
onClick={() => setIsOpen(!isOpen)}
title="Filter requests by method"
>
<IconFilter size={16} strokeWidth={1.5} />
<span className="filter-summary">
{activeFilters.length === Object.keys(filters).length ? 'All' : `${activeFilters.length}/${Object.keys(filters).length}`}
</span>
<IconChevronDown size={14} strokeWidth={1.5} />
</button>
{isOpen && (
<div className="filter-dropdown-menu right">
<div className="filter-dropdown-header">
<span>Filter by Method</span>
<button
className="filter-toggle-all"
onClick={() => onToggleAll(!allFiltersEnabled)}
>
{allFiltersEnabled ? 'Hide All' : 'Show All'}
</button>
</div>
<div className="filter-dropdown-options">
{Object.keys(filters).map((method) => (
<label key={method} className="filter-option">
<input
type="checkbox"
checked={filters[method]}
onChange={(e) => onFilterToggle(method, e.target.checked)}
/>
<div className="filter-option-content">
<MethodBadge method={method} />
<span className="filter-option-label">{method}</span>
<span className="filter-option-count">({requestCounts[method] || 0})</span>
</div>
</label>
))}
</div>
</div>
)}
</div>
);
};
const RequestRow = ({ request, isSelected, onClick }) => {
const { data } = request;
const { request: req, response: res, timestamp } = data;
@@ -241,22 +146,6 @@ const NetworkTab = () => {
});
}, [allRequests, networkFilters]);
const requestCounts = useMemo(() => {
return allRequests.reduce((counts, request) => {
const method = request.data?.request?.method?.toUpperCase() || 'GET';
counts[method] = (counts[method] || 0) + 1;
return counts;
}, {});
}, [allRequests]);
const handleFilterToggle = (method, enabled) => {
dispatch(updateNetworkFilter({ method, enabled }));
};
const handleToggleAllFilters = (enabled) => {
dispatch(toggleAllNetworkFilters(enabled));
};
const handleRequestClick = (request) => {
dispatch(setSelectedRequest(request));
};

View File

@@ -15,7 +15,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
padding: 2px 8px;
background: ${(props) => props.theme.console.headerBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
flex-shrink: 0;
@@ -27,7 +27,6 @@ const StyledWrapper = styled.div`
gap: 8px;
color: ${(props) => props.theme.console.titleColor};
font-size: ${(props) => props.theme.font.size.base};
font-weight: 500;
.request-time {
color: ${(props) => props.theme.console.countColor};
@@ -66,7 +65,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
padding: 4px 8px;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
@@ -92,7 +91,7 @@ const StyledWrapper = styled.div`
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 16px;
padding: 8px;
min-height: 0;
height: 0;
}
@@ -170,8 +169,7 @@ const StyledWrapper = styled.div`
z-index: 10;
td {
padding: 8px 12px;
font-weight: 500;
padding: 4px 8px;
color: ${(props) => props.theme.console.titleColor};
text-transform: uppercase;
font-size: ${(props) => props.theme.font.size.xs};
@@ -198,7 +196,7 @@ const StyledWrapper = styled.div`
}
td {
padding: 8px 12px;
padding: 2px 8px;
vertical-align: top;
word-break: break-word;
}
@@ -256,7 +254,7 @@ const StyledWrapper = styled.div`
}
.response-body-container {
border-radius: 4px;
border-radius: ${(props) => props.theme.border.radius.sm};
overflow: hidden;
height: 400px;
display: flex;
@@ -270,7 +268,7 @@ const StyledWrapper = styled.div`
}
div[role="tablist"] {
padding: 8px 12px;
padding: 4px 8px;
border-bottom: 1px solid ${(props) => props.theme.console.border};
display: flex !important;
gap: 8px !important;
@@ -305,7 +303,7 @@ const StyledWrapper = styled.div`
}
}
.network-logs-container {
.network-logs-wrapper {
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 4px;
overflow: hidden;
@@ -313,17 +311,17 @@ const StyledWrapper = styled.div`
min-height: 200px;
max-height: 400px;
.network-logs {
.network-logs-container {
background: ${(props) => props.theme.console.contentBg} !important;
color: ${(props) => props.theme.console.messageColor} !important;
height: 100% !important;
max-height: 400px !important;
pre {
padding: 0.5rem !important;
.network-logs-pre {
color: ${(props) => props.theme.console.messageColor} !important;
font-size: ${(props) => props.theme.font.size.xs} !important;
line-height: 1.4 !important;
padding: 12px !important;
}
}
}

View File

@@ -141,7 +141,7 @@ const NetworkTab = ({ response }) => {
<div className="tab-content">
<div className="section">
<h4>Network Logs</h4>
<div className="network-logs-container">
<div className="network-logs-wrapper">
{timeline.length > 0 ? (
<Network logs={timeline} />
) : (

View File

@@ -13,7 +13,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
padding: 0 8px;
background: ${(props) => props.theme.console.headerBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
flex-shrink: 0;
@@ -30,7 +30,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
padding: 4px 8px;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
@@ -38,8 +38,6 @@ const StyledWrapper = styled.div`
cursor: pointer;
transition: all 0.2s ease;
font-size: ${(props) => props.theme.font.size.sm};
font-weight: 500;
border-radius: 4px 4px 0 0;
&:hover {
background: ${(props) => props.theme.console.buttonHoverBg};
@@ -47,9 +45,9 @@ const StyledWrapper = styled.div`
}
&.active {
color: ${(props) => props.theme.console.checkboxColor};
border-bottom-color: ${(props) => props.theme.console.checkboxColor};
background: ${(props) => props.theme.console.contentBg};
color: ${(props) => props.theme.primary.strong};
border-bottom-color: ${(props) => props.theme.primary.strong};
background: ${(props) => props.theme.background.mantle};
}
}
@@ -144,9 +142,6 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 4px;
margin-right: 8px;
padding-right: 8px;
border-right: 1px solid ${(props) => props.theme.console.border};
}
.action-controls {
@@ -159,23 +154,21 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
width: 24px;
height: 24px;
background: transparent;
border: none;
border-radius: 4px;
color: ${(props) => props.theme.console.buttonColor};
color: ${(props) => props.theme.text};
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: ${(props) => props.theme.console.buttonHoverBg};
color: ${(props) => props.theme.console.buttonHoverColor};
background: ${(props) => props.theme.background.surface0};
}
&.close-button:hover {
background: #e81123;
color: white;
background: ${(props) => props.theme.background.surface0};
}
}
@@ -187,19 +180,17 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 6px;
padding: 6px 8px;
padding: 2px 8px;
background: transparent;
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 4px;
color: ${(props) => props.theme.console.buttonColor};
border: 1px solid ${(props) => props.theme.border.border0};
border-radius: ${(props) => props.theme.border.radius.sm};
color: ${(props) => props.theme.text};
cursor: pointer;
transition: all 0.2s ease;
font-size: ${(props) => props.theme.font.size.sm};
&:hover {
background: ${(props) => props.theme.console.buttonHoverBg};
color: ${(props) => props.theme.console.buttonHoverColor};
border-color: ${(props) => props.theme.console.border};
background: ${(props) => props.theme.background.surface0};
}
.filter-summary {
@@ -232,7 +223,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
padding: 4px 12px;
background: ${(props) => props.theme.console.dropdownHeaderBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
font-size: ${(props) => props.theme.font.size.sm};
@@ -263,7 +254,7 @@ const StyledWrapper = styled.div`
.filter-option {
display: flex;
align-items: center;
padding: 6px 12px;
padding: 4px 12px;
cursor: pointer;
transition: background-color 0.2s ease;
@@ -325,20 +316,6 @@ const StyledWrapper = styled.div`
.logs-container {
padding: 8px 0;
}
.method-badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 6px;
border-radius: 4px;
font-size: 10px;
font-weight: 500;
color: white;
text-transform: uppercase;
letter-spacing: 0.5px;
min-width: 45px;
}
.log-entry {
display: flex;

View File

@@ -5,10 +5,9 @@ import ToolHint from 'components/ToolHint/index';
const StyledSessionList = styled.div`
.session-list-item {
padding: 10px 12px;
padding: 2px 6px;
cursor: pointer;
border-bottom: 1px solid ${(props) => props.theme.border || 'rgba(255, 255, 255, 0.05)'};
transition: all 0.2s;
display: flex;
flex-direction: column;
gap: 4px;
@@ -24,7 +23,8 @@ const StyledSessionList = styled.div`
&.active {
background: ${(props) => props.theme.sidebarActive || 'rgba(59, 142, 234, 0.12)'};
border-left: 2px solid ${(props) => props.theme.brandColor || '#3b8eea'};
border-left: 2px solid ${(props) => props.theme.primary.subtle};
padding-left: 4px;
}
&:last-child {

View File

@@ -30,7 +30,7 @@ const StyledWrapper = styled.div`
}
.terminal-sessions-header {
padding: 12px;
padding: 6px 8px;
font-weight: 600;
font-size: 13px;
color: ${(props) => props.theme.text};

View File

@@ -182,19 +182,6 @@ const NetworkFilterDropdown = ({ filters, requestCounts, onFilterToggle, onToggl
const allFiltersEnabled = Object.values(filters).every((f) => f);
const activeFilters = Object.entries(filters).filter(([_, enabled]) => enabled);
const getMethodColor = (method) => {
switch (method?.toUpperCase()) {
case 'GET': return '#10b981';
case 'POST': return '#8b5cf6';
case 'PUT': return '#f59e0b';
case 'DELETE': return '#ef4444';
case 'PATCH': return '#06b6d4';
case 'HEAD': return '#6b7280';
case 'OPTIONS': return '#84cc16';
default: return '#6b7280';
}
};
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -241,9 +228,6 @@ const NetworkFilterDropdown = ({ filters, requestCounts, onFilterToggle, onToggl
onChange={(e) => onFilterToggle(method, e.target.checked)}
/>
<div className="filter-option-content">
<span className="method-badge" style={{ backgroundColor: getMethodColor(method) }}>
{method}
</span>
<span className="filter-option-label">{method}</span>
<span className="filter-option-count">({requestCounts[method] || 0})</span>
</div>

View File

@@ -1,6 +1,8 @@
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { darken } from 'polished';
import Console from './Console';
import { useTheme } from 'providers/Theme';
const MIN_DEVTOOLS_HEIGHT = 150;
const MAX_DEVTOOLS_HEIGHT = window.innerHeight * 0.7;
@@ -10,6 +12,9 @@ const Devtools = ({ mainSectionRef }) => {
const isDevtoolsOpen = useSelector((state) => state.logs.isConsoleOpen);
const [devtoolsHeight, setDevtoolsHeight] = useState(DEFAULT_DEVTOOLS_HEIGHT);
const [isResizingDevtools, setIsResizingDevtools] = useState(false);
const { theme } = useTheme();
const dragHandleColor = useMemo(() => darken(0.1, theme.primary.subtle), [theme.primary.subtle]);
const handleDevtoolsResizeStart = useCallback((e) => {
e.preventDefault();
@@ -68,15 +73,15 @@ const Devtools = ({ mainSectionRef }) => {
<div
onMouseDown={handleDevtoolsResizeStart}
style={{
height: '4px',
height: '2px',
cursor: 'row-resize',
backgroundColor: isResizingDevtools ? '#0078d4' : 'transparent',
backgroundColor: isResizingDevtools ? dragHandleColor : 'transparent',
transition: 'background-color 0.2s ease',
zIndex: 20,
position: 'relative'
}}
onMouseEnter={(e) => e.target.style.backgroundColor = '#0078d4'}
onMouseLeave={(e) => e.target.style.backgroundColor = isResizingDevtools ? '#0078d4' : 'transparent'}
onMouseEnter={(e) => e.target.style.backgroundColor = dragHandleColor}
onMouseLeave={(e) => e.target.style.backgroundColor = isResizingDevtools ? dragHandleColor : 'transparent'}
/>
<div style={{ height: `${devtoolsHeight}px`, overflow: 'hidden', position: 'relative' }}>
<Console />

View File

@@ -1,12 +1,20 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const Wrapper = styled.div`
min-width: 160px;
font-size: ${(props) => props.theme.font.size.base};
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.dropdown.color};
background-color: ${(props) => props.theme.dropdown.bg};
box-shadow: ${(props) => props.theme.shadow.sm};
${(props) =>
props.theme.dropdown.shadow && props.theme.dropdown.shadow !== 'none'
? `box-shadow: ${props.theme.dropdown.shadow};`
: ''}
border-radius: ${(props) => props.theme.border.radius.base};
${(props) =>
props.theme.dropdown.border && props.theme.dropdown.border !== 'none'
? `border: 1px solid ${props.theme.dropdown.border};`
: ''}
max-height: 90vh;
overflow-y: auto;
max-width: unset !important;
@@ -45,7 +53,7 @@ const Wrapper = styled.div`
cursor: pointer;
border-radius: 6px;
margin: 0.0625rem 0;
font-size: 0.8125rem;
font-size: ${(props) => props.theme.font.size.sm};
&.active {
color: ${(props) => props.theme.colors.text.yellow} !important;
@@ -136,17 +144,21 @@ const Wrapper = styled.div`
/* Active/selected state - applied to the currently selected item */
&.dropdown-item-active {
color: ${({ theme }) => theme.colors.text.yellow};
background-color: ${({ theme }) => theme.dropdown.activeBg};
font-weight: 500;
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
.dropdown-icon {
color: ${({ theme }) => theme.colors.text.yellow};
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
}
&:hover {
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
}
}
/* Combined state - when active item is also focused */
&.dropdown-item-active.dropdown-item-focused {
background-color: ${({ theme }) => theme.dropdown.activeHoverBg};
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
}
/* Focus visible for accessibility */

View File

@@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
.table-container {
overflow-y: auto;
border-radius: ${(props) => props.theme.border.radius.base};
border: ${(props) => props.theme.workspace.environments.indentBorder};
border: solid 1px ${(props) => props.theme.border.border0};
}
table {
@@ -31,8 +31,8 @@ const StyledWrapper = styled.div`
padding: 5px 10px !important;
border-top: none !important;
border-left: none !important;
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
vertical-align: middle;
&:last-child {
@@ -58,8 +58,8 @@ const StyledWrapper = styled.div`
padding: 1px 10px !important;
border-top: none !important;
border-left: none !important;
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
vertical-align: middle;
&:last-child {
@@ -83,7 +83,6 @@ const StyledWrapper = styled.div`
}
.tooltip-mod {
font-size: 11px !important;
max-width: 200px !important;
}

View File

@@ -12,6 +12,7 @@ const EditableTable = ({
getRowError,
showCheckbox = true,
showDelete = true,
disableCheckbox = false,
checkboxLabel = '',
checkboxKey = 'enabled',
reorderable = false,
@@ -288,6 +289,7 @@ const EditableTable = ({
className="mousetrap"
data-testid="column-checkbox"
checked={row[checkboxKey] ?? true}
disabled={disableCheckbox}
onChange={(e) => handleCheckboxChange(row.uid, e.target.checked)}
/>
)}

View File

@@ -6,6 +6,7 @@ import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions
import { useDispatch } from 'react-redux';
import toast from 'react-hot-toast';
import StyledWrapper from './StyledWrapper';
import Button from 'ui/Button';
const ExportEnvironmentModal = ({ onClose, environments = [], environmentType }) => {
const dispatch = useDispatch();
@@ -242,22 +243,26 @@ const ExportEnvironmentModal = ({ onClose, environments = [], environmentType })
{/* Export Actions */}
<div className="flex justify-end gap-2 mt-4 pt-3 border-t border-gray-200 dark:border-gray-700">
<button
<Button
type="button"
className="btn btn-sm btn-cancel mt-2 flex items-center"
size="sm"
color="secondary"
variant="ghost"
onClick={onClose}
disabled={isExporting}
className="mt-2 mr-2"
>
Cancel
</button>
<button
</Button>
<Button
type="button"
className="btn btn-sm btn-secondary mt-2 flex items-center"
size="sm"
onClick={handleExport}
disabled={isExporting || selectedCount === 0}
className="mt-2"
>
{isExporting ? 'Exporting...' : `Export ${selectedCount || ''} Environment${selectedCount !== 1 ? 's' : ''}`}
</button>
</Button>
</div>
</div>
</Modal>

View File

@@ -26,15 +26,15 @@ const ConfirmCloseEnvironment = ({ onCancel, onCloseWithoutSave, onSaveAndClose,
<div className="flex justify-between mt-6">
<div>
<Button size="sm" color="danger" onClick={onCloseWithoutSave}>
<Button color="danger" onClick={onCloseWithoutSave}>
Don't Save
</Button>
</div>
<div>
<div className="flex gap-2">
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
Cancel
</Button>
<Button size="sm" onClick={onSaveAndClose}>
<Button onClick={onSaveAndClose}>
Save
</Button>
</div>

View File

@@ -33,7 +33,7 @@ const EnvironmentListContent = ({
{environments.map((env) => (
<div
key={env.uid}
className={`dropdown-item ${env.uid === activeEnvironmentUid ? 'active' : ''}`}
className={`dropdown-item ${env.uid === activeEnvironmentUid ? 'dropdown-item-active' : ''}`}
onClick={() => onEnvironmentSelect(env)}
data-tooltip-content={env.name}
data-tooltip-hidden={env.name?.length < 90}

View File

@@ -65,35 +65,6 @@ const Wrapper = styled.div`
overflow: hidden;
}
.tippy-box .tippy-content {
padding: 0;
display: flex;
flex-direction: column;
height: 100%;
.dropdown-item {
display: flex;
align-items: center;
padding: 0.35rem 0.6rem;
cursor: pointer;
font-size: ${(props) => props.theme.font.size.base};
color: ${(props) => props.theme.dropdown.primaryText};
&:hover:not(:disabled) {
background-color: ${(props) => props.theme.dropdown.hoverBg};
}
&.active {
background-color: ${(props) => props.theme.dropdown.selectedBg};
color: ${(props) => props.theme.dropdown.selectedColor};
}
&.no-environment {
color: ${(props) => props.theme.dropdown.mutedText};
}
}
}
.configure-button {
position: absolute;
bottom: 0;
@@ -109,7 +80,7 @@ const Wrapper = styled.div`
}
button {
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
display: flex;
align-items: center;
justify-content: center;
@@ -119,8 +90,8 @@ const Wrapper = styled.div`
}
.tab-button {
color: var(--color-tab-inactive);
font-size: ${(props) => props.theme.font.size.base};
color: ${(props) => props.theme.colors.text.subtext0};
font-size: ${(props) => props.theme.font.size.sm};
.tab-content-wrapper {
position: relative;
@@ -170,7 +141,7 @@ const Wrapper = styled.div`
min-height: 12.5rem;
h3 {
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.5rem;
@@ -178,7 +149,7 @@ const Wrapper = styled.div`
}
p {
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
opacity: 0.75;
font-size: ${(props) => props.theme.font.size.xs};
line-height: 1.5;
@@ -194,9 +165,9 @@ const Wrapper = styled.div`
}
.space-y-2 > button {
border: 0.0625rem solid ${(props) => props.theme.dropdown.primaryText};
border: 0.0625rem solid ${(props) => props.theme.text};
background: transparent;
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
padding: 0.5rem 1rem;
border-radius: 0.375rem;
width: 100%;
@@ -224,7 +195,7 @@ const Wrapper = styled.div`
align-items: center;
justify-content: center;
padding: 2rem 1rem;
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
font-size: ${(props) => props.theme.font.size.base};
line-height: 1.5;
text-align: center;
@@ -232,7 +203,7 @@ const Wrapper = styled.div`
svg {
margin: 0 auto 1rem auto;
color: ${(props) => props.theme.dropdown.primaryText};
color: ${(props) => props.theme.text};
opacity: 0.5;
}
}

View File

@@ -186,7 +186,7 @@ const EnvironmentSelector = ({ collection }) => {
<div className="environment-selector flex align-center cursor-pointer">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
{/* Tab Headers */}
<div className="tab-header flex p-[0.75rem]">
<div className="tab-header flex pt-3 pb-2 px-3">
{tabs.map((tab) => (
<button
key={tab.id}

View File

@@ -26,6 +26,7 @@ const DeleteEnvironment = ({ onClose, environment, collection }) => {
confirmText="Delete"
handleConfirm={onConfirm}
handleCancel={onClose}
confirmButtonColor="danger"
>
Are you sure you want to delete <span className="font-medium">{environment.name}</span> ?
</Modal>

View File

@@ -9,7 +9,7 @@ const Wrapper = styled.div`
.table-container {
overflow-y: auto;
border-radius: 8px;
border: ${(props) => props.theme.workspace.environments.indentBorder};
border: solid 1px ${(props) => props.theme.border.border0};
}
table {
@@ -46,8 +46,8 @@ const Wrapper = styled.div`
td {
padding: 5px 10px !important;
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
&:last-child {
border-right: none;
@@ -64,8 +64,8 @@ const Wrapper = styled.div`
}
td {
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
&:last-child {
border-right: none;
@@ -75,28 +75,7 @@ const Wrapper = styled.div`
}
}
.btn-add-param {
font-size: 12px;
color: ${(props) => props.theme.textLink};
font-weight: 500;
padding: 7px 14px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
border-radius: 6px;
border: ${(props) => props.theme.sidebar.collection.item.indentBorder};
background: transparent;
transition: all 0.15s ease;
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
border-color: ${(props) => props.theme.textLink};
}
}
.tooltip-mod {
font-size: 11px !important;
max-width: 200px !important;
}
@@ -175,7 +154,7 @@ const Wrapper = styled.div`
border-radius: ${(props) => props.theme.border.radius.base};
background: transparent;
color: ${(props) => props.theme.text};
border: ${(props) => props.theme.sidebar.collection.item.indentBorder};
border: 1px solid ${(props) => props.theme.border.border1};
cursor: pointer;
transition: all 0.15s ease;

View File

@@ -1,10 +1,10 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
display: flex;
height: 100%;
overflow: hidden;
background-color: ${(props) => props.theme.bg};
position: relative;
.environments-container {
@@ -22,14 +22,12 @@ const StyledWrapper = styled.div`
z-index: 10;
background: ${(props) => props.theme.bg};
padding: 12px;
border-bottom: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
}
/* Left Sidebar */
.sidebar {
width: 240px;
min-width: 240px;
border-right: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
display: flex;
flex-direction: column;
}
@@ -86,7 +84,7 @@ const StyledWrapper = styled.div`
padding: 6px 8px 6px 28px;
font-size: 12px;
background: transparent;
border: ${(props) => props.theme.sidebar.collection.item.indentBorder};
border: 1px solid ${(props) => props.theme.border.border1};
border-radius: 5px;
color: ${(props) => props.theme.text};
transition: all 0.15s ease;
@@ -174,7 +172,7 @@ const StyledWrapper = styled.div`
}
&.active {
background: ${(props) => props.theme.workspace.environments.activeBg};
background: ${(props) => props.theme.background.surface0};
color: ${(props) => props.theme.text};
}
@@ -193,9 +191,12 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
flex: 1;
min-width: 0;
overflow: hidden;
.environment-name-input {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
@@ -212,12 +213,14 @@ const StyledWrapper = styled.div`
display: flex;
gap: 2px;
margin-left: 4px;
flex-shrink: 0;
}
}
&.creating {
.environment-name-input {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
@@ -234,36 +237,36 @@ const StyledWrapper = styled.div`
display: flex;
gap: 2px;
margin-left: 4px;
flex-shrink: 0;
}
}
.inline-action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
&.save {
color: ${(props) => props.theme.colors.text.green};
&:hover {
background: ${(props) => rgba(props.theme.colors.text.green, 0.1)};
}
}
.inline-action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
padding: 0;
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
&.cancel {
color: ${(props) => props.theme.colors.text.danger};
&.save {
color: ${(props) => props.theme.textLink};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
}
}
&.cancel {
color: ${(props) => props.theme.colors.text.muted};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
color: ${(props) => props.theme.text};
}
&:hover {
background: ${(props) => rgba(props.theme.colors.text.danger, 0.1)};
}
}
}

View File

@@ -11,7 +11,7 @@ const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 10%;
flex: 1;
color: ${(props) => props.theme.colors.text.muted};
@@ -32,22 +32,6 @@ const StyledWrapper = styled.div`
gap: 8px;
}
}
.shared-button {
padding: 5px 10px;
font-size: 12px;
border-radius: 5px;
border: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
background: ${(props) => props.theme.sidebar.bg};
color: ${(props) => props.theme.text};
cursor: pointer;
transition: all 0.1s ease;
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
border-color: ${(props) => props.theme.textLink};
}
}
`;
export default StyledWrapper;

View File

@@ -5,30 +5,34 @@ import StyledWrapper from './StyledWrapper';
import { IconFileAlert } from '@tabler/icons';
import ImportEnvironmentModal from 'components/Environments/Common/ImportEnvironmentModal';
import ExportEnvironmentModal from 'components/Environments/Common/ExportEnvironmentModal';
import Button from 'ui/Button';
const DefaultTab = ({ setTab }) => (
<div className="empty-state">
<IconFileAlert size={48} strokeWidth={1.5} />
<div className="title">No Environments</div>
<div className="actions">
<button className="shared-button" onClick={() => setTab('create')}>
<Button size="sm" color="secondary" onClick={() => setTab('create')}>
Create Environment
</button>
<button className="shared-button" onClick={() => setTab('import')}>
</Button>
<Button size="sm" color="secondary" onClick={() => setTab('import')}>
Import Environment
</button>
</Button>
</div>
</div>
);
const EnvironmentSettings = ({ collection }) => {
const [isModified, setIsModified] = useState(false);
const [selectedEnvironment, setSelectedEnvironment] = useState(null);
const environments = collection?.environments || [];
const [selectedEnvironment, setSelectedEnvironment] = useState(() => {
if (!environments.length) return null;
return environments.find((env) => env.uid === collection?.activeEnvironmentUid) || environments[0];
});
const [tab, setTab] = useState('default');
const [showExportModal, setShowExportModal] = useState(false);
const environments = collection?.environments || [];
if (!environments || !environments.length) {
return (
<StyledWrapper>

View File

@@ -0,0 +1,110 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
display: flex;
align-items: center;
width: 100%;
overflow: hidden;
min-width: 0;
.file-picker-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 4px 8px;
color: ${(props) => props.theme.colors.text.muted};
background: transparent;
border: none;
cursor: pointer;
border-radius: 4px;
transition: color 0.15s ease;
font-size: 12px;
white-space: nowrap;
overflow: hidden;
min-width: 0;
max-width: 100%;
&:hover {
color: ${(props) => props.theme.text} !important;
}
&.read-only {
cursor: default;
opacity: 0.6;
}
&.icon-only {
padding: 4px;
flex-shrink: 0;
}
&.icon-right {
width: 100%;
justify-content: space-between;
}
span {
line-height: 1;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
}
.label {
font-style: italic;
}
svg {
flex-shrink: 0;
}
}
.file-picker-selected {
display: flex;
align-items: center;
padding: 4px 0;
width: 100%;
cursor: pointer;
&.read-only {
cursor: default;
}
.file-icon {
flex-shrink: 0;
color: ${(props) => props.theme.colors.text.muted};
margin-right: 4px;
}
.file-name {
flex: 1;
font-size: 12px;
color: ${(props) => props.theme.text};
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.clear-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
margin-left: 4px;
color: ${(props) => props.theme.colors.text.muted};
background: transparent;
border: none;
cursor: pointer;
border-radius: 4px;
transition: color 0.15s ease;
flex-shrink: 0;
&:hover {
color: ${(props) => props.theme.text};
}
}
}
`;
export default StyledWrapper;

View File

@@ -2,10 +2,33 @@ import React from 'react';
import path from 'utils/common/path';
import { useDispatch } from 'react-redux';
import { browseFiles } from 'providers/ReduxStore/slices/collections/actions';
import { IconX } from '@tabler/icons';
import { IconX, IconUpload, IconFile } from '@tabler/icons';
import { isWindowsOS } from 'utils/common/platform';
import StyledWrapper from './StyledWrapper';
const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false, readOnly = false }) => {
/**
* FilePickerEditor component for selecting files
*
* @param {Object} props
* @param {string|string[]} props.value - Selected file path(s)
* @param {Function} props.onChange - Callback when file selection changes
* @param {Object} props.collection - Collection object with pathname
* @param {boolean} props.isSingleFilePicker - If true, only allows single file selection
* @param {boolean} props.readOnly - If true, disables file selection
* @param {string} props.displayMode - Display mode: 'label', 'icon', or 'labelAndIcon' (default: 'label')
* @param {string} props.label - Custom label text (defaults to "Select File" or "Select Files")
* @param {React.ComponentType} props.icon - Custom icon component (defaults to IconUpload)
*/
const FilePickerEditor = ({
value,
onChange,
collection,
isSingleFilePicker = false,
readOnly = false,
displayMode = 'label',
label,
icon: CustomIcon
}) => {
const dispatch = useDispatch();
const filenames = (isSingleFilePicker ? [value] : value || [])
.filter((v) => v != null && v != '')
@@ -18,6 +41,8 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa
const title = filenames.map((v) => `- ${v}`).join('\n');
const browse = () => {
if (readOnly) return;
dispatch(browseFiles([], [!isSingleFilePicker ? 'multiSelections' : '']))
.then((filePaths) => {
// If file is in the collection's directory, then we use relative path
@@ -39,7 +64,8 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa
});
};
const clear = () => {
const clear = (e) => {
e.stopPropagation();
onChange(isSingleFilePicker ? '' : []);
};
@@ -50,40 +76,69 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa
return filenames.length + ' file(s) selected';
};
const buttonClass = `btn btn-secondary px-1 ${readOnly ? 'view-mode' : 'edit-mode'}`;
const defaultLabel = isSingleFilePicker ? 'Select File' : 'Select Files';
const displayLabel = label || defaultLabel;
const IconComponent = CustomIcon || IconUpload;
return filenames.length > 0 ? (
<div
className={buttonClass}
style={{
fontWeight: 400,
width: '100%',
display: 'flex',
alignItems: 'center',
overflow: 'hidden'
}}
title={title}
>
{!readOnly && (
<button className="align-middle" onClick={clear} style={{ flexShrink: 0 }}>
<IconX size={18} />
</button>
)}
{!readOnly && <>&nbsp;</>}
<span style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
flex: 1
}}
// Render the button content based on displayMode
const renderButtonContent = () => {
switch (displayMode) {
case 'icon':
return <IconComponent size={16} />;
case 'labelAndIcon':
return (
<>
<span className="label">{displayLabel}</span>
<IconComponent size={16} />
</>
);
case 'label':
default:
return <span>{displayLabel}</span>;
}
};
// When files are selected, show file info with clear button
if (filenames.length > 0) {
return (
<StyledWrapper>
<div
className={`file-picker-selected ${readOnly ? 'read-only' : ''}`}
title={title}
onClick={!readOnly ? browse : undefined}
>
<IconFile size={16} className="file-icon" />
<span className="file-name">
{renderButtonText(filenames)}
</span>
{!readOnly && (
<button
className="clear-btn"
onClick={clear}
title="Remove file"
type="button"
>
<IconX size={16} />
</button>
)}
</div>
</StyledWrapper>
);
}
// When no files selected, show the picker button
return (
<StyledWrapper>
<button
className={`file-picker-btn ${readOnly ? 'read-only' : ''} ${displayMode === 'icon' ? 'icon-only' : ''} ${displayMode === 'labelAndIcon' ? 'icon-right' : ''}`}
onClick={browse}
disabled={readOnly}
type="button"
title={displayLabel}
>
{renderButtonText(filenames)}
</span>
</div>
) : (
<button className={buttonClass} style={{ width: '100%' }} onClick={!readOnly ? browse : undefined} disabled={readOnly}>
{isSingleFilePicker ? 'Select File' : 'Select Files'}
</button>
{renderButtonContent()}
</button>
</StyledWrapper>
);
};

View File

@@ -12,7 +12,7 @@ const Wrapper = styled.div`
background-color: ${(props) => props.theme.input.bg};
}
.inherit-mode-text {
color: ${(props) => props.theme.colors.text.yellow};
color: ${(props) => props.theme.primary.text};
}
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};

View File

@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
background: transparent;
.auth-mode-label {
color: ${(props) => props.theme.colors.text.yellow};
color: ${(props) => props.theme.primary.text};
.caret {
color: rgb(140, 140, 140);

View File

@@ -7,6 +7,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
import Markdown from 'components/MarkDown';
import CodeEditor from 'components/CodeEditor';
import Button from 'ui/Button';
import StyledWrapper from './StyledWrapper';
const Documentation = ({ collection, folder }) => {
@@ -37,27 +38,35 @@ const Documentation = ({ collection, folder }) => {
}
return (
<StyledWrapper className="mt-1 h-full w-full relative flex flex-col">
<div className="editing-mode flex justify-between items-center" role="tab" onClick={toggleViewMode}>
<StyledWrapper className="w-full relative flex flex-col">
<div className="editing-mode flex justify-between items-center flex-shrink-0" role="tab" onClick={toggleViewMode}>
{isEditing ? 'Preview' : 'Edit'}
</div>
{isEditing ? (
<div className="mt-2 flex-1 max-h-[70vh]">
<CodeEditor
collection={collection}
theme={displayedTheme}
value={docs || ''}
onEdit={onEdit}
onSave={onSave}
mode="application/text"
/>
<button type="submit" className="submit btn btn-sm btn-secondary my-6" onClick={onSave}>
Save
</button>
<div className="flex flex-col flex-1 min-h-0">
<div className="mt-2 flex-1 overflow-auto min-h-0">
<CodeEditor
collection={collection}
theme={displayedTheme}
value={docs || ''}
onEdit={onEdit}
onSave={onSave}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
mode="application/text"
/>
</div>
<div className="mt-6 flex-shrink-0">
<Button type="submit" size="sm" onClick={onSave}>
Save
</Button>
</div>
</div>
) : (
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
<div className="h-full">
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
</div>
)}
</StyledWrapper>
);

View File

@@ -6,7 +6,7 @@ const StyledWrapper = styled.div`
}
div.title {
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
}
`;

View File

@@ -58,7 +58,7 @@ const Script = ({ collection, folder }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<StyledWrapper className="w-full flex flex-col h-full">
<div className="text-xs mb-4 text-muted">
Pre and post-request scripts that will run before and after any request inside this folder is sent.
</div>

View File

@@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
border: none;
border-bottom: solid 2px transparent;
margin-right: ${(props) => props.theme.tabs.marginRight};
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
cursor: pointer;
&:focus,
@@ -21,6 +21,10 @@ const StyledWrapper = styled.div`
box-shadow: none !important;
}
&:hover {
color: ${(props) => props.theme.tabs.active.color} !important;
}
&.active {
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
color: ${(props) => props.theme.tabs.active.color} !important;

View File

@@ -2,7 +2,7 @@ import styled from 'styled-components';
const StyledWrapper = styled.div`
div.title {
color: var(--color-tab-inactive);
color: ${(props) => props.theme.colors.text.subtext0};
}
`;

View File

@@ -14,11 +14,11 @@ const Vars = ({ collection, folder }) => {
return (
<StyledWrapper className="w-full flex flex-col">
<div className="flex-1 mt-2">
<div>
<div className="mb-3 title text-xs">Pre Request</div>
<VarsTable folder={folder} collection={collection} vars={requestVars} varType="request" />
</div>
<div className="flex-1">
<div>
<div className="mt-3 mb-3 title text-xs">Post Response</div>
<VarsTable folder={folder} collection={collection} vars={responseVars} varType="response" />
</div>

View File

@@ -1,4 +1,5 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
/* Screen reader only content */
@@ -40,9 +41,9 @@ const StyledWrapper = styled.div`
}
.command-k-modal {
background: ${(props) => props.theme.modal.body.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
border: 1px solid ${(props) => props.theme.border.border1};
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
box-shadow: ${(props) => props.theme.shadow.md};
width: 90%;
max-width: 600px;
max-height: 70vh;
@@ -55,8 +56,7 @@ const StyledWrapper = styled.div`
}
.command-k-header {
padding: 12px;
border-bottom: 1px solid ${(props) => props.theme.modal.input.border};
background: ${(props) => props.theme.modal.title.bg};
border-bottom: 1px solid ${(props) => props.theme.border.border1};
}
.search-input-container {
position: relative;
@@ -64,13 +64,11 @@ const StyledWrapper = styled.div`
align-items: center;
width: 100%;
padding: 8px 12px;
border: 1px solid ${(props) => props.theme.modal.input.border};
border: 1px solid ${(props) => props.theme.input.border};
border-radius: 6px;
background: ${(props) => props.theme.modal.input.bg};
transition: all 0.2s ease;
&:focus-within {
border-color: ${(props) => props.theme.colors.text.muted};
box-shadow: 0 0 0 1px ${(props) => props.theme.colors.text.muted}40;
border: 1px solid ${(props) => props.theme.input.focusBorder};
}
.search-icon {
color: ${(props) => props.theme.colors.text.muted};
@@ -92,7 +90,7 @@ const StyledWrapper = styled.div`
border-radius: 4px;
flex-shrink: 0;
&:hover {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)'};
background: ${(props) => rgba(props.theme.text, 0.1)};
}
}
}
@@ -114,9 +112,8 @@ const StyledWrapper = styled.div`
flex: 1;
overflow-y: auto;
max-height: 400px;
background: ${(props) => props.theme.modal.body.bg};
scrollbar-width: thin;
padding: 4px;
padding: 6px 0;
scroll-behavior: smooth;
/* Webkit scrollbar styling */
&::-webkit-scrollbar {
@@ -127,26 +124,27 @@ const StyledWrapper = styled.div`
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.2)'};
background: ${(props) => rgba(props.theme.text, 0.2)};
border-radius: 4px;
&:hover {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.3)' : 'rgba(0,0,0,0.3)'};
background: ${(props) => rgba(props.theme.text, 0.3)};
}
}
}
.result-item {
display: flex;
align-items: center;
padding: 8px 12px;
gap: 8px;
padding: 10px 12px;
margin: 2px 8px;
gap: 10px;
cursor: pointer;
border-left: 2px solid transparent;
&:hover {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'};
border-radius: ${(props) => props.theme.border.radius.base};
transition: background 0.1s ease;
&:hover:not(.selected) {
background: ${(props) => rgba(props.theme.text, 0.05)};
}
&.selected {
background: ${(props) => `${props.theme.colors.text.yellow}15`};
border-left: 2px solid ${(props) => props.theme.colors.text.yellow};
background: ${(props) => props.theme.dropdown.hoverBg};
}
}
.result-icon {
@@ -196,74 +194,82 @@ const StyledWrapper = styled.div`
letter-spacing: 0.1px;
}
.method-badge {
font-size: ${(props) => props.theme.font.size.xs};
font-size: 0.625rem;
font-weight: 500;
padding: 3px 8px;
padding: 2px 6px;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
letter-spacing: 0.3px;
flex-shrink: 0;
min-width: 55px;
min-width: 48px;
text-align: center;
&.get {
color: #2ecc71;
background: rgba(46, 204, 113, 0.1);
color: ${(props) => props.theme.request.methods.get};
background: ${(props) => rgba(props.theme.request.methods.get, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.get, 0.2)};
}
&.post {
color: #3498db;
background: rgba(52, 152, 219, 0.1);
color: ${(props) => props.theme.request.methods.post};
background: ${(props) => rgba(props.theme.request.methods.post, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.post, 0.2)};
}
&.put {
color: #e67e22;
background: rgba(230, 126, 34, 0.1);
color: ${(props) => props.theme.request.methods.put};
background: ${(props) => rgba(props.theme.request.methods.put, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.put, 0.2)};
}
&.delete {
color: #e74c3c;
background: rgba(231, 76, 60, 0.1);
color: ${(props) => props.theme.request.methods.delete};
background: ${(props) => rgba(props.theme.request.methods.delete, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.delete, 0.2)};
}
&.patch {
color: #9b59b6;
background: rgba(155, 89, 182, 0.1);
color: ${(props) => props.theme.request.methods.patch};
background: ${(props) => rgba(props.theme.request.methods.patch, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.patch, 0.2)};
}
&.head {
color: #2980b9;
background: rgba(41, 128, 185, 0.1);
color: ${(props) => props.theme.request.methods.head};
background: ${(props) => rgba(props.theme.request.methods.head, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.head, 0.2)};
}
&.options {
color: #f1c40f;
background: rgba(241, 196, 15, 0.1);
color: ${(props) => props.theme.request.methods.options};
background: ${(props) => rgba(props.theme.request.methods.options, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.options, 0.2)};
}
&.unary {
color: #27ae60;
background: rgba(39, 174, 96, 0.12);
font-weight: 500;
color: ${(props) => props.theme.request.methods.get};
background: ${(props) => rgba(props.theme.request.methods.get, 0.12)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.get, 0.2)};
}
&.client-streaming {
color: #2980b9;
background: rgba(41, 128, 185, 0.12);
font-weight: 500;
color: ${(props) => props.theme.request.methods.post};
background: ${(props) => rgba(props.theme.request.methods.post, 0.12)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.post, 0.2)};
}
&.server-streaming {
color: #f39c12;
background: rgba(243, 156, 18, 0.12);
font-weight: 500;
color: ${(props) => props.theme.request.methods.put};
background: ${(props) => rgba(props.theme.request.methods.put, 0.12)};
border: 1px solid ${(props) => rgba(props.theme.request.methods.put, 0.2)};
}
&.bidirectional-streaming,
&.bidi-streaming {
color: #8e44ad;
background: rgba(142, 68, 173, 0.12);
font-weight: 500;
color: ${(props) => props.theme.colors.text.purple};
background: ${(props) => rgba(props.theme.colors.text.purple, 0.12)};
border: 1px solid ${(props) => rgba(props.theme.colors.text.purple, 0.2)};
}
}
.result-type {
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.muted};
font-size: 0.625rem;
color: ${(props) => props.theme.textLink};
padding: 2px 6px;
border-radius: 3px;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.3px;
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.03)'};
opacity: 0.8;
font-weight: 500;
background: ${(props) => rgba(props.theme.textLink, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.textLink, 0.2)};
flex-shrink: 0;
}
.result-item[data-type="documentation"] {
@@ -280,9 +286,6 @@ const StyledWrapper = styled.div`
letter-spacing: 0.1px;
opacity: 0.8;
}
&:hover:not(.selected) {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'};
}
}
.no-results,
.empty-state {
@@ -293,7 +296,7 @@ const StyledWrapper = styled.div`
}
.command-k-footer {
padding: 8px 12px;
border-top: 1px solid ${(props) => props.theme.modal.input.border};
border-top: 1px solid ${(props) => props.theme.border.border1};
background: ${(props) => props.theme.colors.surface};
}
.keyboard-hints {
@@ -319,10 +322,9 @@ const StyledWrapper = styled.div`
align-items: center;
justify-content: center;
padding: 2px 6px;
border: 1px solid ${(props) => props.theme.modal.input.border};
border: 1px solid ${(props) => props.theme.border.border2};
border-radius: 4px;
background: ${(props) =>
props.theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)'};
background: ${(props) => rgba(props.theme.text, 0.08)};
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
font-family: inherit;
@@ -332,11 +334,10 @@ const StyledWrapper = styled.div`
}
}
.highlight {
background: ${(props) => `${props.theme.colors.text.yellow}30`};
color: ${(props) => props.theme.brand};
border-radius: 2px;
padding: 0 2px;
padding: 1px 2px;
margin: 0 -1px;
font-weight: 500;
}
@keyframes fade-in {
from {

View File

@@ -1,7 +1,7 @@
import React from 'react';
// UNARY - Single request, single response (Blue)
export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '', color = '#3B82F6' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -14,16 +14,16 @@ export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' })
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right */}
<path d="M3 8h18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// CLIENT_STREAMING - Streaming request, single response (Purple)
export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#8B5CF6' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -36,17 +36,17 @@ export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, classNam
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right with double heads */}
<path d="M3 8h18" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// SERVER_STREAMING - Single request, streaming response (Green)
export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#10B981' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -59,17 +59,17 @@ export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, classNam
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right */}
<path d="M3 8h18" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left with double heads */}
<path d="M21 16h-18" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);
// BIDI_STREAMING - Streaming request, streaming response (Orange)
export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#F97316' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -82,12 +82,12 @@ export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right with double heads */}
<path d="M3 8h18" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M14 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left with double heads */}
<path d="M21 16h-18" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke="#F97316" strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M10 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
</svg>
);

View File

@@ -0,0 +1,47 @@
import React, { useId } from 'react';
import styled from 'styled-components';
const StyledSvg = styled.svg`
.icon-stroke {
stroke: ${(props) => props.theme.text};
}
.icon-fill {
fill: ${(props) => props.theme.text};
}
`;
const OpenCollectionIcon = ({ size = 28 }) => {
const clipId = useId();
return (
<StyledSvg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 567 567"
preserveAspectRatio="xMidYMid meet"
>
<g transform="matrix(1, 0, 0, 1, 93, 56)">
<clipPath id={clipId}>
<rect x="0" width="418" y="0" height="455" />
</clipPath>
<g clipPath={`url(#${clipId})`}>
<path
className="icon-fill"
d="M 249.175781 222.132812 C 249.175781 224.011719 249.085938 225.890625 248.90625 227.761719 C 248.722656 229.632812 248.453125 231.492188 248.09375 233.335938 C 247.738281 235.179688 247.289062 237 246.753906 238.800781 C 246.222656 240.601562 245.601562 242.367188 244.898438 244.105469 C 244.195312 245.84375 243.40625 247.542969 242.539062 249.199219 C 241.671875 250.859375 240.726562 252.46875 239.707031 254.035156 C 238.683594 255.597656 237.589844 257.105469 236.421875 258.558594 C 235.253906 260.011719 234.019531 261.40625 232.71875 262.734375 C 231.417969 264.0625 230.054688 265.324219 228.632812 266.519531 C 227.210938 267.710938 225.734375 268.832031 224.203125 269.875 C 222.671875 270.921875 221.097656 271.886719 219.472656 272.773438 C 217.851562 273.660156 216.1875 274.460938 214.488281 275.179688 C 212.789062 275.902344 211.058594 276.535156 209.296875 277.078125 C 207.535156 277.625 205.753906 278.082031 203.949219 278.449219 C 202.144531 278.816406 200.324219 279.089844 198.492188 279.277344 C 196.664062 279.460938 194.828125 279.550781 192.988281 279.550781 C 191.144531 279.550781 189.308594 279.460938 187.480469 279.277344 C 185.648438 279.089844 183.828125 278.816406 182.023438 278.449219 C 180.21875 278.082031 178.4375 277.625 176.675781 277.078125 C 174.914062 276.535156 173.183594 275.902344 171.484375 275.179688 C 169.785156 274.460938 168.121094 273.660156 166.5 272.773438 C 164.875 271.886719 163.300781 270.921875 161.769531 269.875 C 160.238281 268.832031 158.761719 267.710938 157.339844 266.519531 C 155.917969 265.324219 154.554688 264.0625 153.253906 262.734375 C 151.953125 261.40625 150.71875 260.011719 149.550781 258.558594 C 148.386719 257.105469 147.289062 255.597656 146.265625 254.035156 C 145.246094 252.46875 144.300781 250.859375 143.433594 249.199219 C 142.566406 247.542969 141.78125 245.84375 141.074219 244.105469 C 140.371094 242.367188 139.75 240.601562 139.21875 238.800781 C 138.683594 237 138.238281 235.179688 137.878906 233.335938 C 137.519531 231.492188 137.25 229.632812 137.070312 227.761719 C 136.886719 225.890625 136.796875 224.011719 136.796875 222.132812 C 136.796875 220.253906 136.886719 218.375 137.070312 216.503906 C 137.25 214.632812 137.519531 212.773438 137.878906 210.929688 C 138.238281 209.085938 138.683594 207.265625 139.21875 205.464844 C 139.75 203.664062 140.371094 201.898438 141.074219 200.160156 C 141.78125 198.421875 142.566406 196.722656 143.433594 195.066406 C 144.300781 193.40625 145.246094 191.796875 146.265625 190.230469 C 147.289062 188.667969 148.386719 187.160156 149.550781 185.707031 C 150.71875 184.253906 151.953125 182.859375 153.253906 181.53125 C 154.554688 180.203125 155.917969 178.941406 157.339844 177.746094 C 158.761719 176.554688 160.238281 175.433594 161.769531 174.390625 C 163.300781 173.34375 164.875 172.378906 166.5 171.492188 C 168.121094 170.605469 169.785156 169.804688 171.484375 169.085938 C 173.183594 168.363281 174.914062 167.730469 176.675781 167.1875 C 178.4375 166.640625 180.21875 166.183594 182.023438 165.816406 C 183.828125 165.449219 185.648438 165.175781 187.480469 164.988281 C 189.308594 164.804688 191.144531 164.714844 192.988281 164.714844 C 194.828125 164.714844 196.664062 164.804688 198.492188 164.988281 C 200.324219 165.175781 202.144531 165.449219 203.949219 165.816406 C 205.753906 166.183594 207.535156 166.640625 209.296875 167.1875 C 211.058594 167.730469 212.789062 168.363281 214.488281 169.085938 C 216.1875 169.804688 217.851562 170.605469 219.472656 171.492188 C 221.097656 172.378906 222.671875 173.34375 224.203125 174.390625 C 225.734375 175.433594 227.210938 176.554688 228.632812 177.746094 C 230.054688 178.941406 231.417969 180.203125 232.71875 181.53125 C 234.019531 182.859375 235.253906 184.253906 236.421875 185.707031 C 237.589844 187.160156 238.683594 188.667969 239.707031 190.230469 C 240.726562 191.796875 241.671875 193.40625 242.539062 195.066406 C 243.40625 196.722656 244.195312 198.421875 244.898438 200.160156 C 245.601562 201.898438 246.222656 203.664062 246.753906 205.464844 C 247.289062 207.265625 247.738281 209.085938 248.09375 210.929688 C 248.453125 212.773438 248.722656 214.632812 248.90625 216.503906 C 249.085938 218.375 249.175781 220.253906 249.175781 222.132812 Z M 249.175781 222.132812"
fillOpacity="1"
fillRule="nonzero"
/>
<path
className="icon-fill"
d="M 331.925781 84.105469 C 304.367188 55.941406 269.136719 36.925781 230.835938 29.546875 C 192.535156 22.164062 152.945312 26.757812 117.242188 42.726562 C 81.535156 58.699219 51.375 85.304688 30.695312 119.066406 C 10.015625 152.828125 -0.214844 192.179688 1.332031 231.980469 C 2.882812 271.78125 16.140625 310.175781 39.375 342.15625 C 62.613281 374.132812 94.746094 398.207031 131.582031 411.230469 C 168.414062 424.25 208.234375 425.621094 245.839844 415.152344 C 283.445312 404.683594 317.089844 382.871094 342.375 352.558594 L 265.257812 285.382812 C 253.199219 299.839844 237.152344 310.246094 219.214844 315.238281 C 201.273438 320.230469 182.28125 319.578125 164.710938 313.367188 C 147.140625 307.15625 131.8125 295.671875 120.730469 280.417969 C 109.644531 265.164062 103.320312 246.851562 102.582031 227.867188 C 101.84375 208.882812 106.722656 190.109375 116.589844 174.007812 C 126.453125 157.898438 140.839844 145.210938 157.871094 137.59375 C 174.902344 129.972656 193.785156 127.78125 212.054688 131.304688 C 230.324219 134.824219 247.128906 143.894531 260.277344 157.328125 Z M 331.925781 84.105469"
fillOpacity="1"
fillRule="nonzero"
/>
</g>
</g>
</StyledSvg>
);
};
export default OpenCollectionIcon;

View File

@@ -36,9 +36,9 @@ const InheritableSettingsInput = ({
type="button"
className="px-2 py-1 text-xs rounded-sm outline-none transition-colors duration-100 w-24 h-8 flex items-center justify-between"
style={{
backgroundColor: theme.modal.input.bg,
border: `1px solid ${theme.modal.input.border}`,
color: theme.modal.input.text
backgroundColor: theme.input.bg,
border: `1px solid ${theme.input.border}`,
color: theme.text
}}
>
<span>Inherit</span>
@@ -60,9 +60,9 @@ const InheritableSettingsInput = ({
type="text"
className="block px-2 py-1 pr-6 rounded-sm outline-none transition-colors duration-100 w-24 h-8"
style={{
backgroundColor: theme.modal.input.bg,
border: `1px solid ${theme.modal.input.border}`,
color: theme.modal.input.text
backgroundColor: theme.input.bg,
border: `1px solid ${theme.input.border}`,
color: theme.text
}}
autoComplete="off"
autoCorrect="off"

View File

@@ -58,7 +58,7 @@ const RenameWorkspace = ({ onClose, workspace }) => {
return (
<Portal>
<Modal
size="sm"
size="md"
title="Rename Workspace"
confirmText="Rename"
handleConfirm={onSubmit}

View File

@@ -100,15 +100,14 @@ const StyledWrapper = styled.div`
.default-badge {
padding: 1px 6px;
border-radius: ${(props) => props.theme.border.radius.sm};
background: ${(props) => props.theme.sidebar.badge.bg};
color: ${(props) => props.theme.colors.text.muted};
background: ${(props) => props.theme.background.surface1};
color: ${(props) => props.theme.text};
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
}
.workspace-path {
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.muted};
color: ${(props) => props.theme.text.muted};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -14,6 +14,7 @@ import DeleteWorkspace from './DeleteWorkspace';
import StyledWrapper from './StyledWrapper';
import MenuDropdown from 'ui/MenuDropdown/index';
import Button from 'ui/Button';
import { getRevealInFolderLabel } from 'utils/common/platform';
const ManageWorkspace = () => {
const dispatch = useDispatch();
@@ -133,7 +134,7 @@ const ManageWorkspace = () => {
onClick={() => handleShowInFolder(workspace)}
>
<IconFolder size={14} strokeWidth={1.5} />
<span>Show in folder</span>
<span>{getRevealInFolderLabel()}</span>
</button>
)}
{!isDefault && (

View File

@@ -1,4 +1,5 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const Wrapper = styled.div`
color: ${(props) => props.theme.text};
@@ -40,6 +41,7 @@ const Wrapper = styled.div`
flex-grow: 0;
margin: 3vh 10vw;
margin-top: 50px;
border: 1px solid ${(props) => props.theme.border.border0};
&.modal-sm {
min-width: 300px;
@@ -72,7 +74,6 @@ const Wrapper = styled.div`
background-color: ${(props) => props.theme.modal.title.bg};
font-size: ${(props) => props.theme.font.size.md};
padding: 0.5rem 1rem;
font-weight: 500;
border-top-left-radius: ${(props) => props.theme.border.radius.base};
border-top-right-radius: ${(props) => props.theme.border.radius.base};
@@ -98,7 +99,7 @@ const Wrapper = styled.div`
&:hover {
opacity: 1;
background-color: ${(props) => props.theme.modal.closeButton.hoverBg};
background-color: ${(props) => rgba(props.theme.modal.title.color, 0.1)};
}
}
}
@@ -117,15 +118,24 @@ const Wrapper = styled.div`
box-shadow: none;
transition: border-color ease-in-out 0.1s;
border-radius: ${(props) => props.theme.border.radius.sm};
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;
}
}
select.textbox {
appearance: none;
padding-right: 30px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.5rem center;
cursor: pointer;
}
.bruno-form {
color: ${(props) => props.theme.modal.body.color};
}
@@ -166,6 +176,16 @@ const Wrapper = styled.div`
border-bottom-right-radius: ${(props) => props.theme.border.radius.base};
}
}
input[type='radio'] {
cursor: pointer;
accent-color: ${(props) => props.theme.primary.solid};
}
input[type='checkbox'] {
cursor: pointer;
accent-color: ${(props) => props.theme.primary.solid};
}
`;
export default Wrapper;

View File

@@ -96,9 +96,8 @@ const Modal = ({
return closeModal({ type: 'esc' });
}
case ENTER_KEY_CODE: {
// Skip if a submit button is focused - let native button click handle it to avoid double-fire
const isSubmitButton = event.target?.type === 'submit';
if (!shiftKey && !ctrlKey && !altKey && !metaKey && handleConfirm && !isSubmitButton) {
if (!shiftKey && !ctrlKey && !altKey && !metaKey && handleConfirm && !isSubmitButton && !confirmDisabled) {
return handleConfirm();
}
}
@@ -117,7 +116,7 @@ const Modal = ({
return () => {
document.removeEventListener('keydown', handleKeydown);
};
}, [disableEscapeKey, document, handleConfirm]);
}, [disableEscapeKey, document, handleConfirm, confirmDisabled]);
let classes = 'bruno-modal';
if (isClosing) {

View File

@@ -113,7 +113,7 @@ const Notifications = () => {
size={16}
aria-hidden
strokeWidth={1.5}
className={`mr-2 ${unreadNotifications?.length > 0 ? 'bell' : ''}`}
className={`${unreadNotifications?.length > 0 ? 'bell' : ''}`}
/>
{unreadNotifications.length > 0 && (
<span className="notification-count text-xs">{unreadNotifications.length}</span>

View File

@@ -1,17 +1,9 @@
import React from 'react';
import Font from './Font/index';
import Theme from './Theme/index';
const Display = ({ close }) => {
return (
<div className="flex flex-col my-2 gap-10 w-full">
<div className="w-full flex flex-col gap-2">
<span>
Theme
</span>
<Theme close={close} />
</div>
<div className="h-[1px] bg-[#aaa5] w-full"></div>
<div className="w-fit flex flex-col gap-2">
<Font close={close} />
</div>

View File

@@ -173,7 +173,7 @@ const General = ({ close }) => {
};
return (
<StyledWrapper>
<StyledWrapper className="w-full">
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div className="flex items-center my-2">
<input
@@ -367,6 +367,7 @@ const General = ({ close }) => {
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
readOnly={true}
onChange={formik.handleChange}
value={formik.values.defaultCollectionLocation || ''}
onClick={browseDefaultLocation}

View File

@@ -29,8 +29,7 @@ const StyledWrapper = styled.div`
}
.table-container {
max-height: 400px;
overflow-y: scroll;
overflow-y: auto;
}
.key-button {

View File

@@ -12,11 +12,11 @@ const StyledWrapper = styled.div`
outline: 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;
}
}

View File

@@ -18,56 +18,41 @@ const ProxySettings = ({ close }) => {
console.log(preferences);
const proxySchema = Yup.object({
mode: Yup.string().oneOf(['off', 'on', 'system']),
protocol: Yup.string().required().oneOf(['http', 'https', 'socks4', 'socks5']),
hostname: Yup.string()
.when('enabled', {
is: 'on',
then: (hostname) => hostname.required('Specify the hostname for your proxy.'),
otherwise: (hostname) => hostname.nullable()
})
.max(1024),
port: Yup.number()
.min(1)
.max(65535)
.typeError('Specify port between 1 and 65535')
.nullable()
.transform((_, val) => (val ? Number(val) : null)),
auth: Yup.object()
.when('enabled', {
is: 'on',
then: Yup.object({
enabled: Yup.boolean(),
username: Yup.string()
.when(['enabled'], {
is: true,
then: (username) => username.required('Specify username for proxy authentication.')
})
.max(1024),
password: Yup.string()
.when('enabled', {
is: true,
then: (password) => password.required('Specify password for proxy authentication.')
})
.max(1024)
})
})
.optional(),
bypassProxy: Yup.string().optional().max(1024)
disabled: Yup.boolean().optional(),
inherit: Yup.boolean().required(),
config: Yup.object({
protocol: Yup.string().required().oneOf(['http', 'https', 'socks4', 'socks5']),
hostname: Yup.string().max(1024),
port: Yup.number()
.min(1)
.max(65535)
.typeError('Specify port between 1 and 65535')
.nullable()
.transform((_, val) => (val ? Number(val) : null)),
auth: Yup.object({
disabled: Yup.boolean().optional(),
username: Yup.string().max(1024),
password: Yup.string().max(1024)
}).optional(),
bypassProxy: Yup.string().optional().max(1024)
}).required()
});
const formik = useFormik({
initialValues: {
mode: preferences.proxy.mode,
protocol: preferences.proxy.protocol || 'http',
hostname: preferences.proxy.hostname || '',
port: preferences.proxy.port || 0,
auth: {
enabled: preferences.proxy.auth ? preferences.proxy.auth.enabled || false : false,
username: preferences.proxy.auth ? preferences.proxy.auth.username || '' : '',
password: preferences.proxy.auth ? preferences.proxy.auth.password || '' : ''
},
bypassProxy: preferences.proxy.bypassProxy || ''
disabled: preferences.proxy.disabled || false,
inherit: preferences.proxy.inherit || false,
config: {
protocol: preferences.proxy.config?.protocol || 'http',
hostname: preferences.proxy.config?.hostname || '',
port: preferences.proxy.config?.port || 0,
auth: {
disabled: preferences.proxy.config?.auth?.disabled || false,
username: preferences.proxy.config?.auth?.username || '',
password: preferences.proxy.config?.auth?.password || ''
},
bypassProxy: preferences.proxy.config?.bypassProxy || ''
}
},
validationSchema: proxySchema,
onSubmit: (values) => {
@@ -103,16 +88,19 @@ const ProxySettings = ({ close }) => {
useEffect(() => {
formik.setValues({
mode: preferences.proxy.mode,
protocol: preferences.proxy.protocol || 'http',
hostname: preferences.proxy.hostname || '',
port: preferences.proxy.port || '',
auth: {
enabled: preferences.proxy.auth ? preferences.proxy.auth.enabled || false : false,
username: preferences.proxy.auth ? preferences.proxy.auth.username || '' : '',
password: preferences.proxy.auth ? preferences.proxy.auth.password || '' : ''
},
bypassProxy: preferences.proxy.bypassProxy || ''
disabled: preferences.proxy.disabled || false,
inherit: preferences.proxy.inherit || false,
config: {
protocol: preferences.proxy.config?.protocol || 'http',
hostname: preferences.proxy.config?.hostname || '',
port: preferences.proxy.config?.port || '',
auth: {
disabled: preferences.proxy.config?.auth?.disabled || false,
username: preferences.proxy.config?.auth?.username || '',
password: preferences.proxy.config?.auth?.password || ''
},
bypassProxy: preferences.proxy.config?.bypassProxy || ''
}
});
}, [preferences]);
@@ -137,10 +125,11 @@ const ProxySettings = ({ close }) => {
<input
type="radio"
name="mode"
value="false"
checked={formik.values.mode === 'off'}
value="off"
checked={formik.values.disabled === true}
onChange={(e) => {
formik.setFieldValue('mode', 'off');
formik.setFieldValue('disabled', true);
formik.setFieldValue('inherit', false);
}}
className="mr-1 cursor-pointer"
/>
@@ -150,10 +139,11 @@ const ProxySettings = ({ close }) => {
<input
type="radio"
name="mode"
value="true"
checked={formik.values.mode === 'on'}
value="on"
checked={formik.values.disabled === false && formik.values.inherit === false}
onChange={(e) => {
formik.setFieldValue('mode', 'on');
formik.setFieldValue('disabled', false);
formik.setFieldValue('inherit', false);
}}
className="mr-1 cursor-pointer"
/>
@@ -164,15 +154,18 @@ const ProxySettings = ({ close }) => {
type="radio"
name="mode"
value="system"
checked={formik.values.mode === 'system'}
onChange={formik.handleChange}
checked={formik.values.disabled === false && formik.values.inherit === true}
onChange={(e) => {
formik.setFieldValue('disabled', false);
formik.setFieldValue('inherit', true);
}}
className="mr-1 cursor-pointer"
/>
System Proxy
</label>
</div>
</div>
{formik?.values?.mode === 'system' ? (
{formik.values.disabled === false && formik.values.inherit === true ? (
<div className="mb-3 pt-1 text-muted system-proxy-settings">
<small>
Below values are sourced from your system environment variables and cannot be directly updated in Bruno.<br />
@@ -200,7 +193,7 @@ const ProxySettings = ({ close }) => {
</div>
</div>
) : null}
{formik?.values?.mode === 'on' ? (
{formik.values.disabled === false && formik.values.inherit === false ? (
<>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="protocol">
@@ -210,9 +203,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center">
<input
type="radio"
name="protocol"
name="config.protocol"
value="http"
checked={formik.values.protocol === 'http'}
checked={formik.values.config.protocol === 'http'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -221,9 +214,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
name="config.protocol"
value="https"
checked={formik.values.protocol === 'https'}
checked={formik.values.config.protocol === 'https'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -232,9 +225,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
name="config.protocol"
value="socks4"
checked={formik.values.protocol === 'socks4'}
checked={formik.values.config.protocol === 'socks4'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -243,9 +236,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="protocol"
name="config.protocol"
value="socks5"
checked={formik.values.protocol === 'socks5'}
checked={formik.values.config.protocol === 'socks5'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -254,92 +247,94 @@ const ProxySettings = ({ close }) => {
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="hostname">
<label className="settings-label" htmlFor="config.hostname">
Hostname
</label>
<input
id="hostname"
id="config.hostname"
type="text"
name="hostname"
name="config.hostname"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.hostname || ''}
value={formik.values.config.hostname || ''}
/>
{formik.touched.hostname && formik.errors.hostname ? (
<div className="ml-3 text-red-500">{formik.errors.hostname}</div>
{formik.touched.config?.hostname && formik.errors.config?.hostname ? (
<div className="ml-3 text-red-500">{formik.errors.config.hostname}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="port">
<label className="settings-label" htmlFor="config.port">
Port
</label>
<input
id="port"
id="config.port"
type="number"
name="port"
name="config.port"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.port}
value={formik.values.config.port}
/>
{formik.touched.port && formik.errors.port ? (
<div className="ml-3 text-red-500">{formik.errors.port}</div>
{formik.touched.config?.port && formik.errors.config?.port ? (
<div className="ml-3 text-red-500">{formik.errors.config.port}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.enabled">
<label className="settings-label" htmlFor="config.auth.disabled">
Auth
</label>
<input
type="checkbox"
name="auth.enabled"
checked={formik.values.auth.enabled}
onChange={formik.handleChange}
name="config.auth.disabled"
checked={!formik.values.config.auth.disabled}
onChange={(e) => {
formik.setFieldValue('config.auth.disabled', !e.target.checked);
}}
/>
</div>
<div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.username">
<label className="settings-label" htmlFor="config.auth.username">
Username
</label>
<input
id="auth.username"
id="config.auth.username"
type="text"
name="auth.username"
name="config.auth.username"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.username}
value={formik.values.config.auth.username}
onChange={formik.handleChange}
/>
{formik.touched.auth?.username && formik.errors.auth?.username ? (
<div className="ml-3 text-red-500">{formik.errors.auth.username}</div>
{formik.touched.config?.auth?.username && formik.errors.config?.auth?.username ? (
<div className="ml-3 text-red-500">{formik.errors.config.auth.username}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="auth.password">
<label className="settings-label" htmlFor="config.auth.password">
Password
</label>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[2.25rem] relative">
<input
id="auth.password"
id="config.auth.password"
type={passwordVisible ? `text` : 'password'}
name="auth.password"
name="config.auth.password"
className="outline-none w-[10.5rem] bg-transparent"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
value={formik.values.config.auth.password}
onChange={formik.handleChange}
/>
<button
@@ -350,29 +345,29 @@ const ProxySettings = ({ close }) => {
{passwordVisible ? <IconEyeOff size={18} strokeWidth={2} /> : <IconEye size={18} strokeWidth={2} />}
</button>
</div>
{formik.touched.auth?.password && formik.errors.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.auth.password}</div>
{formik.touched.config?.auth?.password && formik.errors.config?.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.config.auth.password}</div>
) : null}
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="bypassProxy">
<label className="settings-label" htmlFor="config.bypassProxy">
Proxy Bypass
</label>
<input
id="bypassProxy"
id="config.bypassProxy"
type="text"
name="bypassProxy"
name="config.bypassProxy"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.bypassProxy || ''}
value={formik.values.config.bypassProxy || ''}
/>
{formik.touched.bypassProxy && formik.errors.bypassProxy ? (
<div className="ml-3 text-red-500">{formik.errors.bypassProxy}</div>
{formik.touched.config?.bypassProxy && formik.errors.config?.bypassProxy ? (
<div className="ml-3 text-red-500">{formik.errors.config.bypassProxy}</div>
) : null}
</div>
</>

View File

@@ -41,7 +41,7 @@ const StyledWrapper = styled.div`
min-height: 70vh;
max-height: 70vh;
overflow-y: auto;
max-width: 50vw;
width: clamp(300px, 45vw, 550px);
}
input[type="checkbox"],

View File

@@ -0,0 +1,102 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
.appearance-container {
padding: 8px 0 16px 0;
}
.theme-mode-option {
border: 1px solid ${(props) => props.theme.input.border};
border-radius: ${(props) => props.theme.border.radius.md};
box-shadow: none;
padding: 6px 8px;
width: auto;
&.selected {
border: 1px solid ${(props) => props.theme.accents.primary};
background: ${(props) => rgba(props.theme.accents.primary, 0.07)};
cursor: default;
}
&:hover {
border: 1px solid ${(props) => props.theme.accents.primary};
}
}
.theme-variant-label {
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.muted};
margin-bottom: 12px;
}
.theme-variants {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.theme-variant-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px 8px;
border: 2px solid ${(props) => props.theme.input.border};
border-radius: ${(props) => props.theme.border.radius.md};
cursor: pointer;
transition: all 0.15s ease;
min-width: 155px;
&:hover {
border-color: ${(props) => props.theme.input.focusBorder};
}
&.selected {
border-color: ${(props) => props.theme.accents.primary};
background: ${(props) => rgba(props.theme.accents.primary, 0.07)};
cursor: default;
}
}
.theme-preview {
width: 60px;
height: 40px;
border-radius: ${(props) => props.theme.border.radius.sm};
margin-bottom: 8px;
display: flex;
overflow: hidden;
}
.theme-preview-sidebar {
width: 15px;
height: 100%;
}
.theme-preview-main {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
padding: 4px;
gap: 3px;
}
.theme-preview-line {
height: 4px;
border-radius: 2px;
width: 80%;
}
.theme-variant-name {
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.text};
}
.section-divider {
height: 1px;
background: ${(props) => props.theme.input.border};
margin: 15px 0;
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,130 @@
import React from 'react';
import { rgba } from 'polished';
import { useTheme } from 'providers/Theme';
import themes, { getLightThemes, getDarkThemes } from 'themes/index';
import { IconBrightnessUp, IconMoon, IconDeviceDesktop } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
const ThemePreview = ({ themeId, isDark }) => {
const theme = themes[themeId] || themes[isDark ? 'dark' : 'light'];
const bgColor = theme.background.base;
const sidebarColor = theme.sidebar.bg;
const lineColor = rgba(theme.brand, 0.5);
return (
<div className="theme-preview" style={{ background: bgColor, border: `1px solid ${lineColor}` }}>
<div className="theme-preview-sidebar" style={{ background: sidebarColor }} />
<div className="theme-preview-main">
<div className="theme-preview-line" style={{ background: lineColor }} />
<div className="theme-preview-line" style={{ background: lineColor, width: '60%' }} />
<div className="theme-preview-line" style={{ background: lineColor, width: '70%' }} />
</div>
</div>
);
};
const ThemeVariantCard = ({ theme, isSelected, onClick }) => {
const isDark = theme.mode === 'dark';
return (
<div className={`theme-variant-card ${isSelected ? 'selected' : ''}`} onClick={onClick}>
<ThemePreview themeId={theme.id} isDark={isDark} />
<span className="theme-variant-name">{theme.name}</span>
</div>
);
};
const Themes = () => {
const {
storedTheme,
setStoredTheme,
themeVariantLight,
setThemeVariantLight,
themeVariantDark,
setThemeVariantDark
} = useTheme();
const lightThemes = getLightThemes();
const darkThemes = getDarkThemes();
const themeModes = [
{ key: 'light', label: 'Light', icon: IconBrightnessUp },
{ key: 'dark', label: 'Dark', icon: IconMoon },
{ key: 'system', label: 'System', icon: IconDeviceDesktop }
];
const handleModeChange = (mode) => {
setStoredTheme(mode);
};
const renderThemeVariants = (themes, selectedVariant, onSelect, label) => (
<div className="theme-variant-section">
<div className="theme-variant-label">{label}</div>
<div className="theme-variants">
{themes.map((theme) => (
<ThemeVariantCard
key={theme.id}
theme={theme}
isSelected={selectedVariant === theme.id}
onClick={() => onSelect(theme.id)}
/>
))}
</div>
</div>
);
return (
<StyledWrapper>
<div className="flex flex-col gap-4 w-full appearance-container">
<div>
<div className="section-header">Appearance</div>
</div>
<div className="flex gap-3 theme-mode-selector justify-start">
{themeModes.map((mode) => {
const Icon = mode.icon;
const isSelected = storedTheme === mode.key;
return (
<button
key={mode.key}
onClick={() => handleModeChange(mode.key)}
className={`theme-mode-option relative ${isSelected ? 'selected' : ''}`}
>
<div className="flex items-center justify-start gap-2">
<Icon size={16} strokeWidth={1.5} />
<span>{mode.label}</span>
</div>
</button>
);
})}
</div>
<div className="section-divider" />
{storedTheme === 'light' && (
<>
{renderThemeVariants(lightThemes, themeVariantLight, setThemeVariantLight, 'Light Theme')}
</>
)}
{storedTheme === 'dark' && (
<>
{renderThemeVariants(darkThemes, themeVariantDark, setThemeVariantDark, 'Dark Theme')}
</>
)}
{storedTheme === 'system' && (
<>
{renderThemeVariants(lightThemes, themeVariantLight, setThemeVariantLight, 'Light Theme')}
<div className="section-divider" />
{renderThemeVariants(darkThemes, themeVariantDark, setThemeVariantDark, 'Dark Theme')}
</>
)}
</div>
</StyledWrapper>
);
};
export default Themes;

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