mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-22 20:25:38 +00:00
feat: theme + ux overhaul
This commit is contained in:
192
package-lock.json
generated
192
package-lock.json
generated
@@ -22,6 +22,9 @@
|
||||
"packages/bruno-requests",
|
||||
"packages/bruno-filestore"
|
||||
],
|
||||
"dependencies": {
|
||||
"ajv": "^8.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.3.2",
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
@@ -1604,7 +1607,7 @@
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz",
|
||||
"integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -1622,7 +1625,7 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz",
|
||||
"integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.22.6",
|
||||
@@ -1639,7 +1642,7 @@
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -1657,7 +1660,7 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@babel/helper-globals": {
|
||||
@@ -1737,7 +1740,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz",
|
||||
"integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -1812,7 +1815,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz",
|
||||
"integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.25.9",
|
||||
@@ -1855,7 +1858,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz",
|
||||
"integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -1872,7 +1875,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz",
|
||||
"integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -1888,7 +1891,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
|
||||
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -1904,7 +1907,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz",
|
||||
"integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -1922,7 +1925,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz",
|
||||
"integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -1957,7 +1960,7 @@
|
||||
"version": "7.21.0-placeholder-for-preset-env.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
|
||||
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2056,7 +2059,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz",
|
||||
"integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2072,7 +2075,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz",
|
||||
"integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2254,7 +2257,7 @@
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
|
||||
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
|
||||
@@ -2271,7 +2274,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2287,7 +2290,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -2305,7 +2308,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz",
|
||||
"integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.25.9",
|
||||
@@ -2323,7 +2326,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz",
|
||||
"integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2339,7 +2342,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz",
|
||||
"integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2371,7 +2374,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz",
|
||||
"integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-class-features-plugin": "^7.25.9",
|
||||
@@ -2388,7 +2391,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz",
|
||||
"integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -2409,7 +2412,7 @@
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -2419,7 +2422,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz",
|
||||
"integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -2436,7 +2439,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz",
|
||||
"integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2452,7 +2455,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -2469,7 +2472,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz",
|
||||
"integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2485,7 +2488,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -2502,7 +2505,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz",
|
||||
"integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2518,7 +2521,7 @@
|
||||
"version": "7.26.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz",
|
||||
"integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2534,7 +2537,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz",
|
||||
"integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2566,7 +2569,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz",
|
||||
"integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -2583,7 +2586,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz",
|
||||
"integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.25.9",
|
||||
@@ -2601,7 +2604,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz",
|
||||
"integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2617,7 +2620,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2633,7 +2636,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz",
|
||||
"integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2649,7 +2652,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2665,7 +2668,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz",
|
||||
"integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -2698,7 +2701,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz",
|
||||
"integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -2717,7 +2720,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz",
|
||||
"integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.25.9",
|
||||
@@ -2734,7 +2737,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -2751,7 +2754,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz",
|
||||
"integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2782,7 +2785,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
|
||||
"integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2798,7 +2801,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
|
||||
"integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-compilation-targets": "^7.25.9",
|
||||
@@ -2816,7 +2819,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
|
||||
"integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -2833,7 +2836,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
|
||||
"integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2865,7 +2868,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz",
|
||||
"integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -2897,7 +2900,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz",
|
||||
"integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
@@ -2915,7 +2918,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3000,7 +3003,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz",
|
||||
"integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -3017,7 +3020,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz",
|
||||
"integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -3034,7 +3037,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz",
|
||||
"integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3050,7 +3053,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz",
|
||||
"integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3066,7 +3069,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz",
|
||||
"integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9",
|
||||
@@ -3083,7 +3086,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3099,7 +3102,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz",
|
||||
"integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3115,7 +3118,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz",
|
||||
"integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3150,7 +3153,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz",
|
||||
"integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
@@ -3166,7 +3169,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -3183,7 +3186,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -3200,7 +3203,7 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz",
|
||||
"integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-create-regexp-features-plugin": "^7.25.9",
|
||||
@@ -3217,7 +3220,7 @@
|
||||
"version": "7.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz",
|
||||
"integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.26.0",
|
||||
@@ -3318,7 +3321,7 @@
|
||||
"version": "0.1.6-no-external-plugins",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
|
||||
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
@@ -9547,6 +9550,7 @@
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
|
||||
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
@@ -9566,6 +9570,7 @@
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -9578,6 +9583,7 @@
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
@@ -9592,6 +9598,7 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@testing-library/jest-dom": {
|
||||
@@ -9702,6 +9709,7 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
@@ -9994,6 +10002,7 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
@@ -10016,6 +10025,7 @@
|
||||
"version": "12.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz",
|
||||
"integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "*",
|
||||
@@ -10026,6 +10036,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
@@ -11400,6 +11411,7 @@
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3"
|
||||
@@ -11844,7 +11856,7 @@
|
||||
"version": "0.4.12",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.12.tgz",
|
||||
"integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/compat-data": "^7.22.6",
|
||||
@@ -11859,7 +11871,7 @@
|
||||
"version": "0.10.6",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz",
|
||||
"integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.2",
|
||||
@@ -11873,7 +11885,7 @@
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.3.tgz",
|
||||
"integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-define-polyfill-provider": "^0.6.3"
|
||||
@@ -13763,7 +13775,7 @@
|
||||
"version": "3.39.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
|
||||
"integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.2"
|
||||
@@ -14912,6 +14924,7 @@
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dom-converter": {
|
||||
@@ -15984,7 +15997,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -18860,7 +18873,7 @@
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
@@ -20870,6 +20883,15 @@
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonschema": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz",
|
||||
"integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz",
|
||||
@@ -21317,6 +21339,7 @@
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
|
||||
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"lz-string": "bin/bin.js"
|
||||
@@ -22931,7 +22954,7 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
@@ -25458,14 +25481,14 @@
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||
"integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regenerate-unicode-properties": {
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz",
|
||||
"integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerate": "^1.4.2"
|
||||
@@ -25484,7 +25507,7 @@
|
||||
"version": "0.15.2",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz",
|
||||
"integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.4"
|
||||
@@ -25494,7 +25517,7 @@
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz",
|
||||
"integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerate": "^1.4.2",
|
||||
@@ -25512,14 +25535,14 @@
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||
"integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regjsparser": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
|
||||
"integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"jsesc": "~3.0.2"
|
||||
@@ -25532,7 +25555,7 @@
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
|
||||
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
@@ -25728,7 +25751,7 @@
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
@@ -28046,7 +28069,7 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -29233,7 +29256,7 @@
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@@ -29294,7 +29317,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
|
||||
"integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -29304,7 +29327,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
|
||||
"integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"unicode-canonical-property-names-ecmascript": "^2.0.0",
|
||||
@@ -29318,7 +29341,7 @@
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz",
|
||||
"integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -29328,7 +29351,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz",
|
||||
"integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -30437,6 +30460,7 @@
|
||||
"json5": "^2.2.3",
|
||||
"jsonc-parser": "^3.2.1",
|
||||
"jsonpath-plus": "^10.3.0",
|
||||
"jsonschema": "^1.5.0",
|
||||
"know-your-http-well": "^0.5.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
14
package.json
14
package.json
@@ -20,17 +20,16 @@
|
||||
],
|
||||
"homepage": "https://usebruno.com",
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"@storybook/addon-webpack5-compiler-babel": "^4.0.0",
|
||||
"@storybook/builder-webpack5": "^10.1.10",
|
||||
"@storybook/react": "^10.1.10",
|
||||
"@storybook/react-webpack5": "^10.1.10",
|
||||
"@stylistic/eslint-plugin": "^5.3.1",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
@@ -49,6 +48,7 @@
|
||||
"pretty-quick": "^3.1.3",
|
||||
"randomstring": "^1.2.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"storybook": "^10.1.10",
|
||||
"ts-jest": "^29.2.6"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -62,6 +62,7 @@
|
||||
"prettier:web": "npm run prettier --workspace=packages/bruno-app",
|
||||
"dev:electron": "npm run dev --workspace=packages/bruno-electron",
|
||||
"dev:electron:debug": "npm run debug --workspace=packages/bruno-electron",
|
||||
"storybook": "npm run storybook --workspace=packages/bruno-app",
|
||||
"build:bruno-common": "npm run build --workspace=packages/bruno-common",
|
||||
"build:bruno-requests": "npm run build --workspace=packages/bruno-requests",
|
||||
"build:bruno-filestore": "npm run build --workspace=packages/bruno-filestore",
|
||||
@@ -97,5 +98,8 @@
|
||||
"json-schema-typed": "8.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^8.17.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"json5": "^2.2.3",
|
||||
"jsonc-parser": "^3.2.1",
|
||||
"jsonpath-plus": "^10.3.0",
|
||||
"jsonschema": "^1.5.0",
|
||||
"know-your-http-well": "^0.5.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@@ -246,12 +246,6 @@ const Wrapper = styled.div`
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-item-active {
|
||||
font-weight: 400 !important;
|
||||
background-color: ${(props) => props.theme.dropdown.selectedBg} !important;
|
||||
color: ${(props) => props.theme.dropdown.selectedColor} !important;
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -19,7 +19,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
color: ${(props) => props.theme.brand};
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
|
||||
@@ -5,9 +5,9 @@ const StyledWrapper = styled.div`
|
||||
background-color: ${(props) => props.theme.requestTabPanel.card.bg};
|
||||
|
||||
.title {
|
||||
border-top: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
|
||||
border-left: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
|
||||
border-right: 1px solid ${(props) => props.theme.requestTabPanel.cardTable.border};
|
||||
border-top: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-left: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-right: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
@@ -15,8 +15,8 @@ const StyledWrapper = styled.div`
|
||||
|
||||
.table {
|
||||
thead {
|
||||
background-color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.bg};
|
||||
color: ${(props) => props.theme.requestTabPanel.cardTable.table.thead.color};
|
||||
color: ${(props) => props.theme.table.thead.color} !important;
|
||||
background: ${(props) => props.theme.sidebar.bg};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -4,7 +4,7 @@ const StyledWrapper = styled.div`
|
||||
max-width: 800px;
|
||||
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
min-width: 160px;
|
||||
@@ -136,17 +137,21 @@ const Wrapper = styled.div`
|
||||
|
||||
/* Active/selected state - applied to the currently selected item */
|
||||
&.dropdown-item-active {
|
||||
color: ${({ theme }) => theme.colors.text.yellow};
|
||||
background-color: ${({ theme }) => theme.dropdown.activeBg};
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
|
||||
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
|
||||
.dropdown-icon {
|
||||
color: ${({ theme }) => theme.colors.text.yellow};
|
||||
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: ${({ theme }) => theme.dropdown.selectedColor} !important;
|
||||
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Combined state - when active item is also focused */
|
||||
&.dropdown-item-active.dropdown-item-focused {
|
||||
background-color: ${({ theme }) => theme.dropdown.activeHoverBg};
|
||||
background-color: ${({ theme }) => rgba(theme.dropdown.selectedColor, 0.07)} !important;
|
||||
}
|
||||
|
||||
/* Focus visible for accessibility */
|
||||
|
||||
@@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
|
||||
.table-container {
|
||||
overflow-y: auto;
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
border: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border: solid 1px ${(props) => props.theme.border.border0};
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -31,8 +31,8 @@ const StyledWrapper = styled.div`
|
||||
padding: 5px 10px !important;
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: solid 1px ${(props) => props.theme.border.border0};
|
||||
vertical-align: middle;
|
||||
|
||||
&:last-child {
|
||||
@@ -58,8 +58,8 @@ const StyledWrapper = styled.div`
|
||||
padding: 1px 10px !important;
|
||||
border-top: none !important;
|
||||
border-left: none !important;
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: solid 1px ${(props) => props.theme.border.border0};
|
||||
vertical-align: middle;
|
||||
|
||||
&:last-child {
|
||||
|
||||
@@ -33,7 +33,7 @@ const EnvironmentListContent = ({
|
||||
{environments.map((env) => (
|
||||
<div
|
||||
key={env.uid}
|
||||
className={`dropdown-item ${env.uid === activeEnvironmentUid ? 'active' : ''}`}
|
||||
className={`dropdown-item ${env.uid === activeEnvironmentUid ? 'dropdown-item-active' : ''}`}
|
||||
onClick={() => onEnvironmentSelect(env)}
|
||||
data-tooltip-content={env.name}
|
||||
data-tooltip-hidden={env.name?.length < 90}
|
||||
|
||||
@@ -65,35 +65,6 @@ const Wrapper = styled.div`
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tippy-box .tippy-content {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.6rem;
|
||||
cursor: pointer;
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background-color: ${(props) => props.theme.dropdown.hoverBg};
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: ${(props) => props.theme.dropdown.selectedBg};
|
||||
color: ${(props) => props.theme.dropdown.selectedColor};
|
||||
}
|
||||
|
||||
&.no-environment {
|
||||
color: ${(props) => props.theme.dropdown.mutedText};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.configure-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
@@ -109,7 +80,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
button {
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -119,7 +90,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
|
||||
.tab-content-wrapper {
|
||||
@@ -170,7 +141,7 @@ const Wrapper = styled.div`
|
||||
min-height: 12.5rem;
|
||||
|
||||
h3 {
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
@@ -178,7 +149,7 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
p {
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
opacity: 0.75;
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
line-height: 1.5;
|
||||
@@ -194,9 +165,9 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.space-y-2 > button {
|
||||
border: 0.0625rem solid ${(props) => props.theme.dropdown.primaryText};
|
||||
border: 0.0625rem solid ${(props) => props.theme.text};
|
||||
background: transparent;
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
width: 100%;
|
||||
@@ -224,7 +195,7 @@ const Wrapper = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 1rem;
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
@@ -232,7 +203,7 @@ const Wrapper = styled.div`
|
||||
|
||||
svg {
|
||||
margin: 0 auto 1rem auto;
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const Wrapper = styled.div`
|
||||
.table-container {
|
||||
overflow-y: auto;
|
||||
border-radius: 8px;
|
||||
border: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border: solid 1px ${(props) => props.theme.border.border0};
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -46,8 +46,8 @@ const Wrapper = styled.div`
|
||||
|
||||
td {
|
||||
padding: 5px 10px !important;
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: solid 1px ${(props) => props.theme.border.border0};
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
@@ -64,8 +64,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: solid 1px ${(props) => props.theme.border.border0};
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
@@ -75,26 +75,6 @@ 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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@@ -174,7 +175,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: ${(props) => props.theme.workspace.environments.activeBg};
|
||||
background: ${(props) => props.theme.background.surface0};
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
|
||||
@@ -235,35 +236,34 @@ 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;
|
||||
|
||||
.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};
|
||||
|
||||
&.save {
|
||||
color: ${(props) => props.theme.textLink};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
}
|
||||
&:hover {
|
||||
background: ${(props) => rgba(props.theme.colors.text.green, 0.1)};
|
||||
}
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
|
||||
&.cancel {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
&:hover {
|
||||
background: ${(props) => rgba(props.theme.colors.text.danger, 0.1)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 10%;
|
||||
flex: 1;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
@@ -32,22 +32,6 @@ const StyledWrapper = styled.div`
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.shared-button {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
|
||||
background: ${(props) => props.theme.sidebar.bg};
|
||||
color: ${(props) => props.theme.text};
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
border-color: ${(props) => props.theme.textLink};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -5,18 +5,19 @@ import StyledWrapper from './StyledWrapper';
|
||||
import { IconFileAlert } from '@tabler/icons';
|
||||
import ImportEnvironmentModal from 'components/Environments/Common/ImportEnvironmentModal';
|
||||
import ExportEnvironmentModal from 'components/Environments/Common/ExportEnvironmentModal';
|
||||
import Button from 'ui/Button';
|
||||
|
||||
const DefaultTab = ({ setTab }) => (
|
||||
<div className="empty-state">
|
||||
<IconFileAlert size={48} strokeWidth={1.5} />
|
||||
<div className="title">No Environments</div>
|
||||
<div className="actions">
|
||||
<button className="shared-button" onClick={() => setTab('create')}>
|
||||
<Button size="sm" color="secondary" onClick={() => setTab('create')}>
|
||||
Create Environment
|
||||
</button>
|
||||
<button className="shared-button" onClick={() => setTab('import')}>
|
||||
</Button>
|
||||
<Button size="sm" color="secondary" onClick={() => setTab('import')}>
|
||||
Import Environment
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -100,15 +100,14 @@ const StyledWrapper = styled.div`
|
||||
.default-badge {
|
||||
padding: 1px 6px;
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
background: ${(props) => props.theme.sidebar.badge.bg};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
background: ${(props) => props.theme.background.surface1};
|
||||
color: ${(props) => props.theme.text};
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.workspace-path {
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
color: ${(props) => props.theme.text.muted};
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
color: ${(props) => props.theme.text};
|
||||
@@ -72,7 +73,6 @@ const Wrapper = styled.div`
|
||||
background-color: ${(props) => props.theme.modal.title.bg};
|
||||
font-size: ${(props) => props.theme.font.size.md};
|
||||
padding: 0.5rem 1rem;
|
||||
font-weight: 500;
|
||||
border-top-left-radius: ${(props) => props.theme.border.radius.base};
|
||||
border-top-right-radius: ${(props) => props.theme.border.radius.base};
|
||||
|
||||
@@ -98,7 +98,7 @@ const Wrapper = styled.div`
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background-color: ${(props) => props.theme.modal.closeButton.hoverBg};
|
||||
background-color: ${(props) => rgba(props.theme.modal.title.color, 0.1)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import React from 'react';
|
||||
import Font from './Font/index';
|
||||
import Theme from './Theme/index';
|
||||
|
||||
const Display = ({ close }) => {
|
||||
return (
|
||||
<div className="flex flex-col my-2 gap-10 w-full">
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
<span>
|
||||
Theme
|
||||
</span>
|
||||
<Theme close={close} />
|
||||
</div>
|
||||
<div className="h-[1px] bg-[#aaa5] w-full"></div>
|
||||
<div className="w-fit flex flex-col gap-2">
|
||||
<Font close={close} />
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.theme-mode-selector {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.theme-mode-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.theme-variant-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.theme-variant-label {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.theme-variants {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.theme-variant-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
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: 120px;
|
||||
|
||||
&: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)};
|
||||
}
|
||||
}
|
||||
|
||||
.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: 24px 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
130
packages/bruno-app/src/components/Preferences/Themes/index.js
Normal file
130
packages/bruno-app/src/components/Preferences/Themes/index.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import React from 'react';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { getLightThemes, getDarkThemes } from 'themes/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ThemePreview = ({ themeId, isDark }) => {
|
||||
const bgColor = isDark ? '#1e1e1e' : '#ffffff';
|
||||
const sidebarColor = isDark ? '#252526' : '#f8f8f8';
|
||||
const lineColor = isDark ? '#3d3d3d' : '#e5e5e5';
|
||||
|
||||
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 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">
|
||||
<div>
|
||||
<div className="section-header">Appearance</div>
|
||||
<div className="theme-mode-selector">
|
||||
<label className="theme-mode-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="theme-mode"
|
||||
value="light"
|
||||
checked={storedTheme === 'light'}
|
||||
onChange={() => handleModeChange('light')}
|
||||
/>
|
||||
<span>Light</span>
|
||||
</label>
|
||||
<label className="theme-mode-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="theme-mode"
|
||||
value="dark"
|
||||
checked={storedTheme === 'dark'}
|
||||
onChange={() => handleModeChange('dark')}
|
||||
/>
|
||||
<span>Dark</span>
|
||||
</label>
|
||||
<label className="theme-mode-option">
|
||||
<input
|
||||
type="radio"
|
||||
name="theme-mode"
|
||||
value="system"
|
||||
checked={storedTheme === 'system'}
|
||||
onChange={() => handleModeChange('system')}
|
||||
/>
|
||||
<span>System</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{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;
|
||||
@@ -1,9 +1,11 @@
|
||||
import Modal from 'components/Modal/index';
|
||||
import classnames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import Support from './Support';
|
||||
import General from './General';
|
||||
import Themes from './Themes';
|
||||
import Proxy from './ProxySettings';
|
||||
import Display from './Display';
|
||||
import Keybindings from './Keybindings';
|
||||
@@ -12,7 +14,8 @@ import Beta from './Beta';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const Preferences = ({ onClose }) => {
|
||||
const [tab, setTab] = useState('general');
|
||||
const preferencesTab = useSelector((state) => state.app.preferencesTab);
|
||||
const [tab, setTab] = useState(preferencesTab || 'general');
|
||||
|
||||
const getTabClassname = (tabName) => {
|
||||
return classnames(`tab select-none ${tabName}`, {
|
||||
@@ -26,6 +29,10 @@ const Preferences = ({ onClose }) => {
|
||||
return <General close={onClose} />;
|
||||
}
|
||||
|
||||
case 'themes': {
|
||||
return <Themes close={onClose} />;
|
||||
}
|
||||
|
||||
case 'proxy': {
|
||||
return <Proxy close={onClose} />;
|
||||
}
|
||||
@@ -56,6 +63,9 @@ const Preferences = ({ onClose }) => {
|
||||
<div className={getTabClassname('general')} role="tab" onClick={() => setTab('general')}>
|
||||
General
|
||||
</div>
|
||||
<div className={getTabClassname('themes')} role="tab" onClick={() => setTab('themes')}>
|
||||
Themes
|
||||
</div>
|
||||
<div className={getTabClassname('display')} role="tab" onClick={() => setTab('display')}>
|
||||
Display
|
||||
</div>
|
||||
|
||||
@@ -4,55 +4,124 @@ const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
/* height: 100%; */
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.grpc-message-header {
|
||||
.font-medium {
|
||||
color: ${(props) => props.theme.text};
|
||||
.messages-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.single {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
&.multi {
|
||||
overflow-y: auto;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.single {
|
||||
height: 100%;
|
||||
|
||||
.editor-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.single) {
|
||||
min-height: 240px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
padding: 4px 0px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.message-label {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
background-color: ${(props) => props.theme.dropdown.hoverBg};
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
}
|
||||
|
||||
&.delete:hover {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#grpc-messages-container {
|
||||
/* height: 100%; */
|
||||
position: relative;
|
||||
.editor-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.add-message-btn-container {
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
|
||||
p {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-message-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-top: 8px;
|
||||
background: ${(props) => props.theme.bg || '#fff'};
|
||||
z-index: 15;
|
||||
border-top: 1px solid ${(props) => props.theme.border || 'rgba(0, 0, 0, 0.1)'};
|
||||
|
||||
.add-message-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
border-top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
padding: 8px;
|
||||
background: ${(props) => props.theme.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { get } from 'lodash';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
@@ -8,37 +8,84 @@ import { sendGrpcMessage, generateGrpcSampleMessage } from 'utils/network/index'
|
||||
import useLocalStorage from 'hooks/useLocalStorage';
|
||||
|
||||
import CodeEditor from 'components/CodeEditor/index';
|
||||
import Button from 'ui/Button';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { IconSend, IconRefresh, IconWand, IconPlus, IconTrash, IconChevronDown, IconChevronUp } from '@tabler/icons';
|
||||
import { IconSend, IconRefresh, IconWand, IconPlus, IconTrash } from '@tabler/icons';
|
||||
import ToolHint from 'components/ToolHint/index';
|
||||
import { toastError } from 'utils/common/error';
|
||||
import toast from 'react-hot-toast';
|
||||
import { getAbsoluteFilePath } from 'utils/common/path';
|
||||
import { prettifyJsonString } from 'utils/common/index';
|
||||
|
||||
const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCollapsed, onToggleCollapse, handleRun, canClientSendMultipleMessages }) => {
|
||||
const MessageToolbar = ({
|
||||
index,
|
||||
canClientStream,
|
||||
isConnectionActive,
|
||||
onSend,
|
||||
onRegenerateMessage,
|
||||
onPrettify,
|
||||
onDeleteMessage,
|
||||
showDelete
|
||||
}) => {
|
||||
return (
|
||||
<div className="message-toolbar">
|
||||
<span className="message-label">Message {index + 1}</span>
|
||||
<div className="toolbar-actions">
|
||||
<ToolHint text="Format JSON" toolhintId={`prettify-msg-${index}`}>
|
||||
<button onClick={onPrettify} className="toolbar-btn">
|
||||
<IconWand size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
<ToolHint text="Generate sample" toolhintId={`regenerate-msg-${index}`}>
|
||||
<button onClick={onRegenerateMessage} className="toolbar-btn">
|
||||
<IconRefresh size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
{canClientStream && (
|
||||
<ToolHint text={isConnectionActive ? 'Send message' : 'Connection not active'} toolhintId={`send-msg-${index}`}>
|
||||
<button
|
||||
onClick={onSend}
|
||||
disabled={!isConnectionActive}
|
||||
className={`toolbar-btn ${!isConnectionActive ? 'disabled' : ''}`}
|
||||
>
|
||||
<IconSend size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
)}
|
||||
|
||||
{showDelete && (
|
||||
<ToolHint text="Delete message" toolhintId={`delete-msg-${index}`}>
|
||||
<button onClick={onDeleteMessage} className="toolbar-btn delete">
|
||||
<IconTrash size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SingleGrpcMessage = ({ message, item, collection, index, methodType, handleRun, canClientSendMultipleMessages, isLast }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { displayedTheme, theme } = useTheme();
|
||||
const { displayedTheme } = useTheme();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body');
|
||||
const isConnectionActive = useSelector((state) => state.collections.activeConnections.includes(item.uid));
|
||||
|
||||
// Access gRPC method metadata from local storage
|
||||
const [reflectionCache] = useLocalStorage('bruno.grpc.reflectionCache', {});
|
||||
const [protofileCache] = useLocalStorage('bruno.grpc.protofileCache', {});
|
||||
|
||||
const canClientStream = methodType === 'client-streaming' || methodType === 'bidi-streaming';
|
||||
|
||||
const { name, content } = message;
|
||||
|
||||
const onEdit = (value) => {
|
||||
const currentMessages = [...(body.grpc || [])];
|
||||
|
||||
currentMessages[index] = {
|
||||
name: name ? name : `message ${index + 1}`,
|
||||
content: value
|
||||
};
|
||||
|
||||
dispatch(updateRequestBody({
|
||||
content: currentMessages,
|
||||
itemUid: item.uid,
|
||||
@@ -53,60 +100,51 @@ const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCol
|
||||
console.error('Error sending message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const onRegenerateMessage = async () => {
|
||||
try {
|
||||
const methodPath = item.draft?.request?.method || item.request?.method;
|
||||
|
||||
if (!methodPath) {
|
||||
toastError(new Error('Method path not found in request'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the URL and protoPath to determine which cache to use
|
||||
const url = item.draft?.request?.url || item.request?.url;
|
||||
const protoPath = item.draft?.request?.protoPath || item.request?.protoPath;
|
||||
|
||||
// Find the method metadata from the appropriate cache
|
||||
let methodMetadata = null;
|
||||
if (protoPath) {
|
||||
// Use protofile cache if protoPath is available
|
||||
const absolutePath = getAbsoluteFilePath(collection.pathname, protoPath);
|
||||
const cachedMethods = protofileCache[absolutePath];
|
||||
if (cachedMethods) {
|
||||
methodMetadata = cachedMethods.find((method) => method.path === methodPath);
|
||||
}
|
||||
} else if (url) {
|
||||
// Use reflection cache if no protoPath (reflection mode)
|
||||
const cachedMethods = reflectionCache[url];
|
||||
if (cachedMethods) {
|
||||
methodMetadata = cachedMethods.find((method) => method.path === methodPath);
|
||||
}
|
||||
}
|
||||
|
||||
const result = await generateGrpcSampleMessage(methodPath,
|
||||
content,
|
||||
{
|
||||
arraySize: 2,
|
||||
methodMetadata // Pass the method metadata to the function
|
||||
});
|
||||
const result = await generateGrpcSampleMessage(methodPath, content, {
|
||||
arraySize: 2,
|
||||
methodMetadata
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
const currentMessages = [...(body.grpc || [])];
|
||||
|
||||
currentMessages[index] = {
|
||||
name: name ? name : `message ${index + 1}`,
|
||||
content: result.message
|
||||
};
|
||||
|
||||
dispatch(updateRequestBody({
|
||||
content: currentMessages,
|
||||
itemUid: item.uid,
|
||||
collectionUid: collection.uid
|
||||
}));
|
||||
|
||||
toast.success('Sample message generated successfully!');
|
||||
toast.success('Sample message generated');
|
||||
} else {
|
||||
toastError(new Error(result.error || 'Failed to generate sample message'));
|
||||
}
|
||||
@@ -118,9 +156,7 @@ const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCol
|
||||
|
||||
const onDeleteMessage = () => {
|
||||
const currentMessages = [...(body.grpc || [])];
|
||||
|
||||
currentMessages.splice(index, 1);
|
||||
|
||||
dispatch(updateRequestBody({
|
||||
content: currentMessages,
|
||||
itemUid: item.uid,
|
||||
@@ -131,7 +167,6 @@ const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCol
|
||||
const onPrettify = () => {
|
||||
try {
|
||||
const prettyBodyJson = prettifyJsonString(content);
|
||||
|
||||
const currentMessages = [...(body.grpc || [])];
|
||||
currentMessages[index] = {
|
||||
name: name ? name : `message ${index + 1}`,
|
||||
@@ -147,101 +182,45 @@ const SingleGrpcMessage = ({ message, item, collection, index, methodType, isCol
|
||||
}
|
||||
};
|
||||
|
||||
const getContainerHeight = (canClientSendMultipleMessages && body.grpc.length > 1) ? `${isCollapsed ? '' : 'h-80'}` : 'h-full';
|
||||
const isSingleMessage = !canClientSendMultipleMessages || body.grpc.length === 1;
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col mb-3 border border-neutral-200 dark:border-neutral-800 rounded-md overflow-hidden ${getContainerHeight} relative`}>
|
||||
<div
|
||||
className="grpc-message-header flex items-center justify-between px-3 py-2 bg-neutral-100 dark:bg-neutral-700 cursor-pointer"
|
||||
onClick={onToggleCollapse}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{isCollapsed
|
||||
? <IconChevronDown size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
: <IconChevronUp size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />}
|
||||
<span className="font-medium">{`Message ${canClientStream ? index + 1 : ''}`}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
|
||||
<ToolHint text="Format JSON with proper indentation and spacing" toolhintId={`prettify-msg-${index}`}>
|
||||
<button
|
||||
onClick={onPrettify}
|
||||
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<IconWand size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
<ToolHint text="Generate a new sample message based on schema" toolhintId={`regenerate-msg-${index}`}>
|
||||
<button
|
||||
onClick={onRegenerateMessage}
|
||||
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<IconRefresh size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
{canClientStream && (
|
||||
<ToolHint text={isConnectionActive ? 'Send gRPC message' : 'Connection not active'} toolhintId={`send-msg-${index}`}>
|
||||
<button
|
||||
onClick={onSend}
|
||||
disabled={!isConnectionActive}
|
||||
className={`p-1 rounded ${isConnectionActive ? 'hover:bg-zinc-200 dark:hover:bg-zinc-600' : 'opacity-50 cursor-not-allowed'} transition-colors`}
|
||||
data-testid={`grpc-send-message-${index}`}
|
||||
>
|
||||
<IconSend
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
className={`${isConnectionActive ? 'text-zinc-700 dark:text-zinc-300' : 'text-zinc-400 dark:text-zinc-500'}`}
|
||||
/>
|
||||
</button>
|
||||
</ToolHint>
|
||||
)}
|
||||
|
||||
{index > 0 && (
|
||||
<ToolHint text="Delete this message" toolhintId={`delete-msg-${index}`}>
|
||||
<button
|
||||
onClick={onDeleteMessage}
|
||||
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<IconTrash size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
</button>
|
||||
</ToolHint>
|
||||
)}
|
||||
</div>
|
||||
<div className={`message-container ${isSingleMessage ? 'single' : ''} ${isLast ? 'last' : ''}`}>
|
||||
<MessageToolbar
|
||||
index={index}
|
||||
canClientStream={canClientStream}
|
||||
isConnectionActive={isConnectionActive}
|
||||
onSend={onSend}
|
||||
onRegenerateMessage={onRegenerateMessage}
|
||||
onPrettify={onPrettify}
|
||||
onDeleteMessage={onDeleteMessage}
|
||||
showDelete={index > 0}
|
||||
/>
|
||||
<div className="editor-container">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
value={content}
|
||||
onEdit={onEdit}
|
||||
onRun={handleRun}
|
||||
onSave={onSave}
|
||||
mode="application/ld+json"
|
||||
enableVariableHighlighting={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!isCollapsed && (
|
||||
<div className={`flex ${body.grpc.length === 1 || !canClientSendMultipleMessages ? 'h-full' : 'h-80'} relative`}>
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
value={content}
|
||||
onEdit={onEdit}
|
||||
onRun={handleRun}
|
||||
onSave={onSave}
|
||||
mode="application/ld+json"
|
||||
enableVariableHighlighting={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const GrpcBody = ({ item, collection, handleRun }) => {
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const isVerticalLayout = preferences?.layout?.responsePaneOrientation === 'vertical';
|
||||
const dispatch = useDispatch();
|
||||
const [collapsedMessages, setCollapsedMessages] = useState([]);
|
||||
const messagesContainerRef = useRef(null);
|
||||
const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body');
|
||||
|
||||
const methodType = item.draft ? get(item, 'draft.request.methodType') : get(item, 'request.methodType');
|
||||
const canClientSendMultipleMessages = methodType === 'client-streaming' || methodType === 'bidi-streaming';
|
||||
|
||||
// Auto-scroll to the latest message when messages are added
|
||||
useEffect(() => {
|
||||
if (messagesContainerRef.current && body?.grpc?.length > 0) {
|
||||
const container = messagesContainerRef.current;
|
||||
@@ -249,26 +228,12 @@ const GrpcBody = ({ item, collection, handleRun }) => {
|
||||
}
|
||||
}, [body?.grpc?.length]);
|
||||
|
||||
const toggleMessageCollapse = (index) => {
|
||||
setCollapsedMessages((prev) => {
|
||||
if (prev.includes(index)) {
|
||||
return prev.filter((i) => i !== index);
|
||||
} else {
|
||||
return [...prev, index];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const addNewMessage = () => {
|
||||
const currentMessages = Array.isArray(body.grpc)
|
||||
? [...body.grpc]
|
||||
: [];
|
||||
|
||||
const currentMessages = Array.isArray(body.grpc) ? [...body.grpc] : [];
|
||||
currentMessages.push({
|
||||
name: `message ${currentMessages.length + 1}`,
|
||||
content: '{}'
|
||||
});
|
||||
|
||||
dispatch(updateRequestBody({
|
||||
content: currentMessages,
|
||||
itemUid: item.uid,
|
||||
@@ -278,61 +243,58 @@ const GrpcBody = ({ item, collection, handleRun }) => {
|
||||
|
||||
if (!body?.grpc || !Array.isArray(body.grpc)) {
|
||||
return (
|
||||
<StyledWrapper isVerticalLayout={isVerticalLayout}>
|
||||
<div className="flex flex-col items-center justify-center py-8">
|
||||
<p className="text-zinc-500 dark:text-zinc-400 mb-4">No gRPC messages available</p>
|
||||
<ToolHint text="Add the first message to your gRPC request" toolhintId="add-first-msg">
|
||||
<button
|
||||
onClick={addNewMessage}
|
||||
className="flex items-center justify-center gap-2 py-2 px-4 rounded-md border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-700 hover:bg-neutral-200 dark:hover:bg-neutral-600 transition-colors"
|
||||
>
|
||||
<IconPlus size={16} strokeWidth={1.5} className="text-neutral-700 dark:text-neutral-300" />
|
||||
<span className="font-medium text-neutral-700 dark:text-neutral-300">Add First Message</span>
|
||||
</button>
|
||||
</ToolHint>
|
||||
<StyledWrapper>
|
||||
<div className="empty-state">
|
||||
<p>No gRPC messages available</p>
|
||||
<Button
|
||||
onClick={addNewMessage}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon={<IconPlus size={14} strokeWidth={1.5} />}
|
||||
>
|
||||
Add Message
|
||||
</Button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const messagesToShow = body.grpc.filter((_, index) => canClientSendMultipleMessages || index === 0);
|
||||
|
||||
return (
|
||||
<StyledWrapper isVerticalLayout={isVerticalLayout}>
|
||||
<StyledWrapper>
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
id="grpc-messages-container"
|
||||
data-testid="grpc-messages-container"
|
||||
className={`flex-1 ${body.grpc.length === 1 || !canClientSendMultipleMessages ? 'h-full' : 'overflow-y-auto'} ${canClientSendMultipleMessages && 'pb-16'}`}
|
||||
className={`messages-container ${canClientSendMultipleMessages && messagesToShow.length > 1 ? 'multi' : 'single'}`}
|
||||
>
|
||||
{body.grpc
|
||||
.filter((_, index) => canClientSendMultipleMessages || index === 0)
|
||||
.map((message, index) => (
|
||||
<SingleGrpcMessage
|
||||
key={index}
|
||||
message={message}
|
||||
item={item}
|
||||
collection={collection}
|
||||
index={index}
|
||||
methodType={methodType}
|
||||
isCollapsed={collapsedMessages.includes(index)}
|
||||
onToggleCollapse={() => toggleMessageCollapse(index)}
|
||||
handleRun={handleRun}
|
||||
canClientSendMultipleMessages={canClientSendMultipleMessages}
|
||||
/>
|
||||
))}
|
||||
{messagesToShow.map((message, index) => (
|
||||
<SingleGrpcMessage
|
||||
key={index}
|
||||
message={message}
|
||||
item={item}
|
||||
collection={collection}
|
||||
index={index}
|
||||
methodType={methodType}
|
||||
handleRun={handleRun}
|
||||
canClientSendMultipleMessages={canClientSendMultipleMessages}
|
||||
isLast={index === messagesToShow.length - 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{canClientSendMultipleMessages && (
|
||||
<div className="add-message-btn-container">
|
||||
<ToolHint text="Add a new gRPC message to the request" toolhintId="add-msg-fixed">
|
||||
<button
|
||||
onClick={addNewMessage}
|
||||
className="add-message-btn flex items-center justify-center gap-2 py-2 px-4 rounded-md border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-700 hover:bg-neutral-200 dark:hover:bg-neutral-600 transition-colors shadow-md"
|
||||
data-testid="grpc-add-message-button"
|
||||
>
|
||||
<IconPlus size={16} strokeWidth={1.5} className="text-neutral-700 dark:text-neutral-300" />
|
||||
<span className="font-medium text-neutral-700 dark:text-neutral-300">Add Message</span>
|
||||
</button>
|
||||
</ToolHint>
|
||||
<div className="add-message-footer">
|
||||
<Button
|
||||
onClick={addNewMessage}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
fullWidth
|
||||
icon={<IconPlus size={14} strokeWidth={1.5} />}
|
||||
>
|
||||
Add Message
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -49,7 +49,7 @@ const Wrapper = styled.div`
|
||||
.infotip-text {
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
color: ${(props) => props.theme.text};
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
@@ -72,7 +72,7 @@ const Wrapper = styled.div`
|
||||
margin-left: -4px;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
border-color: ${(props) => props.theme.requestTabs.active.bg} transparent transparent transparent;
|
||||
border-color: ${(props) => props.theme.background.surface2} transparent transparent transparent;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
|
||||
@@ -348,7 +348,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconRefresh
|
||||
color={theme.requestTabs.icon.color}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
className={`${(isReflectionMode ? reflectionManagement.isLoadingMethods : protoFileManagement.isLoadingMethods) ? 'animate-spin' : 'cursor-pointer'}`}
|
||||
data-testid="refresh-methods-icon"
|
||||
/>
|
||||
@@ -367,7 +367,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconCode
|
||||
color={theme.requestTabs.icon.color}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
/>
|
||||
<span className="infotip-text text-xs">Generate grpcurl command</span>
|
||||
</div>
|
||||
@@ -383,7 +383,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconDeviceFloppy
|
||||
color={item.draft ? theme.colors.text.yellow : theme.requestTabs.icon.color}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
className={`${item.draft ? 'cursor-pointer' : 'cursor-default'}`}
|
||||
/>
|
||||
<span className="infotip-text text-xs">
|
||||
@@ -394,7 +394,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
{isConnectionActive && isStreamingMethod && (
|
||||
<div className="connection-controls relative flex items-center h-full gap-3">
|
||||
<div className="infotip" onClick={handleCancelConnection} data-testid="grpc-cancel-connection-button">
|
||||
<IconX color={theme.requestTabs.icon.color} strokeWidth={1.5} size={22} className="cursor-pointer" />
|
||||
<IconX color={theme.requestTabs.icon.color} strokeWidth={1.5} size={20} className="cursor-pointer" />
|
||||
<span className="infotip-text text-xs">Cancel</span>
|
||||
</div>
|
||||
|
||||
@@ -403,7 +403,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconCheck
|
||||
color={theme.colors.text.green}
|
||||
strokeWidth={2}
|
||||
size={22}
|
||||
size={20}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
@@ -420,7 +420,7 @@ const GrpcQueryUrl = ({ item, collection, handleRun }) => {
|
||||
handleRun(e);
|
||||
}}
|
||||
>
|
||||
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={22} />
|
||||
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={20} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
@@ -49,7 +49,7 @@ const Wrapper = styled.div`
|
||||
.infotiptext {
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
color: ${(props) => props.theme.text};
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
@@ -72,7 +72,7 @@ const Wrapper = styled.div`
|
||||
margin-left: -4px;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
border-color: ${(props) => props.theme.requestTabs.active.bg} transparent transparent transparent;
|
||||
border-color: ${(props) => props.theme.background.surface2} transparent transparent transparent;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
|
||||
@@ -9,7 +9,7 @@ const Wrapper = styled.div`
|
||||
border-radius: 3px;
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
color: ${(props) => props.theme.brand};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
div.title {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -17,13 +17,13 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
color: ${(props) => props.theme.brand};
|
||||
}
|
||||
}
|
||||
|
||||
.caret {
|
||||
color: rgb(140, 140, 140);
|
||||
fill: rgb(140 140 140);
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
fill: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.single {
|
||||
height: 100%;
|
||||
|
||||
.editor-container {
|
||||
height: calc(100% - 32px);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.single) {
|
||||
min-height: 240px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 4px;
|
||||
padding: 4px 0px;
|
||||
height: 32px;
|
||||
flex-shrink: 0;
|
||||
|
||||
.message-label {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.toolbar-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.dropdown.hoverBg};
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
|
||||
&.delete:hover {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IconChevronDown, IconChevronUp, IconTrash, IconWand } from '@tabler/icons';
|
||||
import { IconTrash, IconWand } from '@tabler/icons';
|
||||
import CodeEditor from 'components/CodeEditor/index';
|
||||
import ToolHint from 'components/ToolHint/index';
|
||||
import { get } from 'lodash';
|
||||
@@ -13,6 +13,7 @@ import { toastError } from 'utils/common/error';
|
||||
import { prettifyJsonString } from 'utils/common/index';
|
||||
import xmlFormat from 'xml-formatter';
|
||||
import WSRequestBodyMode from '../BodyMode/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
export const TYPE_BY_DECODER = {
|
||||
base64: 'binary',
|
||||
@@ -28,10 +29,9 @@ export const SingleWSMessage = ({
|
||||
collection,
|
||||
index,
|
||||
methodType,
|
||||
isCollapsed,
|
||||
onToggleCollapse,
|
||||
handleRun,
|
||||
canClientSendMultipleMessages
|
||||
canClientSendMultipleMessages,
|
||||
isLast
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { displayedTheme } = useTheme();
|
||||
@@ -88,9 +88,6 @@ export const SingleWSMessage = ({
|
||||
}));
|
||||
};
|
||||
|
||||
const getContainerHeight
|
||||
= canClientSendMultipleMessages && body.ws.length > 1 ? `${isCollapsed ? '' : 'h-80'}` : 'h-full';
|
||||
|
||||
let codeType = messageFormat;
|
||||
if (TYPE_BY_DECODER[type]) {
|
||||
codeType = TYPE_BY_DECODER[type];
|
||||
@@ -144,60 +141,44 @@ export const SingleWSMessage = ({
|
||||
}
|
||||
};
|
||||
|
||||
const isSingleMessage = !canClientSendMultipleMessages || body.ws.length === 1;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col mb-3 border border-neutral-200 dark:border-neutral-800 rounded-md overflow-hidden ${getContainerHeight} relative`}
|
||||
>
|
||||
<div
|
||||
className="ws-message-header flex items-center justify-between px-3 py-2 bg-neutral-100 dark:bg-neutral-700 cursor-pointer"
|
||||
onClick={onToggleCollapse}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{isCollapsed ? (
|
||||
<IconChevronDown size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
) : (
|
||||
<IconChevronUp size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
|
||||
<StyledWrapper className={`message-container ${isSingleMessage ? 'single' : ''} ${isLast ? 'last' : ''}`}>
|
||||
<div className="message-toolbar">
|
||||
<span className="message-label">Message {index + 1}</span>
|
||||
<div className="toolbar-actions">
|
||||
<WSRequestBodyMode mode={messageFormat} onModeChange={onUpdateMessageType} />
|
||||
<ToolHint text="Prettify" toolhintId={`prettify-msg-${index}`}>
|
||||
<button
|
||||
onClick={onPrettify}
|
||||
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<IconWand size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
|
||||
<ToolHint text="Format" toolhintId={`prettify-msg-${index}`}>
|
||||
<button onClick={onPrettify} className="toolbar-btn">
|
||||
<IconWand size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
{index > 0 && (
|
||||
<ToolHint text="Delete this message" toolhintId={`delete-msg-${index}`}>
|
||||
<button
|
||||
onClick={onDeleteMessage}
|
||||
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
||||
>
|
||||
<IconTrash size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
<ToolHint text="Delete message" toolhintId={`delete-msg-${index}`}>
|
||||
<button onClick={onDeleteMessage} className="toolbar-btn delete">
|
||||
<IconTrash size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</ToolHint>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<div className={`flex ${body.ws.length === 1 || !canClientSendMultipleMessages ? 'h-full' : 'h-80'} relative`}>
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
value={content}
|
||||
onEdit={onEdit}
|
||||
onRun={handleRun}
|
||||
onSave={onSave}
|
||||
mode={codemirrorMode[codeType] ?? 'text/plain'}
|
||||
enableVariableHighlighting={true}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="editor-container">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={displayedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
value={content}
|
||||
onEdit={onEdit}
|
||||
onRun={handleRun}
|
||||
onSave={onSave}
|
||||
mode={codemirrorMode[codeType] ?? 'text/plain'}
|
||||
enableVariableHighlighting={true}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,49 +4,45 @@ const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.ws-message-header {
|
||||
.font-medium {
|
||||
color: ${(props) => props.theme.text};
|
||||
.messages-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.single {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
&.multi {
|
||||
overflow-y: auto;
|
||||
padding-bottom: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-message-btn-container {
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
|
||||
p {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.add-message-footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-top: 8px;
|
||||
background: ${(props) => props.theme.bg || '#fff'};
|
||||
z-index: 15;
|
||||
border-top: 1px solid ${(props) => props.theme.border || 'rgba(0, 0, 0, 0.1)'};
|
||||
|
||||
.add-message-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
border-top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
padding: 8px;
|
||||
background: ${(props) => props.theme.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { get } from 'lodash';
|
||||
import { updateRequestBody } from 'providers/ReduxStore/slices/collections';
|
||||
import { IconPlus } from '@tabler/icons';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import ToolHint from 'components/ToolHint/index';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import Button from 'ui/Button';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { SingleWSMessage } from './SingleWSMessage/index';
|
||||
|
||||
const WSBody = ({ item, collection, handleRun }) => {
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const isVerticalLayout = preferences?.layout?.responsePaneOrientation === 'vertical';
|
||||
const dispatch = useDispatch();
|
||||
const [collapsedMessages, setCollapsedMessages] = useState([]);
|
||||
const messagesContainerRef = useRef(null);
|
||||
const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body');
|
||||
|
||||
@@ -26,16 +23,6 @@ const WSBody = ({ item, collection, handleRun }) => {
|
||||
}
|
||||
}, [body?.ws?.length]);
|
||||
|
||||
const toggleMessageCollapse = (index) => {
|
||||
setCollapsedMessages((prev) => {
|
||||
if (prev.includes(index)) {
|
||||
return prev.filter((i) => i !== index);
|
||||
} else {
|
||||
return [...prev, index];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const addNewMessage = () => {
|
||||
const currentMessages = Array.isArray(body.ws) ? [...body.ws] : [];
|
||||
|
||||
@@ -53,60 +40,58 @@ const WSBody = ({ item, collection, handleRun }) => {
|
||||
|
||||
if (!body?.ws || !Array.isArray(body.ws)) {
|
||||
return (
|
||||
<StyledWrapper isVerticalLayout={isVerticalLayout}>
|
||||
<div className="flex flex-col items-center justify-center py-8">
|
||||
<p className="text-zinc-500 dark:text-zinc-400 mb-4">No WebSocket messages available</p>
|
||||
<ToolHint text="Add the first message to your WebSocket request" toolhintId="add-first-msg">
|
||||
<button
|
||||
onClick={addNewMessage}
|
||||
className="flex items-center justify-center gap-2 py-2 px-4 rounded-md border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-700 hover:bg-neutral-200 dark:hover:bg-neutral-600 transition-colors"
|
||||
>
|
||||
<IconPlus size={16} strokeWidth={1.5} className="text-neutral-700 dark:text-neutral-300" />
|
||||
<span className="font-medium text-neutral-700 dark:text-neutral-300">Add First Message</span>
|
||||
</button>
|
||||
</ToolHint>
|
||||
<StyledWrapper>
|
||||
<div className="empty-state">
|
||||
<p>No WebSocket messages available</p>
|
||||
<Button
|
||||
onClick={addNewMessage}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon={<IconPlus size={14} strokeWidth={1.5} />}
|
||||
>
|
||||
Add Message
|
||||
</Button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const messagesToShow = body.ws.filter((_, index) => canClientSendMultipleMessages || index === 0);
|
||||
|
||||
return (
|
||||
<StyledWrapper isVerticalLayout={isVerticalLayout}>
|
||||
<StyledWrapper>
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
id="ws-messages-container"
|
||||
className={`flex-1 ${body.ws.length === 1 || !canClientSendMultipleMessages ? 'h-full' : 'overflow-y-auto'} ${canClientSendMultipleMessages && 'pb-16'
|
||||
}`}
|
||||
className={`messages-container ${canClientSendMultipleMessages && messagesToShow.length > 1 ? 'multi' : 'single'}`}
|
||||
>
|
||||
{body.ws
|
||||
.filter((_, index) => canClientSendMultipleMessages || index === 0)
|
||||
.map((message, index) => (
|
||||
<SingleWSMessage
|
||||
key={index}
|
||||
message={message}
|
||||
item={item}
|
||||
collection={collection}
|
||||
index={index}
|
||||
methodType={methodType}
|
||||
isCollapsed={collapsedMessages.includes(index)}
|
||||
onToggleCollapse={() => toggleMessageCollapse(index)}
|
||||
handleRun={handleRun}
|
||||
canClientSendMultipleMessages={canClientSendMultipleMessages}
|
||||
/>
|
||||
))}
|
||||
{messagesToShow.map((message, index) => (
|
||||
<SingleWSMessage
|
||||
key={index}
|
||||
message={message}
|
||||
item={item}
|
||||
collection={collection}
|
||||
index={index}
|
||||
methodType={methodType}
|
||||
handleRun={handleRun}
|
||||
canClientSendMultipleMessages={canClientSendMultipleMessages}
|
||||
isLast={index === messagesToShow.length - 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{canClientSendMultipleMessages && (
|
||||
<div className="add-message-btn-container">
|
||||
<ToolHint text="Add a new WebSocket message to the request" toolhintId="add-msg-fixed">
|
||||
<button
|
||||
onClick={addNewMessage}
|
||||
className="add-message-btn flex items-center justify-center gap-2 py-2 px-4 rounded-md border border-neutral-200 dark:border-neutral-800 bg-neutral-100 dark:bg-neutral-700 hover:bg-neutral-200 dark:hover:bg-neutral-600 transition-colors shadow-md"
|
||||
>
|
||||
<IconPlus size={16} strokeWidth={1.5} className="text-neutral-700 dark:text-neutral-300" />
|
||||
<span className="font-medium text-neutral-700 dark:text-neutral-300">Add Message</span>
|
||||
</button>
|
||||
</ToolHint>
|
||||
<div className="add-message-footer">
|
||||
<Button
|
||||
onClick={addNewMessage}
|
||||
variant="filled"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
fullWidth
|
||||
icon={<IconPlus size={14} strokeWidth={1.5} />}
|
||||
>
|
||||
Add Message
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -62,7 +62,7 @@ const StyledWrapper = styled.div`
|
||||
.infotip-text {
|
||||
visibility: hidden;
|
||||
width: auto;
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
color: ${(props) => props.theme.text};
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
@@ -85,7 +85,7 @@ const StyledWrapper = styled.div`
|
||||
margin-left: -4px;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
border-color: ${(props) => props.theme.requestTabs.active.bg} transparent transparent transparent;
|
||||
border-color: ${(props) => props.theme.background.surface2} transparent transparent transparent;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
@@ -95,7 +95,7 @@ const StyledWrapper = styled.div`
|
||||
.connection-controls {
|
||||
.infotip {
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.requestTabPanel.url.errorHoverBg};
|
||||
background-color: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 6%, transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconDeviceFloppy
|
||||
color={hasChanges ? theme.colors.text.yellow : theme.requestTabs.icon.color}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
className={`${hasChanges ? 'cursor-pointer' : 'cursor-default'}`}
|
||||
/>
|
||||
<span className="infotip-text text-xs">
|
||||
@@ -164,7 +164,7 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
|
||||
<IconPlugConnectedX
|
||||
color={theme.colors.text.danger}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
className="cursor-pointer"
|
||||
/>
|
||||
<span className="infotip-text text-xs">Close Connection</span>
|
||||
@@ -181,7 +181,7 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
|
||||
})}
|
||||
color={theme.colors.text.green}
|
||||
strokeWidth={1.5}
|
||||
size={22}
|
||||
size={20}
|
||||
/>
|
||||
<span className="infotip-text text-xs">Connect</span>
|
||||
</div>
|
||||
@@ -189,7 +189,7 @@ const WsQueryUrl = ({ item, collection, handleRun }) => {
|
||||
)}
|
||||
|
||||
<div data-testid="run-button" className="cursor-pointer" onClick={handleRunClick}>
|
||||
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={22} />
|
||||
<IconArrowRight color={theme.requestTabPanel.url.icon} strokeWidth={1.5} size={20} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,8 +7,6 @@ import { clearGlobalEnvironmentDraft } from 'providers/ReduxStore/slices/global-
|
||||
import { saveGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import darkTheme from 'themes/dark';
|
||||
import lightTheme from 'themes/light';
|
||||
import { findItemInCollection, hasRequestChanges } from 'utils/collections';
|
||||
import ConfirmRequestClose from './ConfirmRequestClose';
|
||||
import ConfirmCollectionClose from './ConfirmCollectionClose';
|
||||
@@ -28,8 +26,7 @@ import toast from 'react-hot-toast';
|
||||
|
||||
const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUid, hasOverflow, setHasOverflow, dropdownContainerRef }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
const theme = storedTheme === 'dark' ? darkTheme : lightTheme;
|
||||
const { theme } = useTheme();
|
||||
const tabNameRef = useRef(null);
|
||||
const tabLabelRef = useRef(null);
|
||||
const lastOverflowStateRef = useRef(null);
|
||||
|
||||
@@ -26,7 +26,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
color: ${(props) => props.theme.brand};
|
||||
}
|
||||
|
||||
&.cursor-default {
|
||||
|
||||
@@ -18,7 +18,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.selected-body-mode {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
color: ${(props) => props.theme.brand};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ const StyledWrapper = styled.div`
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
font-size: ${(props) => props.theme.font.size.base};
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -1,42 +1,39 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
border-left: 4px solid ${(props) => props.theme.colors.text.danger};
|
||||
border-top: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
border-radius: 0.375rem;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
max-height: 200px;
|
||||
min-height: 70px;
|
||||
border-left: 3px solid ${(props) => props.theme.colors.text.danger};
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
max-height: 160px;
|
||||
overflow-y: auto;
|
||||
background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')};
|
||||
background-color: color-mix(in srgb, ${(props) => props.theme.colors.text.danger} 6%, transparent);
|
||||
margin-bottom: 8px;
|
||||
|
||||
.close-button {
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: ${(props) => props.theme.text};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.375rem;
|
||||
margin-bottom: 4px;
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-family: monospace;
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
line-height: 1.25rem;
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -6,7 +6,7 @@ const GrpcError = ({ error, onClose }) => {
|
||||
if (!error) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-4 mb-2">
|
||||
<StyledWrapper>
|
||||
<div className="flex items-start gap-3 px-4 py-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="error-title">gRPC Server Error</div>
|
||||
|
||||
@@ -3,94 +3,79 @@ import styled from 'styled-components';
|
||||
const StyledWrapper = styled.div`
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: ${(props) => props.theme.bg};
|
||||
border-radius: 4px;
|
||||
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
font-family: ${(props) => (props.font === 'default' ? 'monospace' : props.font)};
|
||||
font-size: ${(props) => (props.fontSize ? props.fontSize : '13px')};
|
||||
.empty-state {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.accordion-header {
|
||||
background-color: ${(props) => props.theme.requestTabPanel.card.bg};
|
||||
.responses-container {
|
||||
height: 100%;
|
||||
|
||||
&.single {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.multi {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.messages-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&:not(.last) {
|
||||
border-bottom: 1px solid ${(props) => props.theme.border.border1};
|
||||
}
|
||||
}
|
||||
|
||||
.message-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 0;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.plainGrid.hoverBg};
|
||||
}
|
||||
|
||||
&.open {
|
||||
background-color: ${(props) => props.theme.plainGrid.hoverBg};
|
||||
}
|
||||
}
|
||||
|
||||
.error-header {
|
||||
background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(185, 28, 28, 0.1)' : '#fee2e2')};
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
|
||||
div.tabs {
|
||||
div.tab {
|
||||
padding: 6px 0px;
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
&:focus-within,
|
||||
&:focus-visible,
|
||||
&:target {
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
font-weight: ${(props) => props.theme.tabs.active.fontWeight} !important;
|
||||
color: ${(props) => props.theme.tabs.active.color} !important;
|
||||
border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
|
||||
.toggle-btn {
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stream-status {
|
||||
display: inline-flex;
|
||||
.message-label {
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
}
|
||||
|
||||
.latest-badge {
|
||||
margin-left: 8px;
|
||||
padding: 2px 6px;
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.colors.text.green};
|
||||
background-color: color-mix(in srgb, ${(props) => props.theme.colors.text.green} 10%, transparent);
|
||||
border-radius: ${(props) => props.theme.border.radius.sm};
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.complete {
|
||||
color: ${(props) => props.theme.colors.text.green};
|
||||
}
|
||||
|
||||
&.cancelled {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
|
||||
&.streaming {
|
||||
color: ${(props) => props.theme.colors.text.blue};
|
||||
}
|
||||
justify-content: center;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.message-counter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.response-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.response-message {
|
||||
.message-content {
|
||||
height: 240px;
|
||||
margin-bottom: 8px;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-panel-background);
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,46 +1,31 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Accordion from 'components/Accordion';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
import { get } from 'lodash';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { formatISO9075 } from 'date-fns';
|
||||
import GrpcError from '../GrpcError';
|
||||
import { IconChevronDown, IconChevronUp } from '@tabler/icons';
|
||||
|
||||
const GrpcQueryResult = ({ item, collection }) => {
|
||||
const { displayedTheme } = useTheme();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const [showErrorMessage, setShowErrorMessage] = useState(true);
|
||||
const [expandedIndex, setExpandedIndex] = useState(0);
|
||||
|
||||
const response = item.response || {};
|
||||
const responsesList = response?.responses || [];
|
||||
// Reverse the responses list to show the most recent at the top
|
||||
const reversedResponsesList = [...responsesList].reverse();
|
||||
const hasError = response.isError;
|
||||
const hasResponses = responsesList.length > 0;
|
||||
const errorMessage = response.error;
|
||||
|
||||
// Reset error visibility when a new response is received
|
||||
useEffect(() => {
|
||||
if (hasError) {
|
||||
setShowErrorMessage(true);
|
||||
}
|
||||
}, [response, hasError]);
|
||||
|
||||
// Format a timestamp to a human-readable format
|
||||
const formatTimestamp = (timestamp) => {
|
||||
if (!timestamp) return 'Unknown time';
|
||||
|
||||
try {
|
||||
const date = new Date(timestamp);
|
||||
return formatISO9075(date);
|
||||
} catch (e) {
|
||||
return 'Invalid time';
|
||||
}
|
||||
};
|
||||
|
||||
// Format JSON for display
|
||||
const formatJSON = (data) => {
|
||||
try {
|
||||
if (typeof data === 'string') {
|
||||
@@ -55,7 +40,7 @@ const GrpcQueryResult = ({ item, collection }) => {
|
||||
if (!hasResponses && !hasError) {
|
||||
return (
|
||||
<StyledWrapper className="w-full h-full relative flex flex-col">
|
||||
<div className="text-gray-500 dark:text-gray-400 p-4">No messages received</div>
|
||||
<div className="empty-state">No messages received</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
}
|
||||
@@ -64,9 +49,8 @@ const GrpcQueryResult = ({ item, collection }) => {
|
||||
<StyledWrapper className="w-full h-full relative flex flex-col mt-2" data-testid="grpc-response-content">
|
||||
{hasError && showErrorMessage && <GrpcError error={errorMessage} onClose={() => setShowErrorMessage(false)} />}
|
||||
{hasResponses && (
|
||||
<div className={`overflow-y-auto ${responsesList.length === 1 ? 'flex-1' : ''}`} data-testid="grpc-responses-container">
|
||||
<div className={`responses-container ${responsesList.length === 1 ? 'single' : 'multi'}`} data-testid="grpc-responses-container">
|
||||
{responsesList.length === 1 ? (
|
||||
// Single message - render directly without accordion
|
||||
<div className="h-full" data-testid="grpc-single-response">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
@@ -79,43 +63,55 @@ const GrpcQueryResult = ({ item, collection }) => {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
// Multiple messages - use accordion
|
||||
<Accordion defaultIndex={0} dataTestId="grpc-responses-accordion">
|
||||
{reversedResponsesList.map((response, index) => {
|
||||
// Calculate the original response number (for display purposes)
|
||||
<div className="messages-list" data-testid="grpc-responses-list">
|
||||
{reversedResponsesList.map((resp, index) => {
|
||||
const originalIndex = responsesList.length - index - 1;
|
||||
const isExpanded = expandedIndex === index;
|
||||
|
||||
return (
|
||||
<Accordion.Item key={originalIndex} index={index} data-testid={`grpc-response-item-${originalIndex}`}>
|
||||
<Accordion.Header index={index} style={{ padding: '8px 12px', minHeight: '40px' }}>
|
||||
<div className="flex justify-between w-full">
|
||||
<div className="font-medium">
|
||||
Response {originalIndex + 1} {index === 0 ? '(Latest)' : ''}
|
||||
</div>
|
||||
</div>
|
||||
</Accordion.Header>
|
||||
<Accordion.Content index={index} style={{ padding: '0px' }}>
|
||||
<div className="h-60">
|
||||
<div
|
||||
key={originalIndex}
|
||||
className={`message-item ${isExpanded ? 'expanded' : 'collapsed'} ${index === reversedResponsesList.length - 1 ? 'last' : ''}`}
|
||||
data-testid={`grpc-response-item-${originalIndex}`}
|
||||
>
|
||||
<div
|
||||
className="message-header"
|
||||
onClick={() => setExpandedIndex(isExpanded ? -1 : index)}
|
||||
>
|
||||
<span className="message-label">
|
||||
Response {originalIndex + 1}
|
||||
{index === 0 && <span className="latest-badge">Latest</span>}
|
||||
</span>
|
||||
<button className="toggle-btn">
|
||||
{isExpanded ? (
|
||||
<IconChevronUp size={16} strokeWidth={1.5} />
|
||||
) : (
|
||||
<IconChevronDown size={16} strokeWidth={1.5} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{isExpanded && (
|
||||
<div className="message-content">
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
fontSize={get(preferences, 'font.codeFontSize')}
|
||||
theme={displayedTheme}
|
||||
value={formatJSON(response)}
|
||||
value={formatJSON(resp)}
|
||||
mode="application/json"
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
</Accordion.Content>
|
||||
</Accordion.Item>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Accordion>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{hasError && !hasResponses && !showErrorMessage && (
|
||||
<div className="text-gray-500 dark:text-gray-400 p-4">
|
||||
<div className="empty-state">
|
||||
No messages received. A server error occurred but has been dismissed.
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -6,17 +6,24 @@ const StyledWrapper = styled.div`
|
||||
border-collapse: collapse;
|
||||
|
||||
thead {
|
||||
color: #777777;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
padding: 8px 10px;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
&.key {
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.value {
|
||||
word-break: break-all;
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +32,10 @@ const StyledWrapper = styled.div`
|
||||
background-color: ${(props) => props.theme.table.striped};
|
||||
}
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const GrpcResponseHeaders = ({ metadata }) => {
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="2" className="text-center py-4 text-gray-500">
|
||||
<td colSpan="2" className="text-center py-4 empty-message">
|
||||
No metadata received
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -6,17 +6,24 @@ const StyledWrapper = styled.div`
|
||||
border-collapse: collapse;
|
||||
|
||||
thead {
|
||||
color: #777777;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 6px 10px;
|
||||
padding: 8px 10px;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
&.key {
|
||||
color: ${(props) => props.theme.colors.text.subtext1};
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&.value {
|
||||
word-break: break-all;
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +32,10 @@ const StyledWrapper = styled.div`
|
||||
background-color: ${(props) => props.theme.table.striped};
|
||||
}
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ const ResponseTrailers = ({ trailers }) => {
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan="2" className="text-center py-4 text-gray-500">
|
||||
<td colSpan="2" className="text-center py-4 empty-message">
|
||||
No trailers received
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -12,7 +12,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
|
||||
.send-icon {
|
||||
color: ${(props) => props.theme.requestTabPanel.responseSendIcon};
|
||||
color: ${(props) => props.theme.background.surface2};
|
||||
}
|
||||
|
||||
&.vertical-layout {
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
.button-dropdown-button {
|
||||
color: ${(props) => props.theme.dropdown.primaryText};
|
||||
color: ${(props) => props.theme.text};
|
||||
border-color: ${(props) => props.theme.workspace.border};
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
button {
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
@@ -18,7 +18,7 @@ const StyledWrapper = styled.div`
|
||||
.cursor-pointer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
|
||||
&:hover {
|
||||
color: var(--color-tab-active);
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.requestTabPanel.responseStatus};
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
@@ -2,7 +2,7 @@ import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: ${(props) => props.theme.requestTabPanel.responseStatus};
|
||||
`;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const StyledWrapper = styled.div`
|
||||
padding-top: 20%;
|
||||
width: 100%;
|
||||
.send-icon {
|
||||
color: ${(props) => props.theme.requestTabPanel.responseSendIcon};
|
||||
color: ${(props) => props.theme.background.surface2};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
flex-shrink: 1;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -12,7 +12,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -1,31 +1,59 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
overflow-y: auto;
|
||||
overflow-y: auto;
|
||||
|
||||
.ws-message.new {
|
||||
background-color: ${({ theme }) => theme.table.striped};
|
||||
.empty-state {
|
||||
padding: 1rem;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
|
||||
.ws-message:not(:last-child) {
|
||||
border-bottom: 1px solid ${({ theme }) => theme.table.border};
|
||||
}
|
||||
|
||||
.ws-message:not(:last-child).open {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
.ws-incoming {
|
||||
.ws-message {
|
||||
background: ${(props) => props.theme.bg};
|
||||
border-color: ${(props) => props.theme.table.border};
|
||||
|
||||
&.new {
|
||||
background-color: ${({ theme }) => theme.table.striped};
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.border1};
|
||||
}
|
||||
|
||||
&:not(:last-child).open {
|
||||
border-bottom-width: 0px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
|
||||
.message-timestamp {
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
|
||||
.chevron-icon {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
}
|
||||
|
||||
.ws-outgoing {
|
||||
background: ${(props) => props.theme.bg};
|
||||
border-color: ${(props) => props.theme.table.border};
|
||||
.ws-incoming .message-type-icon {
|
||||
color: ${(props) => props.theme.colors.text.green};
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
.ws-outgoing .message-type-icon {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
}
|
||||
|
||||
.ws-info .message-type-icon {
|
||||
color: ${(props) => props.theme.colors.text.blue};
|
||||
}
|
||||
|
||||
.ws-error .message-type-icon {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
@@ -34,11 +62,12 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
div[role='tablist'] {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
.active {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -100,15 +100,17 @@ const WSMessageItem = ({ message, inFocus }) => {
|
||||
}}
|
||||
className={classnames('ws-message flex flex-col p-2', {
|
||||
'ws-incoming': isIncoming,
|
||||
'ws-outgoing': !isIncoming,
|
||||
'ws-outgoing': isOutgoing,
|
||||
'ws-info': isInfo,
|
||||
'ws-error': isError,
|
||||
'open': isOpen,
|
||||
'new': isNew
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={classnames('flex items-center justify-between', {
|
||||
'cursor-pointer': !isInfo,
|
||||
'cursor-not-allowed': isInfo
|
||||
'cursor-pointer': canOpenMessage,
|
||||
'cursor-not-allowed': !canOpenMessage
|
||||
})}
|
||||
onClick={(e) => {
|
||||
if (!canOpenMessage) return;
|
||||
@@ -116,35 +118,26 @@ const WSMessageItem = ({ message, inFocus }) => {
|
||||
}}
|
||||
>
|
||||
<div className="flex min-w-0 shrink">
|
||||
<span
|
||||
className={classnames('font-medium flex items-center gap-1',
|
||||
{
|
||||
'text-green-700': isIncoming,
|
||||
'text-yellow-700': isOutgoing,
|
||||
'text-blue-700': isInfo,
|
||||
'text-red-700': isError,
|
||||
'text-red-700': isError
|
||||
})}
|
||||
>
|
||||
<span className="message-type-icon">
|
||||
<TypeIcon type={message.type} />
|
||||
</span>
|
||||
<span className="ml-3 text-ellipsis max-w-full overflow-hidden text-nowrap">{parsedContent.content}</span>
|
||||
<span className="ml-3 text-ellipsis max-w-full overflow-hidden text-nowrap message-content">{parsedContent.content}</span>
|
||||
</div>
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<div className="flex shrink-0 gap-2 items-center">
|
||||
{message.timestamp && (
|
||||
<span className="text-xs text-gray-400">{new Date(message.timestamp).toISOString()}</span>
|
||||
<span className="message-timestamp">{new Date(message.timestamp).toISOString()}</span>
|
||||
)}
|
||||
{canOpenMessage
|
||||
? (
|
||||
<span className="text-gray-600">
|
||||
<span className="chevron-icon">
|
||||
{isOpen ? (
|
||||
<IconChevronDown size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
<IconChevronDown size={16} strokeWidth={1.5} />
|
||||
) : (
|
||||
<IconChevronRight size={16} strokeWidth={1.5} className="text-zinc-700 dark:text-zinc-300" />
|
||||
<IconChevronRight size={16} strokeWidth={1.5} />
|
||||
)}
|
||||
</span>
|
||||
)
|
||||
: <span class="w-4"></span>}
|
||||
: <span className="w-4"></span>}
|
||||
</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
@@ -188,11 +181,11 @@ const WSMessageItem = ({ message, inFocus }) => {
|
||||
|
||||
const WSMessagesList = ({ order = -1, messages = [] }) => {
|
||||
if (!messages.length) {
|
||||
return <div className="p-4 text-gray-500">No messages yet.</div>;
|
||||
return <StyledWrapper><div className="empty-state">No messages yet.</div></StyledWrapper>;
|
||||
}
|
||||
const ordered = order === -1 ? messages : messages.slice().reverse();
|
||||
return (
|
||||
<StyledWrapper className="ws-messages-list mt-1 flex flex-col">
|
||||
<StyledWrapper className="ws-messages-list mt-2 flex flex-col">
|
||||
{ordered.map((msg, idx, src) => {
|
||||
const inFocus = order === -1 ? src.length - 1 === idx : idx === 0;
|
||||
return <WSMessageItem key={msg.timestamp} inFocus={inFocus} id={idx} message={msg} />;
|
||||
|
||||
@@ -7,7 +7,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
|
||||
@@ -88,7 +88,7 @@ const StyledWrapper = styled.div`
|
||||
transition: transform 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&.is-selected {
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
}
|
||||
|
||||
&.is-dragging {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.tabs {
|
||||
@@ -7,7 +8,7 @@ const StyledWrapper = styled.div`
|
||||
border: none;
|
||||
border-bottom: solid 2px transparent;
|
||||
margin-right: ${(props) => props.theme.tabs.marginRight};
|
||||
color: var(--color-tab-inactive);
|
||||
color: ${(props) => props.theme.colors.text.subtext0};
|
||||
cursor: pointer;
|
||||
|
||||
&:focus,
|
||||
@@ -31,14 +32,24 @@ const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
padding: 10px;
|
||||
border: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
|
||||
background-color: ${(props) => props.theme.sidebar.bg};
|
||||
border: 1px solid ${(props) => props.theme.border.border0};
|
||||
background-color: ${(props) => props.theme.background.base};
|
||||
color: ${(props) => props.theme.text};
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&.no-padding {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.note-warning {
|
||||
color: ${(props) => props.theme.colors.text.warning};
|
||||
background-color: ${(props) => rgba(props.theme.colors.text.warning, 0.06)};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: ${(props) => props.theme.listItem.hoverBg};
|
||||
background-color: ${(props) => props.theme.background.mantle};
|
||||
border-color: ${(props) => props.theme.border.border2};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -105,7 +105,7 @@ const ShareCollection = ({ onClose, collectionUid }) => {
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`flex !flex-col share-button ${
|
||||
className={`flex !flex-col share-button no-padding ${
|
||||
isCollectionLoading
|
||||
? 'opacity-50 cursor-not-allowed'
|
||||
: 'cursor-pointer'
|
||||
@@ -113,7 +113,7 @@ const ShareCollection = ({ onClose, collectionUid }) => {
|
||||
onClick={isCollectionLoading ? undefined : handleExportPostmanCollection}
|
||||
>
|
||||
{hasNonExportableRequestTypes.has && (
|
||||
<div className="px-3 py-2 bg-yellow-50 w-full dark:bg-yellow-900/20 text-yellow-700 dark:text-yellow-400 text-xs border-b border-yellow-100 dark:border-yellow-800/20 flex items-center">
|
||||
<div className="px-3 py-2 w-full flex items-center note-warning">
|
||||
<IconAlertTriangle size={16} className="mr-2 flex-shrink-0" />
|
||||
<span>
|
||||
Note:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.api-specs-badge {
|
||||
margin-inline: 0.5rem;
|
||||
background-color: ${(props) => props.theme.sidebar.badge.bg};
|
||||
border-radius: 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,21 +0,0 @@
|
||||
import { IconFileCode } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ApiSpecsBadge = () => {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="items-center mt-2 relative">
|
||||
<div className="api-specs-badge flex items-center justify-between px-2">
|
||||
<div className="flex items-center py-1 select-none">
|
||||
<span className="mr-2">
|
||||
<IconFileCode size={18} strokeWidth={1.5} />
|
||||
</span>
|
||||
<span>APIs</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApiSpecsBadge;
|
||||
@@ -5,7 +5,7 @@ const StyledWrapper = styled.div`
|
||||
height: 50vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: ${(props) => props.theme.collection.environment.settings.bg};
|
||||
background-color: ${(props) => props.theme.background.base};
|
||||
|
||||
.code-generator {
|
||||
display: flex;
|
||||
|
||||
@@ -25,7 +25,7 @@ const StyledWrapper = styled.div`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 6px 12px;
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
border: 1px solid ${(props) => props.theme.requestTabs.bottomBorder};
|
||||
border-radius: 4px;
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
@@ -320,12 +320,12 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) =>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<span className="mr-2">
|
||||
<Button type="button" size="sm" color="secondary" variant="ghost" onClick={onClose}>
|
||||
<Button type="button" color="secondary" variant="ghost" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
</span>
|
||||
<span>
|
||||
<Button type="submit" size="sm">
|
||||
<Button type="submit">
|
||||
Create
|
||||
</Button>
|
||||
</span>
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import styled from 'styled-components';
|
||||
import { darken, rgba } from 'polished';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
.current-group {
|
||||
background-color: ${(props) => props.theme.sidebar.badge.bg};
|
||||
background-color: ${(props) => props.theme.background.surface1};
|
||||
border-radius: 4px;
|
||||
padding: 0.4rem;
|
||||
padding: 0.3rem 0.6rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid ${(props) => props.theme.sidebar.badge.border || 'transparent'};
|
||||
border: 1px solid ${(props) => props.theme.background.surface2};
|
||||
}
|
||||
|
||||
.current-group:hover {
|
||||
background-color: ${(props) => props.theme.sidebar.badge.hoverBg || props.theme.sidebar.badge.bg};
|
||||
}
|
||||
background-color: ${(props) => darken(0.03, props.theme.background.surface1)};
|
||||
border-color: ${(props) => darken(0.03, props.theme.background.surface2)};
|
||||
|
||||
/* Fix dropdown positioning */
|
||||
[data-tippy-root] {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.theme-menu {
|
||||
min-width: 160px;
|
||||
padding: 4px 0;
|
||||
background: ${(props) => props.theme.dropdown.bg};
|
||||
border: 1px solid ${(props) => props.theme.dropdown.separator};
|
||||
border-radius: ${(props) => props.theme.border.radius.md};
|
||||
box-shadow: ${(props) => props.theme.dropdown.shadow};
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
padding: 6px 12px 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
color: ${(props) => props.theme.dropdown.mutedText};
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
color: ${(props) => props.theme.dropdown.color};
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.dropdown.hoverBg};
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: ${(props) => props.theme.dropdown.selectedColor};
|
||||
background: ${(props) => rgba(props.theme.dropdown.selectedColor, 0.07)};
|
||||
}
|
||||
|
||||
&.has-submenu {
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-icon {
|
||||
margin-right: 8px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.menu-item-label {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
color: ${(props) => props.theme.dropdown.selectedColor};
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.chevron-icon {
|
||||
opacity: 0.6;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.menu-divider {
|
||||
height: 1px;
|
||||
background: ${(props) => props.theme.dropdown.separator};
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.submenu {
|
||||
min-width: 180px;
|
||||
padding: 4px 0;
|
||||
background: ${(props) => props.theme.dropdown.bg};
|
||||
border: 1px solid ${(props) => props.theme.dropdown.separator};
|
||||
border-radius: ${(props) => props.theme.border.radius.md};
|
||||
box-shadow: ${(props) => props.theme.dropdown.shadow};
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,181 @@
|
||||
import React, { useState } from 'react';
|
||||
import Tippy from '@tippyjs/react';
|
||||
import { IconChevronRight, IconCheck, IconSun, IconMoon, IconDeviceDesktop } from '@tabler/icons';
|
||||
import ToolHint from 'components/ToolHint';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { getLightThemes, getDarkThemes } from 'themes/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ThemeDropdown = ({ children }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [lightSubmenuOpen, setLightSubmenuOpen] = useState(false);
|
||||
const [darkSubmenuOpen, setDarkSubmenuOpen] = useState(false);
|
||||
const [tooltipEnabled, setTooltipEnabled] = useState(true);
|
||||
|
||||
const {
|
||||
storedTheme,
|
||||
setStoredTheme,
|
||||
themeVariantLight,
|
||||
themeVariantDark,
|
||||
setThemeVariantLight,
|
||||
setThemeVariantDark
|
||||
} = useTheme();
|
||||
|
||||
const lightThemes = getLightThemes();
|
||||
const darkThemes = getDarkThemes();
|
||||
|
||||
const handleModeSelect = (mode) => {
|
||||
setStoredTheme(mode);
|
||||
};
|
||||
|
||||
const handleThemeSelect = (themeId, isLight) => {
|
||||
if (isLight) {
|
||||
setThemeVariantLight(themeId);
|
||||
} else {
|
||||
setThemeVariantDark(themeId);
|
||||
}
|
||||
setIsOpen(false);
|
||||
setLightSubmenuOpen(false);
|
||||
setDarkSubmenuOpen(false);
|
||||
};
|
||||
|
||||
const renderSubmenu = (themes, isLight, currentVariant) => (
|
||||
<div className="submenu">
|
||||
{themes.map((theme) => (
|
||||
<div
|
||||
key={theme.id}
|
||||
className={`menu-item ${currentVariant === theme.id ? 'active' : ''}`}
|
||||
onClick={() => handleThemeSelect(theme.id, isLight)}
|
||||
>
|
||||
<span className="menu-item-label">{theme.name}</span>
|
||||
{currentVariant === theme.id && (
|
||||
<IconCheck size={14} strokeWidth={2} className="check-icon" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
const menuContent = (
|
||||
<StyledWrapper>
|
||||
<div className="theme-menu">
|
||||
{/* Mode Section */}
|
||||
<div className="menu-label">Mode</div>
|
||||
<div
|
||||
className={`menu-item ${storedTheme === 'light' ? 'active' : ''}`}
|
||||
onClick={() => handleModeSelect('light')}
|
||||
>
|
||||
<IconSun size={14} strokeWidth={1.5} className="menu-item-icon" />
|
||||
<span className="menu-item-label">Light</span>
|
||||
{storedTheme === 'light' && (
|
||||
<IconCheck size={14} strokeWidth={2} className="check-icon" />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`menu-item ${storedTheme === 'dark' ? 'active' : ''}`}
|
||||
onClick={() => handleModeSelect('dark')}
|
||||
>
|
||||
<IconMoon size={14} strokeWidth={1.5} className="menu-item-icon" />
|
||||
<span className="menu-item-label">Dark</span>
|
||||
{storedTheme === 'dark' && (
|
||||
<IconCheck size={14} strokeWidth={2} className="check-icon" />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`menu-item ${storedTheme === 'system' ? 'active' : ''}`}
|
||||
onClick={() => handleModeSelect('system')}
|
||||
>
|
||||
<IconDeviceDesktop size={14} strokeWidth={1.5} className="menu-item-icon" />
|
||||
<span className="menu-item-label">System</span>
|
||||
{storedTheme === 'system' && (
|
||||
<IconCheck size={14} strokeWidth={2} className="check-icon" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="menu-divider" />
|
||||
|
||||
{/* Light Themes with Submenu */}
|
||||
<Tippy
|
||||
content={renderSubmenu(lightThemes, true, themeVariantLight)}
|
||||
placement="right-start"
|
||||
interactive={true}
|
||||
arrow={false}
|
||||
offset={[0, 2]}
|
||||
animation={false}
|
||||
visible={lightSubmenuOpen}
|
||||
onClickOutside={() => setLightSubmenuOpen(false)}
|
||||
appendTo="parent"
|
||||
>
|
||||
<div
|
||||
className="menu-item has-submenu"
|
||||
onMouseEnter={() => {
|
||||
setLightSubmenuOpen(true);
|
||||
setDarkSubmenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="menu-item-label">Light Themes</span>
|
||||
<IconChevronRight size={14} strokeWidth={2} className="chevron-icon" />
|
||||
</div>
|
||||
</Tippy>
|
||||
|
||||
{/* Dark Themes with Submenu */}
|
||||
<Tippy
|
||||
content={renderSubmenu(darkThemes, false, themeVariantDark)}
|
||||
placement="right-start"
|
||||
interactive={true}
|
||||
arrow={false}
|
||||
offset={[0, 2]}
|
||||
animation={false}
|
||||
visible={darkSubmenuOpen}
|
||||
onClickOutside={() => setDarkSubmenuOpen(false)}
|
||||
appendTo="parent"
|
||||
>
|
||||
<div
|
||||
className="menu-item has-submenu"
|
||||
onMouseEnter={() => {
|
||||
setDarkSubmenuOpen(true);
|
||||
setLightSubmenuOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="menu-item-label">Dark Themes</span>
|
||||
<IconChevronRight size={14} strokeWidth={2} className="chevron-icon" />
|
||||
</div>
|
||||
</Tippy>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
const handleOpen = () => {
|
||||
setTooltipEnabled(false);
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
setLightSubmenuOpen(false);
|
||||
setDarkSubmenuOpen(false);
|
||||
// Small delay before re-enabling tooltip to prevent flash
|
||||
setTimeout(() => setTooltipEnabled(true), 100);
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolHint text="Theme" toolhintId="ThemeDropdown" place="top" offset={10} hidden={!tooltipEnabled}>
|
||||
<Tippy
|
||||
content={menuContent}
|
||||
placement="top-start"
|
||||
interactive={true}
|
||||
arrow={false}
|
||||
animation={false}
|
||||
visible={isOpen}
|
||||
onClickOutside={handleClose}
|
||||
appendTo="parent"
|
||||
>
|
||||
<div onClick={() => isOpen ? handleClose() : handleOpen()}>
|
||||
{children}
|
||||
</div>
|
||||
</Tippy>
|
||||
</ToolHint>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeDropdown;
|
||||
@@ -1,14 +1,14 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { IconSettings, IconCookie, IconTool, IconSearch } from '@tabler/icons';
|
||||
import { IconSettings, IconCookie, IconTool, IconSearch, IconPalette } from '@tabler/icons';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import { getKeyBindingsForActionAllOS } from 'providers/Hotkeys/keyMappings';
|
||||
import ToolHint from 'components/ToolHint';
|
||||
import Preferences from 'components/Preferences';
|
||||
import IconSidebarToggle from 'components/Icons/IconSidebarToggle';
|
||||
import Cookies from 'components/Cookies';
|
||||
import Notifications from 'components/Notifications';
|
||||
import Portal from 'components/Portal';
|
||||
import ThemeDropdown from './ThemeDropdown';
|
||||
import { showPreferences } from 'providers/ReduxStore/slices/app';
|
||||
import { openConsole } from 'providers/ReduxStore/slices/logs';
|
||||
import { useApp } from 'providers/App';
|
||||
@@ -81,6 +81,17 @@ const StatusBar = () => {
|
||||
</button>
|
||||
</ToolHint>
|
||||
|
||||
<ThemeDropdown>
|
||||
<button
|
||||
className="status-bar-button"
|
||||
data-trigger="theme"
|
||||
tabIndex={0}
|
||||
aria-label="Change Theme"
|
||||
>
|
||||
<IconPalette size={16} strokeWidth={1.5} aria-hidden="true" />
|
||||
</button>
|
||||
</ThemeDropdown>
|
||||
|
||||
<ToolHint text="Notifications" toolhintId="Notifications" place="top" offset={10}>
|
||||
<div className="status-bar-button">
|
||||
<Notifications />
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
table {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
overflow-y: hidden;
|
||||
overflow-x: auto;
|
||||
padding: 0 1.5px;
|
||||
border-radius: 8px;
|
||||
|
||||
// for icon hover
|
||||
position: inherit;
|
||||
|
||||
grid-template-columns: ${({ columns }) =>
|
||||
columns?.[0]?.width
|
||||
? columns.map((col) => `${col?.width}`).join(' ')
|
||||
: columns.map((col) => `${100 / columns.length}%`).join(' ')};
|
||||
}
|
||||
|
||||
table thead,
|
||||
table tbody,
|
||||
table tr {
|
||||
display: contents;
|
||||
border: none;
|
||||
}
|
||||
|
||||
table th {
|
||||
position: relative;
|
||||
border: none;
|
||||
border-bottom: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
background: ${(props) => props.theme.examples.table.thead.bg};
|
||||
color: ${(props) => props.theme.examples.table.thead.color};
|
||||
font-weight: 400;
|
||||
|
||||
tr td {
|
||||
border: none;
|
||||
color: ${(props) => props.theme.examples.table.thead.color};
|
||||
background: ${(props) => props.theme.examples.table.thead.bg};
|
||||
}
|
||||
}
|
||||
|
||||
table tr td {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
}
|
||||
|
||||
tr {
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
tr.dragging {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
tr.hovered {
|
||||
transform: translateY(10px); /* Adjust the value as needed for the animation effect */
|
||||
}
|
||||
|
||||
table tr th {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
border-top: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
font-weight: 400;
|
||||
|
||||
&:nth-child(1) {
|
||||
border-left: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,109 +0,0 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const Table = ({ minColumnWidth = 1, headers = [], children }) => {
|
||||
const [activeColumnIndex, setActiveColumnIndex] = useState(null);
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const columns = headers?.map((item) => ({
|
||||
...item,
|
||||
ref: useRef()
|
||||
}));
|
||||
|
||||
const updateDivHeights = () => {
|
||||
if (tableRef.current) {
|
||||
const height = tableRef.current.offsetHeight;
|
||||
columns.forEach((col) => {
|
||||
if (col.ref.current) {
|
||||
col.ref.current.querySelector('.resizer').style.height = `${height}px`;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateDivHeights();
|
||||
window.addEventListener('resize', updateDivHeights);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateDivHeights);
|
||||
};
|
||||
}, [columns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableRef.current) {
|
||||
const observer = new MutationObserver(updateDivHeights);
|
||||
observer.observe(tableRef.current, { childList: true, subtree: true });
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}
|
||||
}, [columns]);
|
||||
|
||||
const handleMouseDown = (index) => (e) => {
|
||||
setActiveColumnIndex(index);
|
||||
};
|
||||
|
||||
const handleMouseMove = useCallback((e) => {
|
||||
const gridColumns = columns.map((col, i) => {
|
||||
if (i === activeColumnIndex) {
|
||||
const width = e.clientX - col.ref?.current?.getBoundingClientRect()?.left;
|
||||
|
||||
if (width >= minColumnWidth) {
|
||||
return `${width}px`;
|
||||
}
|
||||
}
|
||||
return `${col.ref.current.offsetWidth}px`;
|
||||
});
|
||||
|
||||
tableRef.current.style.gridTemplateColumns = `${gridColumns.join(' ')}`;
|
||||
},
|
||||
[activeColumnIndex, columns, minColumnWidth]);
|
||||
|
||||
const removeListeners = useCallback(() => {
|
||||
window.removeEventListener('mousemove', handleMouseMove);
|
||||
window.removeEventListener('mouseup', removeListeners);
|
||||
}, [handleMouseMove]);
|
||||
|
||||
const handleMouseUp = useCallback(() => {
|
||||
setActiveColumnIndex(null);
|
||||
removeListeners?.();
|
||||
}, [removeListeners]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeColumnIndex !== null) {
|
||||
window.addEventListener('mousemove', handleMouseMove);
|
||||
window.addEventListener('mouseup', handleMouseUp);
|
||||
}
|
||||
return () => {
|
||||
removeListeners();
|
||||
};
|
||||
}, [activeColumnIndex, handleMouseMove, handleMouseUp, removeListeners]);
|
||||
|
||||
return (
|
||||
<StyledWrapper columns={columns}>
|
||||
<div className="relative">
|
||||
<table ref={tableRef} className="inherit">
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map(({ ref, name }, i) => (
|
||||
<th ref={ref} key={name} title={name}>
|
||||
<span>{name}</span>
|
||||
<div
|
||||
className="resizer absolute cursor-col-resize w-[4px] right-[-2px] top-0 z-10 opacity-50 hover:bg-blue-500 active:bg-blue-500"
|
||||
onMouseDown={handleMouseDown(i)}
|
||||
>
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
@@ -25,14 +25,14 @@ const StyledWrapper = styled.div`
|
||||
|
||||
table th {
|
||||
position: relative;
|
||||
border-bottom: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-bottom: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
}
|
||||
|
||||
table tr td {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
border-top: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-right: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-top: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-right: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
}
|
||||
|
||||
tr {
|
||||
@@ -50,11 +50,11 @@ const StyledWrapper = styled.div`
|
||||
table tr th {
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
border-top: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-right: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-top: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
border-right: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
|
||||
&:nth-child(1) {
|
||||
border-left: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder}77;
|
||||
border-left: 1px solid ${(props) => props.theme.border.BORDER0};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -26,7 +26,7 @@ const StyledWrapper = styled.div`
|
||||
cursor: default;
|
||||
|
||||
&:has(.tag-remove:hover) {
|
||||
background-color: ${(props) => props.theme.requestTabs.active.bg};
|
||||
background-color: ${(props) => props.theme.background.surface2};
|
||||
border-color: ${(props) => props.theme.requestTabs.active.border || props.theme.requestTabs.bottomBorder};
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-1px);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
background-color: ${(props) => props.theme.sidebar.badge};
|
||||
color: ${(props) => props.theme.text};
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { Tooltip as ReactToolHint } from 'react-tooltip';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
|
||||
const ToolHint = ({
|
||||
@@ -20,8 +19,8 @@ const ToolHint = ({
|
||||
const { theme: contextTheme } = useTheme();
|
||||
const appliedTheme = theme || contextTheme;
|
||||
|
||||
const toolhintBackgroundColor = appliedTheme?.sidebar.badge.bg || 'black';
|
||||
const toolhintTextColor = appliedTheme?.text || 'white';
|
||||
const toolhintBackgroundColor = appliedTheme?.background.surface1;
|
||||
const toolhintTextColor = appliedTheme?.text;
|
||||
|
||||
const combinedToolhintStyle = {
|
||||
...tooltipStyle,
|
||||
@@ -40,21 +39,19 @@ const ToolHint = ({
|
||||
<>
|
||||
{!anchorSelect && <span id={toolhintId} className={className}>{children}</span>}
|
||||
{anchorSelect && children}
|
||||
|
||||
<StyledWrapper theme={appliedTheme}>
|
||||
<ReactToolHint
|
||||
{...toolhintProps_final}
|
||||
content={anchorSelect ? undefined : text}
|
||||
className="toolhint"
|
||||
offset={offset}
|
||||
place={place}
|
||||
hidden={hidden}
|
||||
positionStrategy={positionStrategy}
|
||||
noArrow={true}
|
||||
delayShow={delayShow}
|
||||
style={combinedToolhintStyle}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
<ReactToolHint
|
||||
{...toolhintProps_final}
|
||||
content={anchorSelect ? undefined : text}
|
||||
className="toolhint"
|
||||
offset={offset}
|
||||
place={place}
|
||||
hidden={hidden}
|
||||
positionStrategy={positionStrategy}
|
||||
noArrow={true}
|
||||
delayShow={delayShow}
|
||||
style={combinedToolhintStyle}
|
||||
opacity={1}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -88,8 +88,8 @@ const VariablesEditor = ({ collection }) => {
|
||||
|
||||
const reactInspectorTheme
|
||||
= displayedTheme === 'light'
|
||||
? { ...chromeLight, OBJECT_VALUE_STRING_COLOR: theme.variables.runtime.color }
|
||||
: { ...chromeDark, OBJECT_VALUE_STRING_COLOR: theme.variables.runtime.color };
|
||||
? { ...chromeLight, OBJECT_VALUE_STRING_COLOR: theme.text.base }
|
||||
: { ...chromeDark, OBJECT_VALUE_STRING_COLOR: theme.text.base };
|
||||
|
||||
return (
|
||||
<StyledWrapper className="px-4 py-4 overflow-auto">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.main-content {
|
||||
@@ -67,7 +68,7 @@ const StyledWrapper = styled.div`
|
||||
color: ${(props) => props.theme.colors.text.green};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.colors.text.green}1A;
|
||||
background-color: ${(props) => rgba(props.theme.colors.text.green, 0.1)};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ const StyledWrapper = styled.div`
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.colors.text.danger}1A;
|
||||
background-color: ${(props) => rgba(props.theme.colors.text.danger, 0.1)};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,21 +94,6 @@ const StyledWrapper = styled.div`
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
color: ${(props) => props.theme.text};
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -88,7 +88,7 @@ const StyledWrapper = styled.div`
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 8px;
|
||||
background: ${(props) => props.theme.workspace.card.bg};
|
||||
background: ${(props) => props.theme.background.mantle};
|
||||
border: 1px solid ${(props) => props.theme.workspace.border};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
margin-bottom: 16px;
|
||||
|
||||
@@ -8,6 +8,7 @@ import Markdown from 'components/MarkDown';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { IconFileText, IconEdit, IconX } from '@tabler/icons';
|
||||
import Button from 'ui/Button';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const WorkspaceDocs = ({ workspace }) => {
|
||||
@@ -113,9 +114,9 @@ const WorkspaceDocs = ({ workspace }) => {
|
||||
<li>Key workflows</li>
|
||||
<li>Resources & FAQs</li>
|
||||
</ul>
|
||||
<button className="add-docs-btn" onClick={handleAddDocumentation}>
|
||||
<Button color="secondary" size="md" onClick={handleAddDocumentation}>
|
||||
Add Documentation
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ const Wrapper = styled.div`
|
||||
.table-container {
|
||||
overflow-y: auto;
|
||||
border-radius: 8px;
|
||||
border: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border: solid 1px ${(props) => props.theme.border.border0};
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -45,8 +45,8 @@ const Wrapper = styled.div`
|
||||
|
||||
td {
|
||||
padding: 5px 10px !important;
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: ${(props) => props.theme.border.border0};
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
@@ -63,8 +63,8 @@ const Wrapper = styled.div`
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-right: ${(props) => props.theme.workspace.environments.indentBorder};
|
||||
border-bottom: solid 1px ${(props) => props.theme.border.border0};
|
||||
border-right: solid 1px ${(props) => props.theme.border.border0};
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import styled from 'styled-components';
|
||||
import { rgba } from 'polished';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
@@ -174,7 +175,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: ${(props) => props.theme.workspace.environments.activeBg};
|
||||
background: ${(props) => props.theme.background.surface0};
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
|
||||
@@ -250,19 +251,18 @@ const StyledWrapper = styled.div`
|
||||
transition: all 0.15s ease;
|
||||
|
||||
&.save {
|
||||
color: ${(props) => props.theme.textLink};
|
||||
color: ${(props) => props.theme.colors.text.green};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
background: ${(props) => rgba(props.theme.colors.text.green, 0.1)};
|
||||
}
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
color: ${(props) => props.theme.text};
|
||||
background: ${(props) => rgba(props.theme.colors.text.danger, 0.1)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding-top: 10%;
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
svg {
|
||||
@@ -32,22 +31,6 @@ const StyledWrapper = styled.div`
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.shared-button {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
|
||||
background: ${(props) => props.theme.sidebar.bg};
|
||||
color: ${(props) => props.theme.text};
|
||||
cursor: pointer;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
border-color: ${(props) => props.theme.textLink};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -6,18 +6,19 @@ import StyledWrapper from './StyledWrapper';
|
||||
import { IconFileAlert } from '@tabler/icons';
|
||||
import ImportEnvironmentModal from 'components/Environments/Common/ImportEnvironmentModal';
|
||||
import ExportEnvironmentModal from 'components/Environments/Common/ExportEnvironmentModal';
|
||||
import Button from 'ui/Button';
|
||||
|
||||
const DefaultTab = ({ setTab }) => (
|
||||
<div className="empty-state">
|
||||
<IconFileAlert size={48} strokeWidth={1.5} />
|
||||
<div className="title">No Environments</div>
|
||||
<div className="actions">
|
||||
<button className="shared-button" onClick={() => setTab('create')}>
|
||||
<Button size="sm" color="secondary" onClick={() => setTab('create')}>
|
||||
Create Environment
|
||||
</button>
|
||||
<button className="shared-button" onClick={() => setTab('import')}>
|
||||
</Button>
|
||||
<Button size="sm" color="secondary" onClick={() => setTab('import')}>
|
||||
Import Environment
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -97,25 +97,6 @@ const StyledWrapper = styled.div`
|
||||
.collection-dropdown {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease;
|
||||
color: ${(props) => props.theme.text};
|
||||
font-size: ${(props) => props.theme.font.size.sm};
|
||||
|
||||
&:hover {
|
||||
background: ${(props) => props.theme.listItem.hoverBg};
|
||||
}
|
||||
|
||||
&.dropdown-item-danger {
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -288,7 +288,7 @@ const CollectionsList = ({ workspace }) => {
|
||||
<span>Share</span>
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item dropdown-item-danger"
|
||||
className="dropdown-item delete-item"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRemoveCollection(collection);
|
||||
|
||||
@@ -6,6 +6,7 @@ import toast from 'react-hot-toast';
|
||||
import CreateCollection from 'components/Sidebar/CreateCollection';
|
||||
import ImportCollection from 'components/Sidebar/ImportCollection';
|
||||
import ImportCollectionLocation from 'components/Sidebar/ImportCollectionLocation';
|
||||
import Button from 'ui/Button';
|
||||
import CollectionsList from './CollectionsList';
|
||||
import WorkspaceDocs from '../WorkspaceDocs';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
@@ -107,18 +108,30 @@ const WorkspaceOverview = ({ workspace }) => {
|
||||
<div className="quick-actions-section">
|
||||
<div className="section-title">Quick Actions</div>
|
||||
<div className="quick-actions-buttons">
|
||||
<button className="quick-action-btn" onClick={handleCreateCollection}>
|
||||
<IconPlus size={14} strokeWidth={1.5} />
|
||||
<span>Create Collection</span>
|
||||
</button>
|
||||
<button className="quick-action-btn" onClick={handleOpenCollection}>
|
||||
<IconFolder size={14} strokeWidth={1.5} />
|
||||
<span>Open Collection</span>
|
||||
</button>
|
||||
<button className="quick-action-btn" onClick={handleImportCollection}>
|
||||
<IconDownload size={14} strokeWidth={1.5} />
|
||||
<span>Import Collection</span>
|
||||
</button>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon={<IconPlus size={14} strokeWidth={1.5} />}
|
||||
onClick={handleCreateCollection}
|
||||
>
|
||||
Create Collection
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon={<IconFolder size={14} strokeWidth={1.5} />}
|
||||
onClick={handleOpenCollection}
|
||||
>
|
||||
Open Collection
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon={<IconDownload size={14} strokeWidth={1.5} />}
|
||||
onClick={handleImportCollection}
|
||||
>
|
||||
Import Collection
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const initialState = {
|
||||
screenWidth: 500,
|
||||
showHomePage: false,
|
||||
showPreferences: false,
|
||||
preferencesTab: null,
|
||||
showApiSpecPage: false,
|
||||
showManageWorkspacePage: false,
|
||||
isEnvironmentSettingsModalOpen: false,
|
||||
@@ -95,7 +96,13 @@ export const appSlice = createSlice({
|
||||
state.showApiSpecPage = false;
|
||||
},
|
||||
showPreferences: (state, action) => {
|
||||
state.showPreferences = action.payload;
|
||||
if (typeof action.payload === 'object') {
|
||||
state.showPreferences = action.payload.show;
|
||||
state.preferencesTab = action.payload.tab || null;
|
||||
} else {
|
||||
state.showPreferences = action.payload;
|
||||
state.preferencesTab = null;
|
||||
}
|
||||
},
|
||||
updatePreferences: (state, action) => {
|
||||
state.preferences = action.payload;
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Validator } from 'jsonschema';
|
||||
import toast from 'react-hot-toast';
|
||||
import themes from 'themes/index';
|
||||
import themeSchema from 'themes/schema';
|
||||
import useLocalStorage from 'hooks/useLocalStorage/index';
|
||||
|
||||
import { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { createContext, useContext, useEffect, useState, useMemo } from 'react';
|
||||
import { ThemeProvider as SCThemeProvider } from 'styled-components';
|
||||
|
||||
const validator = new Validator();
|
||||
|
||||
export const ThemeContext = createContext();
|
||||
export const ThemeProvider = (props) => {
|
||||
const isBrowserThemeLight = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||
const [displayedTheme, setDisplayedTheme] = useState(isBrowserThemeLight ? 'light' : 'dark');
|
||||
const [storedTheme, setStoredTheme] = useLocalStorage('bruno.theme', 'system');
|
||||
const [themeVariantLight, setThemeVariantLight] = useLocalStorage('bruno.themeVariantLight', 'light');
|
||||
const [themeVariantDark, setThemeVariantDark] = useLocalStorage('bruno.themeVariantDark', 'dark');
|
||||
const toggleHtml = () => {
|
||||
const html = document.querySelector('html');
|
||||
if (html) {
|
||||
@@ -45,14 +52,50 @@ export const ThemeProvider = (props) => {
|
||||
// storedTheme can have 3 values: 'light', 'dark', 'system'
|
||||
// displayedTheme can have 2 values: 'light', 'dark'
|
||||
|
||||
const theme = storedTheme === 'system' ? themes[displayedTheme] : themes[storedTheme];
|
||||
const themeOptions = Object.keys(themes);
|
||||
// Get the appropriate variant based on the current display mode
|
||||
const theme = useMemo(() => {
|
||||
const isLightMode = displayedTheme === 'light';
|
||||
const variantName = isLightMode ? themeVariantLight : themeVariantDark;
|
||||
const fallbackTheme = isLightMode ? themes.light : themes.dark;
|
||||
const fallbackName = isLightMode ? 'light' : 'dark';
|
||||
|
||||
// Check if the variant exists in themes
|
||||
const selectedTheme = themes[variantName];
|
||||
if (!selectedTheme) {
|
||||
// Only show toast if using a non-default variant that doesn't exist
|
||||
if (variantName !== fallbackName) {
|
||||
toast.error(`Theme "${variantName}" not found. Using default ${fallbackName} theme.`, {
|
||||
duration: 4000,
|
||||
id: `theme-not-found-${variantName}` // Prevent duplicate toasts
|
||||
});
|
||||
}
|
||||
return fallbackTheme;
|
||||
}
|
||||
|
||||
// Validate the theme against the schema
|
||||
const validationResult = validator.validate(selectedTheme, themeSchema);
|
||||
if (!validationResult.valid) {
|
||||
const errors = validationResult.errors?.map((e) => e.stack).join(', ') || 'Unknown validation error';
|
||||
console.error(`Theme "${variantName}" validation failed:`, errors);
|
||||
toast.error(`Invalid theme "${variantName}". Using default ${fallbackName} theme.`, {
|
||||
duration: 4000,
|
||||
id: `theme-invalid-${variantName}` // Prevent duplicate toasts
|
||||
});
|
||||
return fallbackTheme;
|
||||
}
|
||||
|
||||
return selectedTheme;
|
||||
}, [displayedTheme, themeVariantLight, themeVariantDark]);
|
||||
|
||||
const value = {
|
||||
theme,
|
||||
themeOptions,
|
||||
storedTheme,
|
||||
displayedTheme,
|
||||
setStoredTheme
|
||||
setStoredTheme,
|
||||
themeVariantLight,
|
||||
setThemeVariantLight,
|
||||
themeVariantDark,
|
||||
setThemeVariantDark
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user