Compare commits

..

1 Commits

Author SHA1 Message Date
Bijin A B
33439b3840 chore: playwright fix 2025-12-25 14:47:56 +05:30
463 changed files with 5166 additions and 20183 deletions

210
package-lock.json generated
View File

@@ -22,14 +22,11 @@
"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.7.0",
"@opencollection/types": "0.3.0",
"@playwright/test": "^1.51.1",
"@rollup/plugin-json": "^6.1.0",
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
@@ -1607,7 +1604,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -1625,7 +1622,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.22.6",
@@ -1642,7 +1639,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -1660,7 +1657,7 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/@babel/helper-globals": {
@@ -1740,7 +1737,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -1815,7 +1812,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.25.9",
@@ -1858,7 +1855,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1875,7 +1872,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -1891,7 +1888,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -1907,7 +1904,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1925,7 +1922,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -1960,7 +1957,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -2059,7 +2056,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2075,7 +2072,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2257,7 +2254,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
@@ -2274,7 +2271,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2290,7 +2287,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2308,7 +2305,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.25.9",
@@ -2326,7 +2323,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2342,7 +2339,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2374,7 +2371,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.25.9",
@@ -2391,7 +2388,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -2412,7 +2409,7 @@
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -2422,7 +2419,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2439,7 +2436,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2455,7 +2452,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2472,7 +2469,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2488,7 +2485,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2505,7 +2502,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2521,7 +2518,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2537,7 +2534,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2569,7 +2566,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2586,7 +2583,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.25.9",
@@ -2604,7 +2601,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2620,7 +2617,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2636,7 +2633,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2652,7 +2649,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2668,7 +2665,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2701,7 +2698,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2720,7 +2717,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-transforms": "^7.25.9",
@@ -2737,7 +2734,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -2754,7 +2751,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2785,7 +2782,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2801,7 +2798,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-compilation-targets": "^7.25.9",
@@ -2819,7 +2816,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -2836,7 +2833,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2868,7 +2865,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -2900,7 +2897,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.25.9",
@@ -2918,7 +2915,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3003,7 +3000,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -3020,7 +3017,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3037,7 +3034,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3053,7 +3050,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3069,7 +3066,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9",
@@ -3086,7 +3083,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3102,7 +3099,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3118,7 +3115,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3153,7 +3150,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.25.9"
@@ -3169,7 +3166,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3186,7 +3183,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3203,7 +3200,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
@@ -3220,7 +3217,7 @@
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
"integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.26.0",
@@ -3321,7 +3318,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
@@ -6100,9 +6097,9 @@
}
},
"node_modules/@opencollection/types": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.7.0.tgz",
"integrity": "sha512-CSwdaHNPa2bNNBAOy++t6W9gBTExUJZW3aPkWyhAjasusThbvjymD/0uCLR50gCXSs0ezv61jsd19m9x+2DMtQ==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@opencollection/types/-/types-0.3.0.tgz",
"integrity": "sha512-kw+co3sM4ATDQI85lgy5UmOilEHFVdNYvOjZXmnSw6PUDUTAGBiaZNdwdSXwp//o4IwKbTQb8/0I3UdjLKh+qA==",
"dev": true,
"license": "MIT"
},
@@ -9550,7 +9547,6 @@
"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",
@@ -9570,7 +9566,6 @@
"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"
@@ -9583,7 +9578,6 @@
"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",
@@ -9598,7 +9592,6 @@
"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": {
@@ -9709,7 +9702,6 @@
"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": {
@@ -10002,7 +9994,6 @@
"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": {
@@ -10025,7 +10016,6 @@
"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": "*",
@@ -10036,7 +10026,6 @@
"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": {
@@ -11411,7 +11400,6 @@
"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"
@@ -11856,7 +11844,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.22.6",
@@ -11871,7 +11859,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.2",
@@ -11885,7 +11873,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/helper-define-polyfill-provider": "^0.6.3"
@@ -13775,7 +13763,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"browserslist": "^4.24.2"
@@ -14924,7 +14912,6 @@
"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": {
@@ -15997,7 +15984,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true,
"devOptional": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -18873,7 +18860,7 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -20883,15 +20870,6 @@
"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",
@@ -21339,7 +21317,6 @@
"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"
@@ -22954,7 +22931,7 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true,
"devOptional": true,
"license": "MIT"
},
"node_modules/path-scurry": {
@@ -25481,14 +25458,14 @@
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
"dev": true,
"devOptional": 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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2"
@@ -25507,7 +25484,7 @@
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
"integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.4"
@@ -25517,7 +25494,7 @@
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"regenerate": "^1.4.2",
@@ -25535,14 +25512,14 @@
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
"dev": true,
"devOptional": 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==",
"dev": true,
"devOptional": true,
"license": "BSD-2-Clause",
"dependencies": {
"jsesc": "~3.0.2"
@@ -25555,7 +25532,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
"dev": true,
"devOptional": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
@@ -25751,7 +25728,7 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -28069,7 +28046,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -29256,7 +29233,7 @@
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -29317,7 +29294,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -29327,7 +29304,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"unicode-canonical-property-names-ecmascript": "^2.0.0",
@@ -29341,7 +29318,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -29351,7 +29328,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==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -30460,7 +30437,6 @@
"json5": "^2.2.3",
"jsonc-parser": "^3.2.1",
"jsonpath-plus": "^10.3.0",
"jsonschema": "^1.5.0",
"know-your-http-well": "^0.5.0",
"linkify-it": "^5.0.0",
"lodash": "^4.17.21",
@@ -33081,7 +33057,6 @@
"@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",
@@ -33628,7 +33603,7 @@
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@opencollection/types": "~0.5.0",
"@opencollection/types": "0.3.0",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^23.0.2",
"@rollup/plugin-node-resolve": "^15.0.1",
@@ -33644,13 +33619,6 @@
"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,16 +20,17 @@
],
"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",
@@ -48,13 +49,12 @@
"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": "node ./scripts/dev.js",
"dev": "concurrently --kill-others \"npm run dev:web\" \"npm run dev:electron\"",
"watch": "npm run dev:watch",
"dev:watch": "node ./scripts/dev-hot-reload.js",
"dev:web": "npm run dev --workspace=packages/bruno-app",
@@ -62,7 +62,6 @@
"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",
@@ -98,8 +97,5 @@
"json-schema-typed": "8.0.1"
}
}
},
"dependencies": {
"ajv": "^8.17.1"
}
}

View File

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

View File

@@ -52,7 +52,6 @@
"json5": "^2.2.3",
"jsonc-parser": "^3.2.1",
"jsonpath-plus": "^10.3.0",
"jsonschema": "^1.5.0",
"know-your-http-well": "^0.5.0",
"linkify-it": "^5.0.0",
"lodash": "^4.17.21",

View File

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

View File

@@ -37,7 +37,7 @@ const FileEditor = ({ apiSpec }) => {
/>
<IconDeviceFloppy
onClick={onSave}
color={hasChanges ? theme.draftColor : theme.requestTabs.icon.color}
color={hasChanges ? theme.colors.text.yellow : 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.text.danger};
color: ${(props) => props.theme.colors.danger};
&:hover {
background-color: ${(props) => props.theme.colors.bg.danger};
color: white;

View File

@@ -246,6 +246,12 @@ 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.primary.text};
color: ${(props) => props.theme.colors.text.yellow};
}
.dropdown-icon {

View File

@@ -1,5 +1,4 @@
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';
@@ -22,8 +21,7 @@ const BulkEditor = ({ params, onChange, onToggle, onSave, onRun }) => {
<CodeEditor
mode="text/plain"
theme={displayedTheme}
font={get(preferences, 'font.codeFont', 'default')}
fontSize={get(preferences, 'font.codeFontSize')}
font={preferences.codeFont || 'default'}
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: ${(props) => props.theme.colors.text.danger} !important;
color: #d32f2f !important;
text-decoration: underline;
}
.CodeMirror-lint-line-warning .CodeMirror-linenumber {
color: ${(props) => props.theme.colors.text.warning} !important;
color: #f57c00 !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-2 {
span.cm-variable {
color: ${(props) => props.theme.codemirror.tokens.variable} !important;
}
span.cm-keyword {
@@ -128,20 +128,14 @@ 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: ${(props) => props.theme.codemirror.variable.valid} !important;
color: #5fad89 !important; /* Soft sage */
}
.cm-variable-invalid {
color: ${(props) => props.theme.codemirror.variable.invalid} !important;
color: #d17b7b !important; /* Soft coral */
}
.CodeMirror-search-hint {

View File

@@ -1,5 +1,4 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
.bruno-search-bar {
@@ -10,15 +9,15 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
flex-wrap: nowrap;
gap: 0;
padding: 1px 3px;
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);
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 {
@@ -39,7 +38,7 @@ const StyledWrapper = styled.div`
padding: 0 1px;
margin: 0 1px;
cursor: pointer;
color: ${(props) => props.theme.colors.text.subtext1};
color: #aaa;
border-radius: 3px;
height: 18px;
width: 18px;
@@ -52,14 +51,27 @@ const StyledWrapper = styled.div`
min-width: 28px;
text-align: center;
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.subtext1};
color: #aaa;
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: ${(props) => props.theme.colors.text.subtext2};
color: inherit;
border: none;
outline: none;
font-size: ${(props) => props.theme.font.size.base};
@@ -80,9 +92,7 @@ const StyledWrapper = styled.div`
}
.searchbar-icon-btn.active {
color: ${(props) => props.theme.brand};
background-color: ${(props) => rgba(props.theme.brand, 0.1)};
font-weight: 500;
color: #f39c12 !important;
}
`;

View File

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

View File

@@ -2,8 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
label {
font-size: ${(props) => props.theme.font.size.sm};
color: ${(props) => props.theme.colors.text.subtext1};
font-size: ${(props) => props.theme.font.size.base};
}
.single-line-editor-wrapper {
@@ -14,8 +13,7 @@ const Wrapper = styled.div`
}
.auth-placement-selector {
font-size: ${(props) => props.theme.font.size.sm};
padding: 0.2rem 0px;
padding: 0.5rem 0px;
border-radius: 3px;
border: solid 1px ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.input.bg};
@@ -39,6 +37,7 @@ 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,31 +57,29 @@ const ApiKeyAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Key</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Key</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={apikeyAuth.key || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAuthChange('key', val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Value</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Value</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={apikeyAuth.value || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAuthChange('value', val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Add To</label>
<label className="block font-medium mb-2">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.primary.text};
color: ${(props) => props.theme.colors.text.yellow};
.caret {
color: rgb(140, 140, 140);

View File

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

View File

@@ -123,20 +123,19 @@ const AwsV4Auth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Access Key ID</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Access Key ID</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={awsv4Auth.accessKeyId || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleAccessKeyIdChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Secret Access Key</label>
<div className="single-line-editor-wrapper mb-3 flex items-center">
<label className="block font-medium mb-2">Secret Access Key</label>
<div className="single-line-editor-wrapper mb-2 flex items-center">
<SingleLineEditor
value={awsv4Auth.secretAccessKey || ''}
theme={storedTheme}
@@ -144,56 +143,51 @@ 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 mb-1">Session Token</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Session Token</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={awsv4Auth.sessionToken || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleSessionTokenChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Service</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Service</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={awsv4Auth.service || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleServiceChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Region</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Region</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={awsv4Auth.region || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleRegionChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Profile Name</label>
<div className="single-line-editor-wrapper">
<label className="block font-medium mb-2">Profile Name</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={awsv4Auth.profileName || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleProfileNameChange(val)}
collection={collection}
isCompact
/>
</div>
</StyledWrapper>

View File

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

View File

@@ -47,19 +47,18 @@ const BasicAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={basicAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Password</label>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={basicAuth.password || ''}
@@ -68,7 +67,6 @@ const BasicAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="basic-password" warningMessage={warningMessage} />}
</div>

View File

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

View File

@@ -33,7 +33,7 @@ const BearerAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Token</label>
<label className="block font-medium mb-2">Token</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={bearerToken}
@@ -42,7 +42,6 @@ const BearerAuth = ({ collection }) => {
onChange={(val) => handleTokenChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="bearer-token" warningMessage={warningMessage} />}
</div>

View File

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

View File

@@ -47,19 +47,18 @@ const DigestAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={digestAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Password</label>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={digestAuth.password || ''}
@@ -68,7 +67,6 @@ const DigestAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="digest-password" warningMessage={warningMessage} />}
</div>

View File

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

View File

@@ -64,20 +64,19 @@ const NTLMAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={ntlmAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUsernameChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Password</label>
<div className="single-line-editor-wrapper mb-3 flex items-center">
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={ntlmAuth.password || ''}
theme={storedTheme}
@@ -85,12 +84,11 @@ const NTLMAuth = ({ collection }) => {
onChange={(val) => handlePasswordChange(val)}
collection={collection}
isSecret={true}
isCompact
/>
{showWarning && <SensitiveFieldWarning fieldName="ntlm-password" warningMessage={warningMessage} />}
</div>
<label className="block mb-1">Domain</label>
<label className="block font-medium mb-2">Domain</label>
<div className="single-line-editor-wrapper">
<SingleLineEditor
value={ntlmAuth.domain || ''}
@@ -98,7 +96,6 @@ const NTLMAuth = ({ collection }) => {
onSave={handleSave}
onChange={(val) => handleDomainChange(val)}
collection={collection}
isCompact
/>
</div>
</StyledWrapper>

View File

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

View File

@@ -47,19 +47,18 @@ const WsseAuth = ({ collection }) => {
return (
<StyledWrapper className="mt-2 w-full">
<label className="block mb-1">Username</label>
<div className="single-line-editor-wrapper mb-3">
<label className="block font-medium mb-2">Username</label>
<div className="single-line-editor-wrapper mb-2">
<SingleLineEditor
value={wsseAuth.username || ''}
theme={storedTheme}
onSave={handleSave}
onChange={(val) => handleUserChange(val)}
collection={collection}
isCompact
/>
</div>
<label className="block mb-1">Password</label>
<label className="block font-medium mb-2">Password</label>
<div className="single-line-editor-wrapper flex items-center">
<SingleLineEditor
value={wsseAuth.password || ''}
@@ -68,7 +67,6 @@ 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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}

View File

@@ -9,8 +9,6 @@ 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();
@@ -57,17 +55,17 @@ const Docs = ({ collection }) => {
<div className="flex flex-row gap-2 items-center justify-center">
{isEditing ? (
<>
<Button type="button" color="secondary" onClick={handleDiscardChanges}>
Cancel
</Button>
<Button type="button" onClick={onSave}>
<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}>
Save
</Button>
</button>
</>
) : (
<ActionIcon className="editing-mode" onClick={toggleViewMode}>
<IconEdit className="cursor-pointer" size={16} strokeWidth={1.5} />
</ActionIcon>
<div className="editing-mode" role="tab" onClick={toggleViewMode}>
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} />
</div>
)}
</div>
</div>

View File

@@ -1,53 +0,0 @@
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,13 +1,11 @@
import React from 'react';
import { getTotalRequestCountInCollection } from 'utils/collections/';
import { IconFolder, IconWorld, IconApi, IconShare, IconBook } from '@tabler/icons';
import { IconFolder, IconWorld, IconApi, IconShare } 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();
@@ -16,7 +14,6 @@ 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);
@@ -28,17 +25,17 @@ const Info = ({ collection }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-fit">
<div 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="icon-box location flex-shrink-0 p-3 rounded-lg">
<IconFolder className="w-5 h-5" stroke={1.5} />
<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>
<div className="ml-4">
<div className="font-medium">Location</div>
<div className="mt-1 text-muted break-all">
<div className="mt-1 text-muted break-all text-xs">
{collection.pathname}
</div>
</div>
@@ -46,15 +43,15 @@ const Info = ({ collection }) => {
{/* Environments Row */}
<div className="flex items-start">
<div className="icon-box environments flex-shrink-0 p-3 rounded-lg">
<IconWorld className="w-5 h-5" stroke={1.5} />
<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>
<div className="ml-4">
<div className="font-medium">Environments</div>
<div className="font-medium text-sm">Environments</div>
<div className="mt-1 flex flex-col gap-1">
<button
type="button"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
className="text-sm text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
@@ -69,7 +66,7 @@ const Info = ({ collection }) => {
</button>
<button
type="button"
className="text-link cursor-pointer hover:underline text-left bg-transparent"
className="text-sm text-link cursor-pointer hover:underline text-left bg-transparent"
onClick={() => {
dispatch(
addTab({
@@ -88,12 +85,12 @@ const Info = ({ collection }) => {
{/* Requests Row */}
<div className="flex items-start">
<div className="icon-box requests flex-shrink-0 p-3 rounded-lg">
<IconApi className="w-5 h-5" stroke={1.5} />
<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>
<div className="ml-4">
<div className="font-medium">Requests</div>
<div className="mt-1 text-muted">
<div className="mt-1 text-muted text-xs">
{
isCollectionLoading ? `${totalItems - itemsLoadingCount} out of ${totalItems} requests in the collection loaded` : `${totalRequestsInCollection} request${totalRequestsInCollection !== 1 ? 's' : ''} in collection`
}
@@ -102,33 +99,20 @@ const Info = ({ collection }) => {
</div>
<div className="flex items-start group cursor-pointer" onClick={handleToggleShowShareCollectionModal(true)}>
<div className="icon-box share flex-shrink-0 p-3 rounded-lg">
<IconShare className="w-5 h-5" stroke={1.5} />
<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>
<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">
<div className="group-hover:underline text-link text-xs">
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>
</StyledWrapper>
</div>
);
};

View File

@@ -1,29 +1,22 @@
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.table.border};
border-left: 1px solid ${(props) => props.theme.table.border};
border-right: 1px solid ${(props) => props.theme.table.border};
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-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 {
color: ${(props) => props.theme.table.thead.color} !important;
background: ${(props) => props.theme.sidebar.bg};
background-color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.bg};
color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.color};
}
}
}

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">
<IconAlertTriangle size={16} className="warning-icon" />
<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" />
<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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.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 text-muted">
<div className="text-xs mb-4 mt-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,151 +8,6 @@ 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="flex items-center" htmlFor="protoFiles">
<label className="font-medium flex items-center" htmlFor="protoFiles">
Proto Files (
{protoFiles.length}
)
<span id="proto-files-tooltip" className="ml-2">
<IconAlertCircle size={16} className="tooltip-icon" />
<IconAlertCircle size={16} className="text-gray-500 cursor-pointer" />
</span>
<Tooltip
anchorId="proto-files-tooltip"
@@ -131,7 +131,7 @@ const ProtobufSettings = ({ collection }) => {
<div>
{protoFiles.some((file) => !file.exists) && (
<div className="error-message text-xs mb-2 flex items-center p-2" data-testid="protobuf-invalid-files-message">
<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">
<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>
<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">
File
</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">
Path
</th>
<th className="text-right">
<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">
Actions
</th>
</tr>
@@ -154,10 +154,10 @@ const ProtobufSettings = ({ collection }) => {
<tbody>
{protoFiles.length === 0 ? (
<tr>
<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>
<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>
</div>
</td>
</tr>
@@ -167,27 +167,27 @@ const ProtobufSettings = ({ collection }) => {
return (
<tr key={index}>
<td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<div className="flex items-center">
<IconFile size={16} className="file-icon mr-2" />
<span className="file-name" data-testid="protobuf-proto-file-name">
<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">
{getBasename(collection.pathname, file.path)}
</span>
{!isValid && <IconAlertCircle size={12} className="invalid-indicator ml-2" />}
{!isValid && <IconAlertCircle size={12} className="text-red-600 dark:text-red-400 ml-2" />}
</div>
</td>
<td>
<div className="path-text">
<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">
{file.path}
</div>
</td>
<td className="text-right">
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2 text-right">
<div className="flex items-center justify-end space-x-1">
{!isValid && (
<button
type="button"
onClick={() => handleReplaceProtoFile(index)}
className="action-button replace-button"
className="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 p-1 rounded"
title="Replace file"
>
<IconFileImport size={14} />
@@ -196,7 +196,7 @@ const ProtobufSettings = ({ collection }) => {
<button
type="button"
onClick={() => handleRemoveProtoFile(index)}
className="action-button remove-button"
className="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 p-1 rounded"
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="flex items-center" htmlFor="importPaths">
<label className="font-medium flex items-center" htmlFor="importPaths">
Import Paths (
{importPaths.length}
)
<span id="import-paths-tooltip" className="ml-2">
<IconAlertCircle size={16} className="tooltip-icon" />
<IconAlertCircle size={16} className="text-gray-500 cursor-pointer" />
</span>
<Tooltip
anchorId="import-paths-tooltip"
@@ -238,7 +238,7 @@ const ProtobufSettings = ({ collection }) => {
<div>
{importPaths.some((path) => !path.exists) && (
<div className="error-message text-xs mb-2 flex items-center p-2" data-testid="protobuf-invalid-import-paths-message">
<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">
<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>
<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">
Directory
</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">
Path
</th>
<th className="text-right">
<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">
Actions
</th>
</tr>
@@ -263,10 +263,10 @@ const ProtobufSettings = ({ collection }) => {
<tbody>
{importPaths.length === 0 ? (
<tr>
<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>
<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>
</div>
</td>
</tr>
@@ -276,37 +276,37 @@ const ProtobufSettings = ({ collection }) => {
return (
<tr key={index}>
<td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<input
type="checkbox"
checked={importPath.enabled}
onChange={() => handleToggleImportPath(index)}
className="h-4 w-4"
className="h-4 w-4 text-gray-600 focus:ring-gray-500 border-gray-300 dark:border-gray-600 rounded"
title={importPath.enabled ? 'Disable this import path' : 'Enable this import path'}
data-testid="protobuf-import-path-checkbox"
/>
</td>
<td>
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2">
<div className="flex items-center">
<IconFolder size={16} className="folder-icon mr-2" />
<span className="directory-name">
<IconFolder size={16} className="text-gray-500 dark:text-gray-400 mr-2" />
<span className="font-medium text-gray-900 dark:text-gray-100">
{getBasename(collection.pathname, importPath.path)}
</span>
{!isValid && <IconAlertCircle size={12} className="invalid-indicator ml-2" />}
{!isValid && <IconAlertCircle size={12} className="text-red-600 dark:text-red-400 ml-2" />}
</div>
</td>
<td>
<div className="path-text">
<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">
{importPath.path}
</div>
</td>
<td className="text-right">
<td className="border border-gray-200 dark:border-gray-700 px-3 py-2 text-right">
<div className="flex items-center justify-end space-x-1">
{!isValid && (
<button
type="button"
onClick={() => handleReplaceImportPath(index)}
className="action-button replace-button"
className="text-red-600 hover:text-red-900 dark:text-red-400 dark:hover:text-red-300 p-1 rounded"
title="Replace directory"
>
<IconFileImport size={14} />
@@ -315,7 +315,7 @@ const ProtobufSettings = ({ collection }) => {
<button
type="button"
onClick={() => handleRemoveImportPath(index)}
className="action-button remove-button"
className="text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-300 p-1 rounded"
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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}

View File

@@ -12,19 +12,7 @@ import Button from 'ui/Button';
const ProxySettings = ({ collection }) => {
const dispatch = useDispatch();
const initialProxyConfig = {
inherit: true,
config: {
protocol: 'http',
hostname: '',
port: '',
auth: {
username: '',
password: ''
},
bypassProxy: ''
}
};
const initialProxyConfig = { enabled: 'global', protocol: 'http', hostname: '', port: '', auth: { enabled: false, username: '', password: '' }, bypassProxy: '' };
// Get proxy from draft.brunoConfig if it exists, otherwise from brunoConfig
const currentProxyConfig = collection.draft?.brunoConfig
@@ -94,57 +82,34 @@ const ProxySettings = ({ collection }) => {
const handleEnabledChange = (e) => {
const value = e.target.value;
// 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 });
}
// Convert string to boolean or keep as 'global'
const enabled = value === 'true' ? true : value === 'false' ? false : 'global';
updateProxy({ enabled });
};
const handleProtocolChange = (e) => {
updateProxy({
config: {
...currentProxyConfig.config,
protocol: e.target.value
}
});
updateProxy({ protocol: e.target.value });
};
const handleHostnameChange = (e) => {
const hostname = e.target.value;
if (validateHostnameOnChange(hostname)) {
updateProxy({
config: {
...currentProxyConfig.config,
hostname
}
});
updateProxy({ hostname });
}
};
const handlePortChange = (e) => {
const port = e.target.value ? Number(e.target.value) : '';
if (validatePortOnChange(port)) {
updateProxy({
config: {
...currentProxyConfig.config,
port
}
});
updateProxy({ port });
}
};
const handleAuthEnabledChange = (e) => {
updateProxy({
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
disabled: !e.target.checked
}
auth: {
...currentProxyConfig.auth,
enabled: e.target.checked
}
});
};
@@ -153,12 +118,9 @@ const ProxySettings = ({ collection }) => {
const username = e.target.value;
if (validateAuthUsernameOnChange(username)) {
updateProxy({
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
username
}
auth: {
...currentProxyConfig.auth,
username
}
});
}
@@ -168,12 +130,9 @@ const ProxySettings = ({ collection }) => {
const password = e.target.value;
if (validateAuthPasswordOnChange(password)) {
updateProxy({
config: {
...currentProxyConfig.config,
auth: {
...currentProxyConfig.config.auth,
password
}
auth: {
...currentProxyConfig.auth,
password
}
});
}
@@ -182,19 +141,11 @@ const ProxySettings = ({ collection }) => {
const handleBypassProxyChange = (e) => {
const bypassProxy = e.target.value;
if (validateBypassProxyOnChange(bypassProxy)) {
updateProxy({
config: {
...currentProxyConfig.config,
bypassProxy
}
});
updateProxy({ bypassProxy });
}
};
// 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');
const enabledValue = currentProxyConfig.enabled === true ? 'true' : currentProxyConfig.enabled === false ? 'false' : 'global';
return (
<StyledWrapper className="h-full w-full">
@@ -206,9 +157,9 @@ const ProxySettings = ({ collection }) => {
<InfoTip infotipId="request-var">
<div>
<ul>
<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>
<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>
</ul>
</div>
</InfoTip>
@@ -218,12 +169,12 @@ const ProxySettings = ({ collection }) => {
<input
type="radio"
name="enabled"
value="inherit"
checked={enabledValue === 'inherit'}
value="global"
checked={enabledValue === 'global'}
onChange={handleEnabledChange}
className="mr-1"
/>
inherit
global
</label>
<label className="flex items-center ml-4">
<input
@@ -249,168 +200,164 @@ const ProxySettings = ({ collection }) => {
</label>
</div>
</div>
{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>
<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
id="hostname"
type="text"
name="hostname"
className="block textbox"
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]"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handleHostnameChange}
value={currentProxyConfig.config?.hostname || ''}
value={currentProxyConfig.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 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>
</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="mt-6">
<Button type="submit" size="sm" onClick={handleSave}>
Save

View File

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

View File

@@ -56,7 +56,7 @@ const Script = ({ collection }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full">
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<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: ${(props) => props.theme.colors.text.subtext0};
color: var(--color-tab-inactive);
cursor: pointer;
&:focus,
@@ -19,10 +19,6 @@ 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;
@@ -44,11 +40,6 @@ 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: ${(props) => props.theme.colors.text.subtext0};
color: var(--color-tab-inactive);
}
`;

View File

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

View File

@@ -1,26 +1,5 @@
import styled from 'styled-components';
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};
}
`;
const StyledWrapper = styled.div``;
export default StyledWrapper;

View File

@@ -11,7 +11,6 @@ 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));
@@ -226,147 +225,145 @@ const ModifyCookieModal = ({ onClose, domain, cookie }) => {
</div>
)}
>
<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"
<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"
/>
</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>
<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>
{/* 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>
<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>
)}
</div>
</div>
)}
</form>
</StyledWrapper>
{/* 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>
</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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}
@@ -70,92 +70,6 @@ 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="warning-icon" />
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<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 color="secondary" variant="ghost" onClick={onClose}>
<Button size="sm" color="secondary" variant="ghost" onClick={onClose}>
Close
</Button>
</div>
<div>
<Button color="danger" onClick={onClear}>
<Button size="sm" 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="warning-icon" />
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<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 color="secondary" variant="ghost" onClick={onClose}>
<Button size="sm" color="secondary" variant="ghost" onClick={onClose}>
Close
</Button>
</div>
<div>
<Button color="danger" onClick={onDelete}>
<Button size="sm" color="danger" onClick={onDelete}>
Delete
</Button>
</div>
@@ -159,28 +159,27 @@ 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="empty-icon" />
<IconCookieOff size={48} strokeWidth={1.5} className="text-gray-500" />
<h2 className="text-lg font-medium mt-4">No cookies found</h2>
<p className="empty-text mt-2">Add cookies to get started</p>
<Button
<p className="text-gray-500 mt-2">Add cookies to get started</p>
<button
type="submit"
size="sm"
className="mt-8"
icon={<IconCirclePlus strokeWidth={1.5} size={16} />}
className="submit btn btn-sm btn-secondary flex items-center gap-1 mt-8"
onClick={(e) => {
e.stopPropagation();
handleAddCookie();
}}
>
Add Cookie
</Button>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add Cookie</span>
</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="empty-text mt-2">Try a different search term</p>
<p className="text-gray-500 mt-2">Try a different search term</p>
</div>
) : (
// Show cookies list
@@ -191,14 +190,14 @@ const CollectionProperties = ({ onClose }) => {
<Accordion.Header index={i} className="flex items-center">
<div className="flex items-center">
<span>{domainWithCookies.domain}</span>
<span className="domain-count ml-2 text-xs">
<span className="ml-2 text-xs dark:text-gray-300 text-gray-500">
({domainWithCookies.cookies.length}{' '}
{domainWithCookies.cookies.length === 1 ? 'cookie' : 'cookies'})
</span>
<div className="ml-auto flex items-center gap-2">
<button
type="submit"
className="action-button flex items-center gap-1"
className="flex items-center gap-1 text-gray-500 hover:text-gray-950 dark:text-white dark:hover:text-gray-300"
onClick={(e) => {
e.stopPropagation();
handleAddCookie(domainWithCookies.domain);
@@ -211,7 +210,7 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleClearDomainCookies(domainWithCookies.domain);
}}
className="action-button-danger mr-2"
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600 mr-2"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
@@ -222,7 +221,7 @@ const CollectionProperties = ({ onClose }) => {
<div className="flex items-center justify-between">
<table className="w-full">
<thead>
<tr className="text-left">
<tr className="text-left border-b border-gray-200 dark:border-neutral-600 text-gray-700 dark:text-gray-300">
<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>
@@ -234,7 +233,7 @@ const CollectionProperties = ({ onClose }) => {
</thead>
<tbody>
{domainWithCookies.cookies.map((cookie) => (
<tr key={cookie.key}>
<tr key={cookie.key} className="border-b border-gray-200 dark:border-neutral-600 last:border-none">
<td className="py-2 px-4 truncate">
<span id={`cookie-key-${cookie.key}`}>{cookie.key}</span>
<Tooltip
@@ -275,7 +274,8 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleEditCookie(domainWithCookies.domain, cookie);
}}
className="edit-button"
className="text-gray-700 hover:text-gray-950
dark:text-white dark:hover:text-gray-300"
>
<IconEdit strokeWidth={1.5} size={16} />
</button>
@@ -284,7 +284,7 @@ const CollectionProperties = ({ onClose }) => {
e.stopPropagation();
handleDeleteCookie(domainWithCookies.domain, cookie.path, cookie.key);
}}
className="delete-button"
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>

View File

@@ -38,6 +38,20 @@ 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,6 +32,12 @@ const StyledWrapper = styled.div`
}
}
.network-controls {
display: flex;
align-items: center;
gap: 8px;
}
.network-content {
flex: 1;
overflow: hidden;
@@ -75,10 +81,11 @@ const StyledWrapper = styled.div`
display: grid;
grid-template-columns: 80px 80px 150px 1fr 100px 80px 80px;
gap: 12px;
padding: 4px 16px;
padding: 8px 16px;
background: ${(props) => props.theme.console.headerBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
font-size: 10px;
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
color: ${(props) => props.theme.console.titleColor};
text-transform: uppercase;
letter-spacing: 0.5px;
@@ -96,7 +103,8 @@ const StyledWrapper = styled.div`
display: grid;
grid-template-columns: 80px 80px 150px 1fr 100px 80px 80px;
gap: 12px;
padding: 2px 16px;
padding: 6px 16px;
border-bottom: 1px solid ${(props) => props.theme.console.border};
cursor: pointer;
transition: background-color 0.1s ease;
font-size: ${(props) => props.theme.font.size.sm};
@@ -107,8 +115,7 @@ const StyledWrapper = styled.div`
}
&.selected {
padding-left: 13px;
background: ${(props) => props.theme.console.logHoverBg};
background: ${(props) => props.theme.console.buttonHoverBg};
border-left: 3px solid ${(props) => props.theme.console.checkboxColor};
}
}
@@ -116,19 +123,25 @@ const StyledWrapper = styled.div`
.method-badge {
display: inline-flex;
align-items: center;
justify-content: start;
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;
}
.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;
@@ -161,6 +174,120 @@ 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,33 +1,128 @@
import React, { useMemo } from 'react';
import React, { useState, useRef, useEffect, 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 methodLower = method?.toLowerCase() || 'get';
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';
}
};
return (
<span className={`method-badge ${methodLower}`}>
<span
className="method-badge"
style={{ backgroundColor: getMethodColor(method) }}
>
{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">
<span
className="status-badge"
style={{ color: getStatusColor(statusCode) }}
>
{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;
@@ -146,6 +241,22 @@ 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: 2px 8px;
padding: 8px 16px;
background: ${(props) => props.theme.console.headerBg};
border-bottom: 1px solid ${(props) => props.theme.console.border};
flex-shrink: 0;
@@ -27,6 +27,7 @@ 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};
@@ -65,7 +66,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
padding: 8px 16px;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
@@ -91,7 +92,7 @@ const StyledWrapper = styled.div`
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 8px;
padding: 16px;
min-height: 0;
height: 0;
}
@@ -169,7 +170,8 @@ const StyledWrapper = styled.div`
z-index: 10;
td {
padding: 4px 8px;
padding: 8px 12px;
font-weight: 500;
color: ${(props) => props.theme.console.titleColor};
text-transform: uppercase;
font-size: ${(props) => props.theme.font.size.xs};
@@ -196,7 +198,7 @@ const StyledWrapper = styled.div`
}
td {
padding: 2px 8px;
padding: 8px 12px;
vertical-align: top;
word-break: break-word;
}
@@ -254,15 +256,11 @@ const StyledWrapper = styled.div`
}
.response-body-container {
border-radius: ${(props) => props.theme.border.radius.sm};
border-radius: 4px;
overflow: hidden;
height: 400px;
display: flex;
flex-direction: column;
pre {
padding: 8px !important;
}
.w-full.h-full.relative.flex {
height: 100% !important;
@@ -272,7 +270,7 @@ const StyledWrapper = styled.div`
}
div[role="tablist"] {
padding: 4px 8px;
padding: 8px 12px;
border-bottom: 1px solid ${(props) => props.theme.console.border};
display: flex !important;
gap: 8px !important;
@@ -307,7 +305,7 @@ const StyledWrapper = styled.div`
}
}
.network-logs-wrapper {
.network-logs-container {
border: 1px solid ${(props) => props.theme.console.border};
border-radius: 4px;
overflow: hidden;
@@ -315,17 +313,17 @@ const StyledWrapper = styled.div`
min-height: 200px;
max-height: 400px;
.network-logs-container {
.network-logs {
background: ${(props) => props.theme.console.contentBg} !important;
color: ${(props) => props.theme.console.messageColor} !important;
height: 100% !important;
max-height: 400px !important;
padding: 0.5rem !important;
.network-logs-pre {
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-wrapper">
<div className="network-logs-container">
{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 8px;
padding: 0 16px;
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: 4px 8px;
padding: 6px 12px;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
@@ -38,6 +38,8 @@ 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};
@@ -45,9 +47,9 @@ const StyledWrapper = styled.div`
}
&.active {
color: ${(props) => props.theme.primary.strong};
border-bottom-color: ${(props) => props.theme.primary.strong};
background: ${(props) => props.theme.background.mantle};
color: ${(props) => props.theme.console.checkboxColor};
border-bottom-color: ${(props) => props.theme.console.checkboxColor};
background: ${(props) => props.theme.console.contentBg};
}
}
@@ -142,6 +144,9 @@ 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 {
@@ -154,21 +159,23 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
width: 28px;
height: 28px;
background: transparent;
border: none;
border-radius: 4px;
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.console.buttonColor};
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background: ${(props) => props.theme.background.surface0};
background: ${(props) => props.theme.console.buttonHoverBg};
color: ${(props) => props.theme.console.buttonHoverColor};
}
&.close-button:hover {
background: ${(props) => props.theme.background.surface0};
background: #e81123;
color: white;
}
}
@@ -180,17 +187,19 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
gap: 6px;
padding: 2px 8px;
padding: 6px 8px;
background: transparent;
border: 1px solid ${(props) => props.theme.border.border0};
border-radius: ${(props) => props.theme.border.radius.sm};
color: ${(props) => props.theme.text};
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.background.surface0};
background: ${(props) => props.theme.console.buttonHoverBg};
color: ${(props) => props.theme.console.buttonHoverColor};
border-color: ${(props) => props.theme.console.border};
}
.filter-summary {
@@ -223,7 +232,7 @@ const StyledWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 12px;
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};
@@ -254,7 +263,7 @@ const StyledWrapper = styled.div`
.filter-option {
display: flex;
align-items: center;
padding: 4px 12px;
padding: 6px 12px;
cursor: pointer;
transition: background-color 0.2s ease;
@@ -316,6 +325,20 @@ 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,9 +5,10 @@ import ToolHint from 'components/ToolHint/index';
const StyledSessionList = styled.div`
.session-list-item {
padding: 2px 6px;
padding: 10px 12px;
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;
@@ -23,8 +24,7 @@ const StyledSessionList = styled.div`
&.active {
background: ${(props) => props.theme.sidebarActive || 'rgba(59, 142, 234, 0.12)'};
border-left: 2px solid ${(props) => props.theme.primary.subtle};
padding-left: 4px;
border-left: 2px solid ${(props) => props.theme.brandColor || '#3b8eea'};
}
&:last-child {

View File

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

View File

@@ -182,6 +182,19 @@ 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)) {
@@ -228,6 +241,9 @@ 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,8 +1,6 @@
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import React, { useCallback, useEffect, useState } 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;
@@ -12,9 +10,6 @@ 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();
@@ -73,15 +68,15 @@ const Devtools = ({ mainSectionRef }) => {
<div
onMouseDown={handleDevtoolsResizeStart}
style={{
height: '2px',
height: '4px',
cursor: 'row-resize',
backgroundColor: isResizingDevtools ? dragHandleColor : 'transparent',
backgroundColor: isResizingDevtools ? '#0078d4' : 'transparent',
transition: 'background-color 0.2s ease',
zIndex: 20,
position: 'relative'
}}
onMouseEnter={(e) => e.target.style.backgroundColor = dragHandleColor}
onMouseLeave={(e) => e.target.style.backgroundColor = isResizingDevtools ? dragHandleColor : 'transparent'}
onMouseEnter={(e) => e.target.style.backgroundColor = '#0078d4'}
onMouseLeave={(e) => e.target.style.backgroundColor = isResizingDevtools ? '#0078d4' : 'transparent'}
/>
<div style={{ height: `${devtoolsHeight}px`, overflow: 'hidden', position: 'relative' }}>
<Console />

View File

@@ -1,20 +1,12 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const Wrapper = styled.div`
min-width: 160px;
font-size: ${(props) => props.theme.font.size.sm};
font-size: ${(props) => props.theme.font.size.base};
color: ${(props) => props.theme.dropdown.color};
background-color: ${(props) => props.theme.dropdown.bg};
${(props) =>
props.theme.dropdown.shadow && props.theme.dropdown.shadow !== 'none'
? `box-shadow: ${props.theme.dropdown.shadow};`
: ''}
box-shadow: ${(props) => props.theme.shadow.sm};
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;
@@ -53,7 +45,7 @@ const Wrapper = styled.div`
cursor: pointer;
border-radius: 6px;
margin: 0.0625rem 0;
font-size: ${(props) => props.theme.font.size.sm};
font-size: 0.8125rem;
&.active {
color: ${(props) => props.theme.colors.text.yellow} !important;
@@ -144,21 +136,17 @@ const Wrapper = styled.div`
/* Active/selected state - applied to the currently selected item */
&.dropdown-item-active {
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
color: ${({ theme }) => theme.colors.text.yellow};
background-color: ${({ theme }) => theme.dropdown.activeBg};
font-weight: 500;
.dropdown-icon {
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
}
&:hover {
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
color: ${({ theme }) => theme.colors.text.yellow};
}
}
/* Combined state - when active item is also focused */
&.dropdown-item-active.dropdown-item-focused {
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
background-color: ${({ theme }) => theme.dropdown.activeHoverBg};
}
/* 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: solid 1px ${(props) => props.theme.border.border0};
border: ${(props) => props.theme.workspace.environments.indentBorder};
}
table {
@@ -31,8 +31,8 @@ const StyledWrapper = styled.div`
padding: 5px 10px !important;
border-top: none !important;
border-left: none !important;
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
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: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
vertical-align: middle;
&:last-child {
@@ -83,6 +83,7 @@ const StyledWrapper = styled.div`
}
.tooltip-mod {
font-size: 11px !important;
max-width: 200px !important;
}

View File

@@ -12,7 +12,6 @@ const EditableTable = ({
getRowError,
showCheckbox = true,
showDelete = true,
disableCheckbox = false,
checkboxLabel = '',
checkboxKey = 'enabled',
reorderable = false,
@@ -289,7 +288,6 @@ 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,7 +6,6 @@ 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();
@@ -243,26 +242,22 @@ 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"
size="sm"
color="secondary"
variant="ghost"
className="btn btn-sm btn-cancel mt-2 flex items-center"
onClick={onClose}
disabled={isExporting}
className="mt-2 mr-2"
>
Cancel
</Button>
<Button
</button>
<button
type="button"
size="sm"
className="btn btn-sm btn-secondary mt-2 flex items-center"
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 color="danger" onClick={onCloseWithoutSave}>
<Button size="sm" color="danger" onClick={onCloseWithoutSave}>
Don't Save
</Button>
</div>
<div className="flex gap-2">
<div>
<Button size="sm" color="secondary" variant="ghost" onClick={onCancel}>
Cancel
</Button>
<Button onClick={onSaveAndClose}>
<Button size="sm" 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 ? 'dropdown-item-active' : ''}`}
className={`dropdown-item ${env.uid === activeEnvironmentUid ? 'active' : ''}`}
onClick={() => onEnvironmentSelect(env)}
data-tooltip-content={env.name}
data-tooltip-hidden={env.name?.length < 90}

View File

@@ -65,6 +65,35 @@ 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;
@@ -80,7 +109,7 @@ const Wrapper = styled.div`
}
button {
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
display: flex;
align-items: center;
justify-content: center;
@@ -90,8 +119,8 @@ const Wrapper = styled.div`
}
.tab-button {
color: ${(props) => props.theme.colors.text.subtext0};
font-size: ${(props) => props.theme.font.size.sm};
color: var(--color-tab-inactive);
font-size: ${(props) => props.theme.font.size.base};
.tab-content-wrapper {
position: relative;
@@ -141,7 +170,7 @@ const Wrapper = styled.div`
min-height: 12.5rem;
h3 {
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.5rem;
@@ -149,7 +178,7 @@ const Wrapper = styled.div`
}
p {
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
opacity: 0.75;
font-size: ${(props) => props.theme.font.size.xs};
line-height: 1.5;
@@ -165,9 +194,9 @@ const Wrapper = styled.div`
}
.space-y-2 > button {
border: 0.0625rem solid ${(props) => props.theme.text};
border: 0.0625rem solid ${(props) => props.theme.dropdown.primaryText};
background: transparent;
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
padding: 0.5rem 1rem;
border-radius: 0.375rem;
width: 100%;
@@ -195,7 +224,7 @@ const Wrapper = styled.div`
align-items: center;
justify-content: center;
padding: 2rem 1rem;
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
font-size: ${(props) => props.theme.font.size.base};
line-height: 1.5;
text-align: center;
@@ -203,7 +232,7 @@ const Wrapper = styled.div`
svg {
margin: 0 auto 1rem auto;
color: ${(props) => props.theme.text};
color: ${(props) => props.theme.dropdown.primaryText};
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 pt-3 pb-2 px-3">
<div className="tab-header flex p-[0.75rem]">
{tabs.map((tab) => (
<button
key={tab.id}

View File

@@ -26,7 +26,6 @@ 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: solid 1px ${(props) => props.theme.border.border0};
border: ${(props) => props.theme.workspace.environments.indentBorder};
}
table {
@@ -46,8 +46,8 @@ const Wrapper = styled.div`
td {
padding: 5px 10px !important;
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
&:last-child {
border-right: none;
@@ -64,8 +64,8 @@ const Wrapper = styled.div`
}
td {
border-bottom: solid 1px ${(props) => props.theme.border.border0};
border-right: solid 1px ${(props) => props.theme.border.border0};
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
&:last-child {
border-right: none;
@@ -75,7 +75,28 @@ 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;
}
@@ -154,7 +175,7 @@ const Wrapper = styled.div`
border-radius: ${(props) => props.theme.border.radius.base};
background: transparent;
color: ${(props) => props.theme.text};
border: 1px solid ${(props) => props.theme.border.border1};
border: ${(props) => props.theme.sidebar.collection.item.indentBorder};
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,12 +22,14 @@ 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;
}
@@ -84,7 +86,7 @@ const StyledWrapper = styled.div`
padding: 6px 8px 6px 28px;
font-size: 12px;
background: transparent;
border: 1px solid ${(props) => props.theme.border.border1};
border: ${(props) => props.theme.sidebar.collection.item.indentBorder};
border-radius: 5px;
color: ${(props) => props.theme.text};
transition: all 0.15s ease;
@@ -172,7 +174,7 @@ const StyledWrapper = styled.div`
}
&.active {
background: ${(props) => props.theme.background.surface0};
background: ${(props) => props.theme.workspace.environments.activeBg};
color: ${(props) => props.theme.text};
}
@@ -233,34 +235,35 @@ const StyledWrapper = styled.div`
gap: 2px;
margin-left: 4px;
}
}
.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};
.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;
&:hover {
background: ${(props) => rgba(props.theme.colors.text.green, 0.1)};
&.save {
color: ${(props) => props.theme.textLink};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
}
}
}
&.cancel {
color: ${(props) => props.theme.colors.text.danger};
&:hover {
background: ${(props) => rgba(props.theme.colors.text.danger, 0.1)};
&.cancel {
color: ${(props) => props.theme.colors.text.muted};
&:hover {
background: ${(props) => props.theme.listItem.hoverBg};
color: ${(props) => props.theme.text};
}
}
}
}

View File

@@ -11,7 +11,7 @@ const StyledWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding-top: 10%;
justify-content: center;
flex: 1;
color: ${(props) => props.theme.colors.text.muted};
@@ -32,6 +32,22 @@ 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,34 +5,30 @@ 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 size="sm" color="secondary" onClick={() => setTab('create')}>
<button className="shared-button" onClick={() => setTab('create')}>
Create Environment
</Button>
<Button size="sm" color="secondary" onClick={() => setTab('import')}>
</button>
<button className="shared-button" onClick={() => setTab('import')}>
Import Environment
</Button>
</button>
</div>
</div>
);
const EnvironmentSettings = ({ collection }) => {
const [isModified, setIsModified] = useState(false);
const environments = collection?.environments || [];
const [selectedEnvironment, setSelectedEnvironment] = useState(() => {
if (!environments.length) return null;
return environments.find((env) => env.uid === collection?.activeEnvironmentUid) || environments[0];
});
const [selectedEnvironment, setSelectedEnvironment] = useState(null);
const [tab, setTab] = useState('default');
const [showExportModal, setShowExportModal] = useState(false);
const environments = collection?.environments || [];
if (!environments || !environments.length) {
return (
<StyledWrapper>

View File

@@ -1,110 +0,0 @@
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,33 +2,10 @@ 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, IconUpload, IconFile } from '@tabler/icons';
import { IconX } from '@tabler/icons';
import { isWindowsOS } from 'utils/common/platform';
import StyledWrapper from './StyledWrapper';
/**
* 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 FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false, readOnly = false }) => {
const dispatch = useDispatch();
const filenames = (isSingleFilePicker ? [value] : value || [])
.filter((v) => v != null && v != '')
@@ -41,8 +18,6 @@ const FilePickerEditor = ({
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
@@ -64,8 +39,7 @@ const FilePickerEditor = ({
});
};
const clear = (e) => {
e.stopPropagation();
const clear = () => {
onChange(isSingleFilePicker ? '' : []);
};
@@ -76,69 +50,40 @@ const FilePickerEditor = ({
return filenames.length + ' file(s) selected';
};
const defaultLabel = isSingleFilePicker ? 'Select File' : 'Select Files';
const displayLabel = label || defaultLabel;
const IconComponent = CustomIcon || IconUpload;
const buttonClass = `btn btn-secondary px-1 ${readOnly ? 'view-mode' : 'edit-mode'}`;
// 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}
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
}}
>
{renderButtonContent()}
</button>
</StyledWrapper>
{renderButtonText(filenames)}
</span>
</div>
) : (
<button className={buttonClass} style={{ width: '100%' }} onClick={!readOnly ? browse : undefined} disabled={readOnly}>
{isSingleFilePicker ? 'Select File' : 'Select Files'}
</button>
);
};

View File

@@ -12,7 +12,7 @@ const Wrapper = styled.div`
background-color: ${(props) => props.theme.input.bg};
}
.inherit-mode-text {
color: ${(props) => props.theme.primary.text};
color: ${(props) => props.theme.colors.text.yellow};
}
.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.primary.text};
color: ${(props) => props.theme.colors.text.yellow};
.caret {
color: rgb(140, 140, 140);

View File

@@ -7,7 +7,6 @@ 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 }) => {
@@ -38,35 +37,27 @@ const Documentation = ({ collection, folder }) => {
}
return (
<StyledWrapper className="w-full relative flex flex-col">
<div className="editing-mode flex justify-between items-center flex-shrink-0" role="tab" onClick={toggleViewMode}>
<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}>
{isEditing ? 'Preview' : 'Edit'}
</div>
{isEditing ? (
<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 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>
) : (
<div className="h-full">
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
</div>
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
)}
</StyledWrapper>
);

View File

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

View File

@@ -58,7 +58,7 @@ const Script = ({ collection, folder }) => {
};
return (
<StyledWrapper className="w-full flex flex-col h-full">
<StyledWrapper className="w-full flex flex-col h-full pt-4">
<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: ${(props) => props.theme.colors.text.subtext0};
color: var(--color-tab-inactive);
cursor: pointer;
&:focus,
@@ -21,10 +21,6 @@ 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: ${(props) => props.theme.colors.text.subtext0};
color: var(--color-tab-inactive);
}
`;

View File

@@ -14,11 +14,11 @@ const Vars = ({ collection, folder }) => {
return (
<StyledWrapper className="w-full flex flex-col">
<div>
<div className="flex-1 mt-2">
<div className="mb-3 title text-xs">Pre Request</div>
<VarsTable folder={folder} collection={collection} vars={requestVars} varType="request" />
</div>
<div>
<div className="flex-1">
<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,5 +1,4 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const StyledWrapper = styled.div`
/* Screen reader only content */
@@ -41,9 +40,9 @@ const StyledWrapper = styled.div`
}
.command-k-modal {
background: ${(props) => props.theme.modal.body.bg};
border: 1px solid ${(props) => props.theme.border.border1};
border: 1px solid ${(props) => props.theme.modal.input.border};
border-radius: 8px;
box-shadow: ${(props) => props.theme.shadow.md};
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 90%;
max-width: 600px;
max-height: 70vh;
@@ -56,7 +55,8 @@ const StyledWrapper = styled.div`
}
.command-k-header {
padding: 12px;
border-bottom: 1px solid ${(props) => props.theme.border.border1};
border-bottom: 1px solid ${(props) => props.theme.modal.input.border};
background: ${(props) => props.theme.modal.title.bg};
}
.search-input-container {
position: relative;
@@ -64,11 +64,13 @@ const StyledWrapper = styled.div`
align-items: center;
width: 100%;
padding: 8px 12px;
border: 1px solid ${(props) => props.theme.input.border};
border: 1px solid ${(props) => props.theme.modal.input.border};
border-radius: 6px;
background: ${(props) => props.theme.modal.input.bg};
transition: all 0.2s ease;
&:focus-within {
border: 1px solid ${(props) => props.theme.input.focusBorder};
border-color: ${(props) => props.theme.colors.text.muted};
box-shadow: 0 0 0 1px ${(props) => props.theme.colors.text.muted}40;
}
.search-icon {
color: ${(props) => props.theme.colors.text.muted};
@@ -90,7 +92,7 @@ const StyledWrapper = styled.div`
border-radius: 4px;
flex-shrink: 0;
&:hover {
background: ${(props) => rgba(props.theme.text, 0.1)};
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.1)'};
}
}
}
@@ -112,8 +114,9 @@ const StyledWrapper = styled.div`
flex: 1;
overflow-y: auto;
max-height: 400px;
background: ${(props) => props.theme.modal.body.bg};
scrollbar-width: thin;
padding: 6px 0;
padding: 4px;
scroll-behavior: smooth;
/* Webkit scrollbar styling */
&::-webkit-scrollbar {
@@ -124,27 +127,26 @@ const StyledWrapper = styled.div`
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: ${(props) => rgba(props.theme.text, 0.2)};
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.2)'};
border-radius: 4px;
&:hover {
background: ${(props) => rgba(props.theme.text, 0.3)};
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.3)' : 'rgba(0,0,0,0.3)'};
}
}
}
.result-item {
display: flex;
align-items: center;
padding: 10px 12px;
margin: 2px 8px;
gap: 10px;
padding: 8px 12px;
gap: 8px;
cursor: pointer;
border-radius: ${(props) => props.theme.border.radius.base};
transition: background 0.1s ease;
&:hover:not(.selected) {
background: ${(props) => rgba(props.theme.text, 0.05)};
border-left: 2px solid transparent;
&:hover {
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'};
}
&.selected {
background: ${(props) => props.theme.dropdown.hoverBg};
background: ${(props) => `${props.theme.colors.text.yellow}15`};
border-left: 2px solid ${(props) => props.theme.colors.text.yellow};
}
}
.result-icon {
@@ -194,82 +196,74 @@ const StyledWrapper = styled.div`
letter-spacing: 0.1px;
}
.method-badge {
font-size: 0.625rem;
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
padding: 2px 6px;
padding: 3px 8px;
border-radius: 4px;
text-transform: uppercase;
letter-spacing: 0.3px;
letter-spacing: 0.5px;
flex-shrink: 0;
min-width: 48px;
min-width: 55px;
text-align: center;
&.get {
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)};
color: #2ecc71;
background: rgba(46, 204, 113, 0.1);
}
&.post {
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)};
color: #3498db;
background: rgba(52, 152, 219, 0.1);
}
&.put {
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)};
color: #e67e22;
background: rgba(230, 126, 34, 0.1);
}
&.delete {
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)};
color: #e74c3c;
background: rgba(231, 76, 60, 0.1);
}
&.patch {
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)};
color: #9b59b6;
background: rgba(155, 89, 182, 0.1);
}
&.head {
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)};
color: #2980b9;
background: rgba(41, 128, 185, 0.1);
}
&.options {
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)};
color: #f1c40f;
background: rgba(241, 196, 15, 0.1);
}
&.unary {
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)};
color: #27ae60;
background: rgba(39, 174, 96, 0.12);
font-weight: 500;
}
&.client-streaming {
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)};
color: #2980b9;
background: rgba(41, 128, 185, 0.12);
font-weight: 500;
}
&.server-streaming {
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)};
color: #f39c12;
background: rgba(243, 156, 18, 0.12);
font-weight: 500;
}
&.bidirectional-streaming,
&.bidi-streaming {
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)};
color: #8e44ad;
background: rgba(142, 68, 173, 0.12);
font-weight: 500;
}
}
.result-type {
font-size: 0.625rem;
color: ${(props) => props.theme.textLink};
font-size: ${(props) => props.theme.font.size.xs};
color: ${(props) => props.theme.colors.text.muted};
padding: 2px 6px;
border-radius: 4px;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.3px;
font-weight: 500;
background: ${(props) => rgba(props.theme.textLink, 0.1)};
border: 1px solid ${(props) => rgba(props.theme.textLink, 0.2)};
background: ${(props) => props.theme.mode === 'dark' ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.03)'};
opacity: 0.8;
flex-shrink: 0;
}
.result-item[data-type="documentation"] {
@@ -286,6 +280,9 @@ 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 {
@@ -296,7 +293,7 @@ const StyledWrapper = styled.div`
}
.command-k-footer {
padding: 8px 12px;
border-top: 1px solid ${(props) => props.theme.border.border1};
border-top: 1px solid ${(props) => props.theme.modal.input.border};
background: ${(props) => props.theme.colors.surface};
}
.keyboard-hints {
@@ -322,9 +319,10 @@ const StyledWrapper = styled.div`
align-items: center;
justify-content: center;
padding: 2px 6px;
border: 1px solid ${(props) => props.theme.border.border2};
border: 1px solid ${(props) => props.theme.modal.input.border};
border-radius: 4px;
background: ${(props) => rgba(props.theme.text, 0.08)};
background: ${(props) =>
props.theme.mode === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)'};
font-size: ${(props) => props.theme.font.size.xs};
font-weight: 500;
font-family: inherit;
@@ -334,10 +332,11 @@ const StyledWrapper = styled.div`
}
}
.highlight {
color: ${(props) => props.theme.brand};
background: ${(props) => `${props.theme.colors.text.yellow}30`};
border-radius: 2px;
padding: 1px 2px;
padding: 0 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 = '', color = '#3B82F6' }) => (
export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
@@ -14,16 +14,16 @@ export const IconGrpcUnary = ({ size = 18, strokeWidth = 1.5, className = '', co
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
{/* Request arrow (top) - right */}
<path d="M3 8h18" stroke={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke="#3B82F6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#3B82F6" strokeWidth={strokeWidth} />
</svg>
);
// CLIENT_STREAMING - Streaming request, single response (Purple)
export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#8B5CF6' }) => (
export const IconGrpcClientStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
<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={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} />
<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} />
{/* Response arrow (bottom) - left */}
<path d="M21 16h-18" stroke={color} strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M21 16h-18" stroke="#8B5CF6" strokeWidth={strokeWidth} />
<path d="M6 13l-3 3l3 3" stroke="#8B5CF6" strokeWidth={strokeWidth} />
</svg>
);
// SERVER_STREAMING - Single request, streaming response (Green)
export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#10B981' }) => (
export const IconGrpcServerStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
<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={color} strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke={color} strokeWidth={strokeWidth} />
<path d="M3 8h18" stroke="#10B981" strokeWidth={strokeWidth} />
<path d="M18 5l3 3l-3 3" stroke="#10B981" strokeWidth={strokeWidth} />
{/* Response arrow (bottom) - left with double heads */}
<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} />
<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} />
</svg>
);
// BIDI_STREAMING - Streaming request, streaming response (Orange)
export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '', color = '#F97316' }) => (
export const IconGrpcBidiStreaming = ({ size = 18, strokeWidth = 1.5, className = '' }) => (
<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={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} />
<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} />
{/* Response arrow (bottom) - left with double heads */}
<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} />
<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} />
</svg>
);

View File

@@ -1,47 +0,0 @@
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.input.bg,
border: `1px solid ${theme.input.border}`,
color: theme.text
backgroundColor: theme.modal.input.bg,
border: `1px solid ${theme.modal.input.border}`,
color: theme.modal.input.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.input.bg,
border: `1px solid ${theme.input.border}`,
color: theme.text
backgroundColor: theme.modal.input.bg,
border: `1px solid ${theme.modal.input.border}`,
color: theme.modal.input.text
}}
autoComplete="off"
autoCorrect="off"

View File

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

View File

@@ -100,14 +100,15 @@ const StyledWrapper = styled.div`
.default-badge {
padding: 1px 6px;
border-radius: ${(props) => props.theme.border.radius.sm};
background: ${(props) => props.theme.background.surface1};
color: ${(props) => props.theme.text};
background: ${(props) => props.theme.sidebar.badge.bg};
color: ${(props) => props.theme.colors.text.muted};
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.text.muted};
color: ${(props) => props.theme.colors.text.muted};
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -14,7 +14,6 @@ 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();
@@ -134,7 +133,7 @@ const ManageWorkspace = () => {
onClick={() => handleShowInFolder(workspace)}
>
<IconFolder size={14} strokeWidth={1.5} />
<span>{getRevealInFolderLabel()}</span>
<span>Show in folder</span>
</button>
)}
{!isDefault && (

View File

@@ -1,5 +1,4 @@
import styled from 'styled-components';
import { rgba } from 'polished';
const Wrapper = styled.div`
color: ${(props) => props.theme.text};
@@ -41,7 +40,6 @@ 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;
@@ -74,6 +72,7 @@ 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};
@@ -99,7 +98,7 @@ const Wrapper = styled.div`
&:hover {
opacity: 1;
background-color: ${(props) => rgba(props.theme.modal.title.color, 0.1)};
background-color: ${(props) => props.theme.modal.closeButton.hoverBg};
}
}
}
@@ -118,24 +117,15 @@ 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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.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};
}
@@ -176,16 +166,6 @@ 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,8 +96,9 @@ 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 && !confirmDisabled) {
if (!shiftKey && !ctrlKey && !altKey && !metaKey && handleConfirm && !isSubmitButton) {
return handleConfirm();
}
}
@@ -116,7 +117,7 @@ const Modal = ({
return () => {
document.removeEventListener('keydown', handleKeydown);
};
}, [disableEscapeKey, document, handleConfirm, confirmDisabled]);
}, [disableEscapeKey, document, handleConfirm]);
let classes = 'bruno-modal';
if (isClosing) {

View File

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

View File

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

View File

@@ -29,7 +29,8 @@ const StyledWrapper = styled.div`
}
.table-container {
overflow-y: auto;
max-height: 400px;
overflow-y: scroll;
}
.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.input.bg};
border: 1px solid ${(props) => props.theme.input.border};
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.input.focusBorder} !important;
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}

View File

@@ -18,41 +18,56 @@ const ProxySettings = ({ close }) => {
console.log(preferences);
const proxySchema = Yup.object({
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()
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)
});
const formik = useFormik({
initialValues: {
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 || ''
}
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 || ''
},
validationSchema: proxySchema,
onSubmit: (values) => {
@@ -88,19 +103,16 @@ const ProxySettings = ({ close }) => {
useEffect(() => {
formik.setValues({
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 || ''
}
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 || ''
});
}, [preferences]);
@@ -125,11 +137,10 @@ const ProxySettings = ({ close }) => {
<input
type="radio"
name="mode"
value="off"
checked={formik.values.disabled === true}
value="false"
checked={formik.values.mode === 'off'}
onChange={(e) => {
formik.setFieldValue('disabled', true);
formik.setFieldValue('inherit', false);
formik.setFieldValue('mode', 'off');
}}
className="mr-1 cursor-pointer"
/>
@@ -139,11 +150,10 @@ const ProxySettings = ({ close }) => {
<input
type="radio"
name="mode"
value="on"
checked={formik.values.disabled === false && formik.values.inherit === false}
value="true"
checked={formik.values.mode === 'on'}
onChange={(e) => {
formik.setFieldValue('disabled', false);
formik.setFieldValue('inherit', false);
formik.setFieldValue('mode', 'on');
}}
className="mr-1 cursor-pointer"
/>
@@ -154,18 +164,15 @@ const ProxySettings = ({ close }) => {
type="radio"
name="mode"
value="system"
checked={formik.values.disabled === false && formik.values.inherit === true}
onChange={(e) => {
formik.setFieldValue('disabled', false);
formik.setFieldValue('inherit', true);
}}
checked={formik.values.mode === 'system'}
onChange={formik.handleChange}
className="mr-1 cursor-pointer"
/>
System Proxy
</label>
</div>
</div>
{formik.values.disabled === false && formik.values.inherit === true ? (
{formik?.values?.mode === 'system' ? (
<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 />
@@ -193,7 +200,7 @@ const ProxySettings = ({ close }) => {
</div>
</div>
) : null}
{formik.values.disabled === false && formik.values.inherit === false ? (
{formik?.values?.mode === 'on' ? (
<>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="protocol">
@@ -203,9 +210,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center">
<input
type="radio"
name="config.protocol"
name="protocol"
value="http"
checked={formik.values.config.protocol === 'http'}
checked={formik.values.protocol === 'http'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -214,9 +221,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="config.protocol"
name="protocol"
value="https"
checked={formik.values.config.protocol === 'https'}
checked={formik.values.protocol === 'https'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -225,9 +232,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="config.protocol"
name="protocol"
value="socks4"
checked={formik.values.config.protocol === 'socks4'}
checked={formik.values.protocol === 'socks4'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -236,9 +243,9 @@ const ProxySettings = ({ close }) => {
<label className="flex items-center ml-4">
<input
type="radio"
name="config.protocol"
name="protocol"
value="socks5"
checked={formik.values.config.protocol === 'socks5'}
checked={formik.values.protocol === 'socks5'}
onChange={formik.handleChange}
className="mr-1"
/>
@@ -247,94 +254,92 @@ const ProxySettings = ({ close }) => {
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.hostname">
<label className="settings-label" htmlFor="hostname">
Hostname
</label>
<input
id="config.hostname"
id="hostname"
type="text"
name="config.hostname"
name="hostname"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.config.hostname || ''}
value={formik.values.hostname || ''}
/>
{formik.touched.config?.hostname && formik.errors.config?.hostname ? (
<div className="ml-3 text-red-500">{formik.errors.config.hostname}</div>
{formik.touched.hostname && formik.errors.hostname ? (
<div className="ml-3 text-red-500">{formik.errors.hostname}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.port">
<label className="settings-label" htmlFor="port">
Port
</label>
<input
id="config.port"
id="port"
type="number"
name="config.port"
name="port"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.config.port}
value={formik.values.port}
/>
{formik.touched.config?.port && formik.errors.config?.port ? (
<div className="ml-3 text-red-500">{formik.errors.config.port}</div>
{formik.touched.port && formik.errors.port ? (
<div className="ml-3 text-red-500">{formik.errors.port}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.auth.disabled">
<label className="settings-label" htmlFor="auth.enabled">
Auth
</label>
<input
type="checkbox"
name="config.auth.disabled"
checked={!formik.values.config.auth.disabled}
onChange={(e) => {
formik.setFieldValue('config.auth.disabled', !e.target.checked);
}}
name="auth.enabled"
checked={formik.values.auth.enabled}
onChange={formik.handleChange}
/>
</div>
<div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.auth.username">
<label className="settings-label" htmlFor="auth.username">
Username
</label>
<input
id="config.auth.username"
id="auth.username"
type="text"
name="config.auth.username"
name="auth.username"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.config.auth.username}
value={formik.values.auth.username}
onChange={formik.handleChange}
/>
{formik.touched.config?.auth?.username && formik.errors.config?.auth?.username ? (
<div className="ml-3 text-red-500">{formik.errors.config.auth.username}</div>
{formik.touched.auth?.username && formik.errors.auth?.username ? (
<div className="ml-3 text-red-500">{formik.errors.auth.username}</div>
) : null}
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.auth.password">
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[2.25rem] relative">
<input
id="config.auth.password"
id="auth.password"
type={passwordVisible ? `text` : 'password'}
name="config.auth.password"
name="auth.password"
className="outline-none w-[10.5rem] bg-transparent"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.config.auth.password}
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
<button
@@ -345,29 +350,29 @@ const ProxySettings = ({ close }) => {
{passwordVisible ? <IconEyeOff size={18} strokeWidth={2} /> : <IconEye size={18} strokeWidth={2} />}
</button>
</div>
{formik.touched.config?.auth?.password && formik.errors.config?.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.config.auth.password}</div>
{formik.touched.auth?.password && formik.errors.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.auth.password}</div>
) : null}
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="config.bypassProxy">
<label className="settings-label" htmlFor="bypassProxy">
Proxy Bypass
</label>
<input
id="config.bypassProxy"
id="bypassProxy"
type="text"
name="config.bypassProxy"
name="bypassProxy"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.config.bypassProxy || ''}
value={formik.values.bypassProxy || ''}
/>
{formik.touched.config?.bypassProxy && formik.errors.config?.bypassProxy ? (
<div className="ml-3 text-red-500">{formik.errors.config.bypassProxy}</div>
{formik.touched.bypassProxy && formik.errors.bypassProxy ? (
<div className="ml-3 text-red-500">{formik.errors.bypassProxy}</div>
) : null}
</div>
</>

View File

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

View File

@@ -1,102 +0,0 @@
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

@@ -1,130 +0,0 @@
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