mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-04 09:58:35 +00:00
Compare commits
367 Commits
v1.0.0
...
feature/in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b059faca53 | ||
|
|
9ad265e74f | ||
|
|
aed1b41da6 | ||
|
|
d8354dca14 | ||
|
|
e375ffbed1 | ||
|
|
7de5bbbdf6 | ||
|
|
dbb5e912eb | ||
|
|
4e34aba1ca | ||
|
|
b5fccef417 | ||
|
|
db9aeec498 | ||
|
|
3e627522b7 | ||
|
|
85f24eec77 | ||
|
|
bdfcd78f3a | ||
|
|
fce164001b | ||
|
|
814d31e638 | ||
|
|
51d4dbd69b | ||
|
|
05cab18e18 | ||
|
|
fcc3a1e944 | ||
|
|
45395754bf | ||
|
|
0db6103b69 | ||
|
|
2608ec035e | ||
|
|
d0c25d46c9 | ||
|
|
d62982d52d | ||
|
|
2810c6758d | ||
|
|
3967859c51 | ||
|
|
ef5df9e114 | ||
|
|
75e19574ac | ||
|
|
b15ad5ca71 | ||
|
|
0bd3ba493f | ||
|
|
d2d1994546 | ||
|
|
e4ae0c0357 | ||
|
|
52cec963a3 | ||
|
|
f797c5d06b | ||
|
|
65879c8994 | ||
|
|
2e544183db | ||
|
|
ceccddf7f1 | ||
|
|
c1f5da1280 | ||
|
|
63aa3ded1c | ||
|
|
26c7d4f532 | ||
|
|
b7b4453278 | ||
|
|
49a51d6028 | ||
|
|
abb24c93c5 | ||
|
|
5ba2c98e1d | ||
|
|
bc01188c98 | ||
|
|
1754ea9f59 | ||
|
|
2aa073c69a | ||
|
|
48ec87ec8c | ||
|
|
35b6f7bb0a | ||
|
|
fff0293600 | ||
|
|
41d0698a87 | ||
|
|
f7ea8c93a6 | ||
|
|
83c9629820 | ||
|
|
d6f6032c6f | ||
|
|
e49999bb56 | ||
|
|
8b92055413 | ||
|
|
5ca7f6b7ad | ||
|
|
83f5763e01 | ||
|
|
4a5196c8f5 | ||
|
|
eba065aa7e | ||
|
|
10dc1d95b0 | ||
|
|
00c7b40593 | ||
|
|
b0ee137277 | ||
|
|
887c65b0d8 | ||
|
|
84905ed80f | ||
|
|
1f4ab3b5bd | ||
|
|
da983599ab | ||
|
|
fe11e45703 | ||
|
|
a08fd7eb52 | ||
|
|
d268b4786a | ||
|
|
4b76bf85f4 | ||
|
|
ed6f91533b | ||
|
|
7899b04c40 | ||
|
|
cdddf8af76 | ||
|
|
9e44c4a95f | ||
|
|
3982f9c3c3 | ||
|
|
46dda28c3a | ||
|
|
9f6890b769 | ||
|
|
eb340d4ace | ||
|
|
66f917ecec | ||
|
|
e3865d4710 | ||
|
|
983fb2c4fd | ||
|
|
cb6513c580 | ||
|
|
b8451d01ca | ||
|
|
a15a4e4a2d | ||
|
|
cada4f201a | ||
|
|
93661bd0d2 | ||
|
|
454e0e5260 | ||
|
|
647a819051 | ||
|
|
99c2dd9030 | ||
|
|
ab37e53346 | ||
|
|
8ace37848b | ||
|
|
447f40053d | ||
|
|
de530a889c | ||
|
|
882341c35b | ||
|
|
174f99f9fb | ||
|
|
2103ab20bf | ||
|
|
f0e22cb5df | ||
|
|
ee2295aec1 | ||
|
|
2d16e07747 | ||
|
|
a839d311dc | ||
|
|
82bafd5268 | ||
|
|
fc09697404 | ||
|
|
ee2d7a187a | ||
|
|
f8ff305cf4 | ||
|
|
c2c2ef6e2b | ||
|
|
aa18f17fb9 | ||
|
|
baeeeb2bb0 | ||
|
|
fff3e6d88a | ||
|
|
7953863b9d | ||
|
|
10183319c4 | ||
|
|
89c5fc2f03 | ||
|
|
8ddec6bf0c | ||
|
|
d257db27b8 | ||
|
|
08935c64bb | ||
|
|
8e1d04f5c1 | ||
|
|
da3bd95add | ||
|
|
cc89e34b4c | ||
|
|
56a456a9b6 | ||
|
|
eaa448306b | ||
|
|
9f8dba0fb2 | ||
|
|
784f63ca5b | ||
|
|
77cdc2179d | ||
|
|
d749e4b848 | ||
|
|
bb729b5793 | ||
|
|
a60f351736 | ||
|
|
0d0c4166c1 | ||
|
|
567744c2ce | ||
|
|
cb47b7be5f | ||
|
|
3061507284 | ||
|
|
d3bec5631e | ||
|
|
98b45a2fd4 | ||
|
|
dc39538d02 | ||
|
|
6d3a518043 | ||
|
|
e24b75e7d8 | ||
|
|
318036a279 | ||
|
|
b482dd68a5 | ||
|
|
1c83f5c885 | ||
|
|
e0969d6aab | ||
|
|
07eee055d4 | ||
|
|
19efa2fd35 | ||
|
|
480f8cf877 | ||
|
|
9246bf4fcb | ||
|
|
11d0bc01d2 | ||
|
|
06d62175bf | ||
|
|
96d50ebd93 | ||
|
|
06ccbc8dc2 | ||
|
|
cf329e58e7 | ||
|
|
85d55393ef | ||
|
|
f84933553f | ||
|
|
6632ae1dcb | ||
|
|
40406b96a2 | ||
|
|
832810cacd | ||
|
|
2c618cb08b | ||
|
|
95197098af | ||
|
|
06893bf3c3 | ||
|
|
861d76f7b7 | ||
|
|
33f780fb76 | ||
|
|
bacb70ea7e | ||
|
|
2aa876e526 | ||
|
|
2240acb272 | ||
|
|
752d4ae79e | ||
|
|
32c8bf296a | ||
|
|
ba994cb5a0 | ||
|
|
f4b27afb8d | ||
|
|
d23bd85cc6 | ||
|
|
a1f2e9336d | ||
|
|
d13b4d1a6b | ||
|
|
161b5eed10 | ||
|
|
41e922544c | ||
|
|
669a9fad69 | ||
|
|
3c5a8b32be | ||
|
|
d0f858681d | ||
|
|
bb852c5f80 | ||
|
|
6c73362ff2 | ||
|
|
e307c12fc8 | ||
|
|
b7e84f3623 | ||
|
|
8e78c1b4e4 | ||
|
|
64bdda6f90 | ||
|
|
a6a59ddbd7 | ||
|
|
b715e917fe | ||
|
|
78c5392675 | ||
|
|
4e02f8ad45 | ||
|
|
ffe4f7dba8 | ||
|
|
e31b601cef | ||
|
|
7d3d543f24 | ||
|
|
8d372fcdbc | ||
|
|
d00e3479cb | ||
|
|
d7eca52473 | ||
|
|
cf767165e6 | ||
|
|
3bc774bf55 | ||
|
|
0d9b5451fe | ||
|
|
0fe657d0fc | ||
|
|
ce545724bd | ||
|
|
2b08468581 | ||
|
|
0e320535a8 | ||
|
|
746c5e825e | ||
|
|
6a55a8d6ea | ||
|
|
8a48797e00 | ||
|
|
fad71e936c | ||
|
|
61f3e64751 | ||
|
|
fc6ba4641a | ||
|
|
e7d2aa3599 | ||
|
|
aa1cef9e70 | ||
|
|
3b77cfb8d6 | ||
|
|
fb8277f03e | ||
|
|
fa7afd4237 | ||
|
|
2b19ef0c2d | ||
|
|
f0d5cdecb7 | ||
|
|
be58497ba9 | ||
|
|
db1883536e | ||
|
|
c018bfc044 | ||
|
|
379697a02d | ||
|
|
c62f96c96e | ||
|
|
e1e0696e3c | ||
|
|
98ea1aa548 | ||
|
|
553d1c062e | ||
|
|
2ec343a95b | ||
|
|
2ddf79ed3f | ||
|
|
ef98eb707c | ||
|
|
fd1e8f6aa8 | ||
|
|
738c6af797 | ||
|
|
4c83dff96c | ||
|
|
531426b0a6 | ||
|
|
4d9549d2cc | ||
|
|
7980b726f6 | ||
|
|
3f80a4dfa2 | ||
|
|
e1a96e0f23 | ||
|
|
49be0b243b | ||
|
|
9f535aeba7 | ||
|
|
bd71adebe0 | ||
|
|
7367972d56 | ||
|
|
f345155a94 | ||
|
|
8f4ce9c13b | ||
|
|
7a4d59d50c | ||
|
|
a7e2f08efc | ||
|
|
30a2eb5ee7 | ||
|
|
7b40432195 | ||
|
|
699f6ba97d | ||
|
|
9486d0c829 | ||
|
|
1f0370c422 | ||
|
|
10fb06eb97 | ||
|
|
a11247d359 | ||
|
|
61f70b3e6a | ||
|
|
75bbb6d66a | ||
|
|
11ce057310 | ||
|
|
42f6353ec8 | ||
|
|
3bd7bc75a8 | ||
|
|
d2a99b0221 | ||
|
|
4abb4009e6 | ||
|
|
f8c23ed6da | ||
|
|
e96da632b2 | ||
|
|
cef8094709 | ||
|
|
353386b4da | ||
|
|
4f56506dac | ||
|
|
722fbdb474 | ||
|
|
ae689a8615 | ||
|
|
52baa69b70 | ||
|
|
b68e622695 | ||
|
|
005a936a61 | ||
|
|
bad9d0a3ef | ||
|
|
fff6870002 | ||
|
|
2ee6c5effc | ||
|
|
d3c854a9da | ||
|
|
f39d985877 | ||
|
|
500e3853a5 | ||
|
|
1ee6b5f974 | ||
|
|
d76793e74d | ||
|
|
d17c2f8ebd | ||
|
|
a5ce7e9c9c | ||
|
|
f617504cd6 | ||
|
|
924bf1217f | ||
|
|
0c51e77d6c | ||
|
|
8130de23ff | ||
|
|
a0b2f80508 | ||
|
|
e1a74d0652 | ||
|
|
a5523967f6 | ||
|
|
c3895d65e1 | ||
|
|
8ed88d42c0 | ||
|
|
043b80171e | ||
|
|
ba761098be | ||
|
|
7b9a4f457e | ||
|
|
3e137ac6b4 | ||
|
|
a0196a01b3 | ||
|
|
aab591cb12 | ||
|
|
07a9d4645f | ||
|
|
bab12d7894 | ||
|
|
25af7a211a | ||
|
|
86553464cc | ||
|
|
35a4d46dc5 | ||
|
|
159f39dd18 | ||
|
|
50d8577298 | ||
|
|
af0d4d26bb | ||
|
|
435847081d | ||
|
|
b8f4236c1a | ||
|
|
ce91812ce5 | ||
|
|
04aa921099 | ||
|
|
08fbb91667 | ||
|
|
5be12543e5 | ||
|
|
1e6c85eb01 | ||
|
|
a3125605f3 | ||
|
|
3ed86acb46 | ||
|
|
8fb8eee5ef | ||
|
|
4a23e75dee | ||
|
|
64923e47a2 | ||
|
|
0ce3deea86 | ||
|
|
56ac0eefb8 | ||
|
|
fdb9b10353 | ||
|
|
44d90c42ed | ||
|
|
9271c67d7f | ||
|
|
1f709c6faa | ||
|
|
741718ede6 | ||
|
|
c46fbfbde3 | ||
|
|
b633fc58d2 | ||
|
|
8183ce03c5 | ||
|
|
129d659628 | ||
|
|
db0de68987 | ||
|
|
09d6bc8169 | ||
|
|
631e436079 | ||
|
|
dc32d7246c | ||
|
|
9cf8a584a0 | ||
|
|
cd9276f4d2 | ||
|
|
87b34122c2 | ||
|
|
0e0f04c5ad | ||
|
|
dbd3ab6064 | ||
|
|
e8f88d8a13 | ||
|
|
c1f5d474b1 | ||
|
|
c04b83bcaa | ||
|
|
f8b5a48076 | ||
|
|
6fd84fcec3 | ||
|
|
6687a20835 | ||
|
|
68a508e26a | ||
|
|
05bf40fa82 | ||
|
|
49f29fa2af | ||
|
|
89e0d3ae7e | ||
|
|
bbcab8d338 | ||
|
|
76a26b634d | ||
|
|
820e3033ea | ||
|
|
2d4f337f03 | ||
|
|
fb4ad848d6 | ||
|
|
31ecc281a8 | ||
|
|
745cb85f95 | ||
|
|
aac4a33c92 | ||
|
|
ff63cea108 | ||
|
|
65f688dbc2 | ||
|
|
3f6f4ccd12 | ||
|
|
3838848b03 | ||
|
|
915b227ede | ||
|
|
206098a216 | ||
|
|
a0fe80604e | ||
|
|
d76253ea04 | ||
|
|
4a1d45f458 | ||
|
|
769e48706d | ||
|
|
3374db1ac8 | ||
|
|
c7fa5c5608 | ||
|
|
9602f42a71 | ||
|
|
3bfc966e10 | ||
|
|
d4b8caeedc | ||
|
|
fcaf3a8e85 | ||
|
|
62295e7a23 | ||
|
|
5e5b00e487 | ||
|
|
36d0db43b2 | ||
|
|
03460c592a | ||
|
|
e658629482 | ||
|
|
cbfd7fa5f4 | ||
|
|
36d6d115d4 | ||
|
|
d4c0207545 | ||
|
|
4258e031c6 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
github: helloanoop
|
||||
2
.github/workflows/unit-tests.yml
vendored
2
.github/workflows/unit-tests.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Test Package bruno-cli
|
||||
run: npm run test --workspace=packages/bruno-cli
|
||||
- name: Test Package bruno-electron
|
||||
run: npm run test --workspace=packages/bruno-electron
|
||||
run: npm run test --workspace=packages/bruno-electron --passWithNoTests
|
||||
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,6 +6,8 @@ yarn.lock
|
||||
pnpm-lock.yaml
|
||||
.pnp
|
||||
.pnp.js
|
||||
bun.lockb
|
||||
bun.lock
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
**English** | [Українська](docs/contributing/contributing_ua.md) | [Русский](docs/contributing/contributing_ru.md) | [Türkçe](docs/contributing/contributing_tr.md) | [Deutsch](docs/contributing/contributing_de.md) | [Français](docs/contributing/contributing_fr.md)
|
||||
|
||||
**English** | [Українська](docs/contributing/contributing_ua.md) | [Русский](docs/contributing/contributing_ru.md) | [Türkçe](docs/contributing/contributing_tr.md) | [Deutsch](docs/contributing/contributing_de.md) | [Français](docs/contributing/contributing_fr.md) | [Português (BR)](docs/contributing/contributing_pt_br.md) | [বাংলা](docs/contributing/contributing_bn.md) | [Español](docs/contributing/contributing_es.md) | [Română](docs/contributing/contributing_ro.md) | [Polski](docs/contributing/contributing_pl.md)
|
||||
| [简体中文](docs/contributing/contributing_cn.md)
|
||||
## Let's make bruno better, together !!
|
||||
|
||||
We are happy that you are looking to improve bruno. Below are the guidelines to get started bringing up bruno on your computer.
|
||||
|
||||
87
docs/contributing/contributing_bn.md
Normal file
87
docs/contributing/contributing_bn.md
Normal file
@@ -0,0 +1,87 @@
|
||||
[English](/contributing.md) | [Українська](/contributing_ua.md) | [Русский](/contributing_ru.md) | [Türkçe](/contributing_tr.md) | [Deutsch](docs/contributing/contributing_de.md) | [Français](/contributing_fr.md) | **বাংলা**
|
||||
|
||||
## আসুন ব্রুনোকে আরও ভালো করি, একসাথে!!
|
||||
|
||||
আমরা খুশি যে আপনি ব্রুনোর উন্নতি করতে চাইছেন। নীচে আপনার কম্পিউটারে ব্রুনো ইনষ্টল করার নির্দেশিকা রয়েছে৷।
|
||||
|
||||
### Technology Stack (প্রযুক্তি স্ট্যাক)
|
||||
|
||||
ব্রুনো Next.js এবং React ব্যবহার করে নির্মিত। এছাড়াও আমরা একটি ডেস্কটপ সংস্করণ পাঠাতে ইলেক্ট্রন ব্যবহার করি (যা স্থানীয় সংগ্রহ সমর্থন করে)
|
||||
|
||||
নিম্ন লিখিত লাইব্রেরি আমরা ব্যবহার করি -
|
||||
|
||||
- CSS - Tailwind
|
||||
- Code Editors - Codemirror
|
||||
- State Management - Redux
|
||||
- Icons - Tabler Icons
|
||||
- Forms - formik
|
||||
- Schema Validation - Yup
|
||||
- Request Client - axios
|
||||
- Filesystem Watcher - chokidar
|
||||
|
||||
### Dependencies (নির্ভরতা)
|
||||
|
||||
আপনার প্রয়োজন হবে [নোড v18.x বা সর্বশেষ LTS সংস্করণ](https://nodejs.org/en/) এবং npm 8.x। আমরা প্রকল্পে npm ওয়ার্কস্পেস ব্যবহার করি ।
|
||||
|
||||
## Development
|
||||
|
||||
ব্রুনো একটি ডেস্কটপ অ্যাপ হিসেবে তৈরি করা হচ্ছে। আপনাকে একটি টার্মিনালে Next.js অ্যাপটি চালিয়ে অ্যাপটি লোড করতে হবে এবং তারপরে অন্য টার্মিনালে ইলেক্ট্রন অ্যাপটি চালাতে হবে।
|
||||
|
||||
### Dependencies (নির্ভরতা)
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# nodejs 18 সংস্করণ ব্যবহার করুন
|
||||
nvm use
|
||||
|
||||
# নির্ভরতা ইনস্টল করুন
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# গ্রাফকিউএল ডক্স তৈরি করুন
|
||||
npm run build:graphql-docs
|
||||
|
||||
# ব্রুনো কোয়েরি তৈরি করুন
|
||||
npm run build:bruno-query
|
||||
|
||||
# NextJs অ্যাপ চালান (টার্মিনাল 1)
|
||||
npm run dev:web
|
||||
|
||||
# ইলেক্ট্রন অ্যাপ চালান (টার্মিনাল 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Troubleshooting (সমস্যা সমাধান)
|
||||
|
||||
আপনি যখন 'npm install' চালান তখন আপনি একটি 'অসমর্থিত প্ল্যাটফর্ম' ত্রুটির সম্মুখীন হতে পারেন৷ এটি ঠিক করতে, আপনাকে `node_modules` এবং `package-lock.json` মুছে ফেলতে হবে এবং `npm install` চালাতে হবে। এটি অ্যাপটি চালানোর জন্য প্রয়োজনীয় সমস্ত প্যাকেজ ইনস্টল করবে যাতে এই ত্রুটি ঠিক হয়ে যেতে পারে ।
|
||||
|
||||
```shell
|
||||
# সাব-ডিরেক্টরিতে নোড_মডিউল মুছুন
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# সাব-ডিরেক্টরিতে প্যাকেজ-লক মুছুন
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Testing (পরীক্ষা)
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Raising Pull Request (পুল অনুরোধ উত্থাপন)
|
||||
|
||||
- অনুগ্রহ করে PR এর আকার ছোট রাখুন এবং একটি বিষয়ে ফোকাস করুন।
|
||||
- অনুগ্রহ করে শাখা তৈরির বিন্যাস অনুসরণ করুন।
|
||||
- বৈশিষ্ট্য/[ফিচারের নাম]: এই শাখায় একটি নির্দিষ্ট বৈশিষ্ট্যের জন্য পরিবর্তন থাকতে হবে।
|
||||
- উদাহরণ: বৈশিষ্ট্য/ডার্ক-মোড।
|
||||
- বাগফিক্স/[বাগ নাম]: এই শাখায় একটি নির্দিষ্ট বাগ-এর জন্য শুধুমাত্র বাগ ফিক্স থাকা উচিত।
|
||||
- উদাহরণ বাগফিক্স/বাগ-1।
|
||||
90
docs/contributing/contributing_cn.md
Normal file
90
docs/contributing/contributing_cn.md
Normal file
@@ -0,0 +1,90 @@
|
||||
[English](/contributing.md) | [Українська](./contributing_ua.md) | [Русский](./contributing_ru.md) | [Türkçe](./contributing_tr.md) | [Deutsch](./contributing_de.md) | [Français](./contributing_fr.md) | [Português (BR)](./contributing_pt_br.md) | [বাংলা](./contributing_bn.md) | [Español](./contributing_es.md) | [Română](./contributing_ro.md) | [Polski](./contributing_pl.md) | **简体中文**
|
||||
|
||||
## 让我们一起改进 Bruno!
|
||||
|
||||
很高兴看到您考虑改进 Bruno。以下是获取 Bruno 并在您的电脑上运行它的规则和指南。
|
||||
|
||||
### 使用的技术
|
||||
|
||||
Bruno 基于 NextJs 和 React 构建。我们使用 Electron 来封装桌面版本。
|
||||
|
||||
我们使用的库包括:
|
||||
|
||||
- CSS - Tailwind
|
||||
- 代码编辑器 - Codemirror
|
||||
- 状态管理 - Redux
|
||||
- 图标 - Tabler Icons
|
||||
- 表单 - formik
|
||||
- 模式验证 - Yup
|
||||
- 请求客户端 - axios
|
||||
- 文件系统监视器 - chokidar
|
||||
|
||||
### 依赖项
|
||||
|
||||
您需要 [Node v18.x 或最新的 LTS 版本](https://nodejs.org/en/) 和 npm 8.x。我们在这个项目中也使用 npm 工作区(_npm workspaces_)。
|
||||
|
||||
|
||||
## 开发
|
||||
|
||||
Bruno 是作为一个 _client lourd(重客户端)_ 应用程序开发的。您需要在一个终端中启动 nextjs 来加载应用程序,然后在另一个终端中启动 Electron 应用程序。
|
||||
|
||||
### 依赖项
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### 本地开发
|
||||
|
||||
```bash
|
||||
# 使用 node 版本 18
|
||||
nvm use
|
||||
|
||||
# 安装依赖项
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# 构建 graphql 文档
|
||||
npm run build:graphql-docs
|
||||
|
||||
# 构建 bruno 查询
|
||||
npm run build:bruno-query
|
||||
|
||||
# 启动 next(终端 1)
|
||||
npm run dev:web
|
||||
|
||||
# 启动重客户端(终端 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### 故障排除
|
||||
|
||||
在运行 npm install 时,您可能会遇到 Unsupported platform 错误。为了解决这个问题,请删除 node_modules 目录和 package-lock.json 文件,然后再次运行 npm install。这应该会安装运行应用程序所需的所有包。
|
||||
|
||||
```shell
|
||||
# 删除子目录中的 node_modules 目录
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# 删除子目录中的 package-lock.json 文件
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
|
||||
### 测试
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
|
||||
### 提交 Pull Request
|
||||
|
||||
- 请保持 PR 精简并专注于单一目标
|
||||
- 请遵循分支命名格式:
|
||||
- feature/[feature name]:该分支应包含特定功能
|
||||
- 例如:feature/dark-mode
|
||||
- bugfix/[bug name]:该分支应仅包含特定 bug 的修复
|
||||
- 例如:bugfix/bug-1
|
||||
@@ -1,3 +1,5 @@
|
||||
[English](/contributing.md) | [Українська](/contributing_ua.md) | [Русский](/contributing_ru.md) | [Türkçe](/contributing_tr.md) | **Deutsch** | [Français](/contributing_fr.md) | [বাংলা](docs/contributing/contributing_bn.md)
|
||||
|
||||
## Lass uns Bruno noch besser machen, gemeinsam !!
|
||||
|
||||
Ich freue mich, dass Du Bruno verbessern möchtest. Hier findest Du eine Anleitung, mit der Du Bruno auf Deinem Computer einrichten kannst.
|
||||
|
||||
85
docs/contributing/contributing_es.md
Normal file
85
docs/contributing/contributing_es.md
Normal file
@@ -0,0 +1,85 @@
|
||||
## ¡Juntos, hagamos a Bruno mejor!
|
||||
|
||||
Estamos encantados de que quieras ayudar a mejorar Bruno. A continuación encontrarás las instrucciones para empezar a trabajar con Bruno en tu computadora.
|
||||
|
||||
### Tecnologías utilizadas
|
||||
|
||||
Bruno está construido con NextJs y React. También usamos electron para distribuir una versión de escritorio (que soporta colecciones locales).
|
||||
|
||||
Librerías que utilizamos:
|
||||
|
||||
- CSS - Tailwind
|
||||
- Editores de código - Codemirror
|
||||
- Manejo del estado - Redux
|
||||
- Íconos - Tabler Icons
|
||||
- Formularios - formik
|
||||
- Validación de esquemas - Yup
|
||||
- Cliente de peticiones - axios
|
||||
- Monitor del sistema de archivos - chokidar
|
||||
|
||||
### Dependencias
|
||||
|
||||
Necesitarás [Node v18.x o la última versión LTS](https://nodejs.org/es) y npm 8.x. Ten en cuenta que utilizamos espacios de trabajo de npm en el proyecto.
|
||||
|
||||
## Desarrollo
|
||||
|
||||
Bruno está siendo desarrollado como una aplicación de escritorio. Para ejecutarlo, primero debes ejecutar la aplicación de nextjs en una terminal y luego ejecutar la aplicación de electron en otra terminal.
|
||||
|
||||
### Dependencias
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Desarrollo local
|
||||
|
||||
```bash
|
||||
# Utiliza la versión 18 de nodejs
|
||||
nvm use
|
||||
|
||||
# Instala las dependencias
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# Construye la documentación de graphql
|
||||
npm run build:graphql-docs
|
||||
|
||||
# Construye bruno-query
|
||||
npm run build:bruno-query
|
||||
|
||||
# Ejecuta la aplicación de nextjs (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# Ejecuta la aplicación de electron (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Solución de problemas
|
||||
|
||||
Es posible que encuentres un error de `Unsupported platform` cuando ejecutes `npm install`. Para solucionarlo, debes eliminar la carpeta `node_modules` y el archivo `package-lock.json`, luego, ejecuta `npm install`. Lo anterior debería instalar todos los paquetes necesarios para ejecutar la aplicación.
|
||||
|
||||
```shell
|
||||
# Elimina la carpeta node_modules en los subdirectorios
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# Elimina el archivo package-lock en los subdirectorios
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Pruebas
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Crea un Pull Request
|
||||
|
||||
- Por favor, mantén los Pull Request pequeños y enfocados en una sola cosa.
|
||||
- Por favor, sigue el siguiente formato para la creación de ramas:
|
||||
- feature/[nombre de la funcionalidad]: Esta rama debe contener los cambios para una funcionalidad específica.
|
||||
- Ejemplo: feature/dark-mode
|
||||
- bugfix/[nombre del error]: Esta rama debe contener solo correcciones de errores para un error específico.
|
||||
- Ejemplo: bugfix/bug-1
|
||||
@@ -1,12 +1,14 @@
|
||||
[English](/contributing.md) | [Українська](docs/contributing/contributing_ua.md) | [Русский](docs/contributing/contributing_ru.md) | [Türkçe](docs/contributing/contributing_tr.md) | [Deutsch](docs/contributing/contributing_de.md) | **Français** | [Português (BR)](docs/contributing/contributing_pt_br.md) | [বাংলা](docs/contributing/contributing_bn.md) | [Español](docs/contributing/contributing_es.md) | [Română](docs/contributing/contributing_ro.md) | [Polski](docs/contributing/contributing_pl.md)
|
||||
|
||||
## Ensemble, améliorons Bruno !
|
||||
|
||||
Je suis content de voir que vous envisagez améliorer Bruno. Ci-dessous, vous trouverez les règles et guides pour récupérer Bruno sur votre ordinateur.
|
||||
Je suis content de voir que vous envisagez d'améliorer Bruno. Vous trouverez ci-dessous les règles et guides pour récupérer Bruno sur votre ordinateur.
|
||||
|
||||
### Technologies utilisées
|
||||
|
||||
Bruno est construit en utilisant NextJs et React. Nous utilisons aussi Electron pour embarquer la version ordinateur (qui permet les collections locales).
|
||||
Bruno est basé sur NextJs et React. Nous utilisons aussi Electron pour embarquer la version ordinateur (ce qui permet les collections locales).
|
||||
|
||||
Les bibliothèques que nous utilisons :
|
||||
Les librairies que nous utilisons :
|
||||
|
||||
- CSS - Tailwind
|
||||
- Code Editors - Codemirror
|
||||
@@ -21,22 +23,10 @@ Les bibliothèques que nous utilisons :
|
||||
|
||||
Vous aurez besoin de [Node v18.x ou la dernière version LTS](https://nodejs.org/en/) et npm 8.x. Nous utilisons aussi les espaces de travail npm (_npm workspaces_) dans ce projet.
|
||||
|
||||
### Commençons à coder
|
||||
|
||||
Veuillez vous référez à la [documentation de développement](docs/development_fr.md) pour les instructions de démarrage de l'environnement de développement local.
|
||||
|
||||
### Ouvrir une Pull Request
|
||||
|
||||
- Merci de conserver les PR petites et focalisées sur un seul objectif
|
||||
- Merci de suivre le format de nom des branches
|
||||
- feature/[feature name]: Cette branche devrait contenir une fonctionnalité spécifique
|
||||
- Exemple: feature/dark-mode
|
||||
- bugfix/[bug name]: Cette branche devrait contenir seulement une solution pour pour une bogue spécifique
|
||||
- Exemple: bugfix/bug-1
|
||||
|
||||
## Développement
|
||||
|
||||
Bruno est développé comme une application de _lourde_. Vous devez charger l'application en démarrant nextjs dans un terminal, puis démarre l'application Electron dans un autre terminal.
|
||||
Bruno est développé comme une application _client lourd_. Vous devrez charger l'application en démarrant nextjs dans un premier terminal, puis démarre l'application Electron dans un second.
|
||||
|
||||
|
||||
### Dépendances
|
||||
|
||||
@@ -45,39 +35,40 @@ Bruno est développé comme une application de _lourde_. Vous devez charger l'ap
|
||||
### Développement local
|
||||
|
||||
```bash
|
||||
# use nodejs 18 version
|
||||
# utiliser node en version 18
|
||||
nvm use
|
||||
|
||||
# install deps
|
||||
# installation des dépendances
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# build graphql docs
|
||||
# construction des docs graphql
|
||||
npm run build:graphql-docs
|
||||
|
||||
# build bruno query
|
||||
# construction de bruno query
|
||||
npm run build:bruno-query
|
||||
|
||||
# run next app (terminal 1)
|
||||
# démarrage de next (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# run electron app (terminal 2)
|
||||
# démarrage du client lourd (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Dépannage
|
||||
|
||||
Vous pourriez rencontrer une error `Unsupported platform` pendant le lancement de `npm install`. Pour résoudre cela, veuillez supprimer le répertoire `node_modules`, le fichier `package-lock.json` et lancer à nouveau `npm install`. Cela devrait isntaller tous les paquets nécessaires pour lancer l'application.
|
||||
Vous pourriez rencontrer une erreur `Unsupported platform` durant le lancement de `npm install`. Pour résoudre cela, veuillez supprimer le répertoire `node_modules` ainsi que le fichier `package-lock.json` et lancez à nouveau `npm install`. Cela devrait isntaller tous les paquets nécessaires pour lancer l'application.
|
||||
|
||||
```shell
|
||||
# Delete node_modules in sub-directories
|
||||
# Efface les répertoires node_modules dans les sous-répertoires
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# Delete package-lock in sub-directories
|
||||
# Efface les fichiers package-lock.json dans les sous-répertoires
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
```bash
|
||||
@@ -87,3 +78,13 @@ npm test --workspace=packages/bruno-schema
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
|
||||
### Ouvrir une Pull Request
|
||||
|
||||
- Merci de conserver les PR minimes et focalisées sur un seul objectif
|
||||
- Merci de suivre le format de nom des branches :
|
||||
- feature/[feature name]: Cette branche doit contenir une fonctionnalité spécifique
|
||||
- Exemple: feature/dark-mode
|
||||
- bugfix/[bug name]: Cette branche doit contenir seulement une solution pour un bug spécifique
|
||||
- Exemple: bugfix/bug-1
|
||||
89
docs/contributing/contributing_it.md
Normal file
89
docs/contributing/contributing_it.md
Normal file
@@ -0,0 +1,89 @@
|
||||
## Insieme, miglioriamo Bruno!
|
||||
|
||||
Sono felice di vedere che hai intenzione di migliorare Bruno. Di seguito, troverai le regole e le guide per ripristinare Bruno sul tuo computer.
|
||||
|
||||
### Tecnologie utilizzate
|
||||
|
||||
Bruno è costruito utilizzando Next.js e React. Utilizziamo anche Electron per incorporare la versione desktop (che consente raccolte locali).
|
||||
|
||||
Le librerie che utilizziamo sono:
|
||||
|
||||
- CSS - Tailwind
|
||||
- Code Editors - Codemirror
|
||||
- State Management - Redux
|
||||
- Icons - Tabler Icons
|
||||
- Forms - formik
|
||||
- Schema Validation - Yup
|
||||
- Request Client - axios
|
||||
- Filesystem Watcher - chokidar
|
||||
|
||||
### Dependences
|
||||
|
||||
Hai bisogno di [Node v18.x o dell'ultima versione LTS](https://nodejs.org/en/) di npm 8.x. Utilizziamo gli spazi di lavoro npm (_npm workspaces_) in questo progetto.
|
||||
|
||||
### Iniziamo a codificare
|
||||
|
||||
Si prega di fare riferimento alla [documentazione di sviluppo](docs/development_it.md) per le istruzioni su come avviare l'ambiente di sviluppo locale.
|
||||
|
||||
### Aprire una richiesta di pull (Pull Request)
|
||||
|
||||
- Si prega di mantenere le Pull Request (PR) brevi e concentrate su un singolo obiettivo.
|
||||
- Si prega di seguire il formato di denominazione dei rami.
|
||||
- feature/[feature name]: Questo ramo dovrebbe contenere una specifica funzionalità.
|
||||
- Esempio: feature/dark-mode
|
||||
- bugfix/[bug name]: Questo ramo dovrebbe contenere solo una soluzione per un bug specifico.
|
||||
- Esempio: bugfix/bug-1
|
||||
|
||||
## Sviluppo
|
||||
|
||||
Bruno è sviluppato come un'applicazione "heavy". È necessario caricare l'applicazione avviando Next.js in una finestra del terminale e quindi avviare l'applicazione Electron in un altro terminale.
|
||||
|
||||
### Sviluppo
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Sviluppo locale
|
||||
|
||||
```bash
|
||||
# use nodejs 18 version
|
||||
nvm use
|
||||
|
||||
# install deps
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# build graphql docs
|
||||
npm run build:graphql-docs
|
||||
|
||||
# build bruno query
|
||||
npm run build:bruno-query
|
||||
|
||||
# run next app (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# run electron app (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Risoluzione dei problemi
|
||||
|
||||
Potresti trovare un errore `Unsupported platform` durante l'esecuzione di `npm install`. Per risolvere questo problema, ti preghiamo di eliminare la cartella `node_modules`, il file `package-lock.json` e di seguito nuovamente `npm install`. Qeusto dovrebbe installare tutti i pacchetti necessari per avviare l'applicazione.
|
||||
|
||||
```shell
|
||||
# delete node_modules in sub-directories
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# delete package-lock in sub-directories
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
88
docs/contributing/contributing_pl.md
Normal file
88
docs/contributing/contributing_pl.md
Normal file
@@ -0,0 +1,88 @@
|
||||
[English](/contributing.md) | [Українська](docs/contributing/contributing_ua.md) | [Русский](docs/contributing/contributing_ru.md) | [Türkçe](docs/contributing/contributing_tr.md) | [Deutsch](docs/contributing/contributing_de.md) | [Français](docs/contributing/contributing_fr.md) | [Português (BR)](docs/contributing/contributing_pt_br.md) | [বাংলা](docs/contributing/contributing_bn.md) | [Español](docs/contributing/contributing_es.md) | [Română](docs/contributing/contributing_ro.md) | **Polski**
|
||||
|
||||
## Wspólnie uczynijmy Bruno lepszym !!
|
||||
|
||||
Cieszymy się, że chcesz udoskonalić Bruno. Poniżej znajdziesz wskazówki, jak rozpocząć pracę z Bruno na Twoim komputerze.
|
||||
|
||||
### Stos Technologiczny
|
||||
|
||||
Bruno jest zbudowane przy użyciu Next.js i React. Używamy również electron do stworzenia wersji desktopowej (która obsługuje lokalne kolekcje)
|
||||
|
||||
Biblioteki, których używamy
|
||||
|
||||
- CSS - Tailwind
|
||||
- Edytory Kodu - Codemirror
|
||||
- Zarządzanie Stanem - Redux
|
||||
- Ikony - Tabler Icons
|
||||
- Formularze - formik
|
||||
- Walidacja Schematu - Yup
|
||||
- Klient Zapytań - axios
|
||||
- Obserwator Systemu Plików - chokidar
|
||||
|
||||
### Zależności
|
||||
|
||||
Będziesz potrzebować [Node v18.x lub najnowszej wersji LTS](https://nodejs.org/en/) oraz npm 8.x. W projekcie używamy npm workspaces
|
||||
|
||||
## Rozwój
|
||||
|
||||
Bruno jest rozwijane jako aplikacja desktopowa. Musisz załadować aplikację, uruchamiając aplikację Next.js w jednym terminalu, a następnie uruchomić aplikację electron w innym terminalu.
|
||||
|
||||
### Zależności
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Lokalny Rozwój
|
||||
|
||||
```bash
|
||||
# użyj wersji nodejs 18
|
||||
nvm use
|
||||
|
||||
# zainstaluj zależności
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# zbuduj dokumentację graphql
|
||||
npm run build:graphql-docs
|
||||
|
||||
# zbuduj zapytanie bruno
|
||||
npm run build:bruno-query
|
||||
|
||||
# uruchom aplikację next (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# uruchom aplikację electron (terminal 2)
|
||||
npm run dev:electron
|
||||
|
||||
|
||||
### Rozwiązywanie Problemów
|
||||
|
||||
Możesz napotkać błąd `Unsupported platform` podczas uruchamiania `npm install`. Aby to naprawić, będziesz musiał usunąć `node_modules` i `package-lock.json`, a następnie uruchomić `npm install`. Powinno to zainstalować wszystkie niezbędne pakiety potrzebne do uruchomienia aplikacji.
|
||||
|
||||
```shell
|
||||
# Usuń node_modules w podkatalogach
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# Usuń package-lock w podkatalogach
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
|
||||
```
|
||||
|
||||
### Testowanie
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Tworzenie Pull Request
|
||||
|
||||
- Prosimy, aby PR były małe i skoncentrowane na jednej rzeczy
|
||||
- Prosimy przestrzegać formatu tworzenia gałęzi
|
||||
- feature/[nazwa funkcji]: Ta gałąź powinna zawierać zmiany dotyczące konkretnej funkcji
|
||||
- Przykład: feature/dark-mode
|
||||
- bugfix/[nazwa błędu]: Ta gałąź powinna zawierać tylko poprawki dla konkretnego błędu
|
||||
- Przykład bugfix/bug-1
|
||||
85
docs/contributing/contributing_pt_br.md
Normal file
85
docs/contributing/contributing_pt_br.md
Normal file
@@ -0,0 +1,85 @@
|
||||
## Vamos tornar o Bruno melhor, juntos!!
|
||||
|
||||
Estamos felizes que você queira ajudar a melhorar o Bruno. Abaixo estão as diretrizes e orientações para começar a executar o Bruno no seu computador.
|
||||
|
||||
### Stack de Tecnologias
|
||||
|
||||
O Bruno é construído usando Next.js e React. Também usamos o Electron para disponibilizar uma versão para desktop (que suporta coleções locais).
|
||||
|
||||
Bibliotecas que utilizamos:
|
||||
|
||||
- CSS - Tailwind
|
||||
- Editor de Código - Codemirror
|
||||
- Gerenciador de Estado - Redux
|
||||
- Ícones - Tabler Icons
|
||||
- Formulários - formik
|
||||
- Validador de Schema - Yup
|
||||
- Cliente de Requisições - axios
|
||||
- Monitor de Arquivos - chokidar
|
||||
|
||||
### Dependências
|
||||
|
||||
Você precisará do [Node v18.x (ou da versão LTS mais recente)](https://nodejs.org/en/) e do npm na versão 8.x. Nós utilizamos npm workspaces no projeto.
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
Bruno está sendo desenvolvido como um aplicativo de desktop. Você precisa carregar o programa executando o aplicativo Next.js em um terminal e, em seguida, executar o aplicativo Electron em outro terminal.
|
||||
|
||||
### Dependências
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Desenvolvimento Local
|
||||
|
||||
```bash
|
||||
# use nodejs 18 version
|
||||
nvm use
|
||||
|
||||
# install deps
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# build graphql docs
|
||||
npm run build:graphql-docs
|
||||
|
||||
# build bruno query
|
||||
npm run build:bruno-query
|
||||
|
||||
# run next app (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# run electron app (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
Você pode se deparar com o erro `Unsupported platform` ao executar o comando `npm install`. Para corrigir isso, você precisará excluir a pasta `node_modules` e o arquivo `package-lock.json` e, em seguida, executar o comando `npm install` novamente. Isso deve instalar todos os pacotes necessários para executar o aplicativo.
|
||||
|
||||
```shell
|
||||
# delete node_modules in sub-directories
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# delete package-lock in sub-directories
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Testando
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Envio de Pull Request
|
||||
|
||||
- Por favor, mantenha os PRs pequenos e focados em uma única coisa.
|
||||
- Siga o formato de criação de branches.
|
||||
- feature/[nome da funcionalidade]: Esta branch deve conter alterações para uma funcionalidade específica.
|
||||
- Exemplo: feature/dark-mode
|
||||
- bugfix/[nome do bug]: Esta branch deve conter apenas correções para um bug específico.
|
||||
- Exemplo: bugfix/bug-1
|
||||
81
docs/contributing/contributing_ro.md
Normal file
81
docs/contributing/contributing_ro.md
Normal file
@@ -0,0 +1,81 @@
|
||||
[English](/contributing.md) | [Українська](/docs/contributing/contributing_ua.md) | [Русский](/docs/contributing/contributing_ru.md) | [Türkçe](/docs/contributing/contributing_tr.md) | [Deutsch](/docs/contributing/contributing_de.md) | [Français](/docs/contributing/contributing_fr.md) | [Português (BR)](/docs/contributing/contributing_pt_br.md) | [বাংলা](/docs/contributing/contributing_bn.md) | [Español](/docs/contributing/contributing_es.md) | [Italiano](/docs/contributing/contributing_it.md) | **Română**
|
||||
|
||||
## Haideţi să îmbunătățim Bruno, împreună!!
|
||||
|
||||
Ne bucurăm că doriți să îmbunătățiți bruno. Mai jos sunt instrucțiunile pentru ca să porniți bruno pe calculatorul dvs.
|
||||
|
||||
### Stack-ul tehnologic
|
||||
|
||||
Bruno este construit cu Next.js și React. De asemenea, folosim electron pentru a livra o versiune desktop (care poate folosi colecții locale)
|
||||
|
||||
Bibliotecile pe care le folosim
|
||||
|
||||
- CSS - Tailwind
|
||||
- Editori de cod - Codemirror
|
||||
- Management de condiție - Redux
|
||||
- Icoane - Tabler Icons
|
||||
- Formulare - formik
|
||||
- Validarea schemelor - Yup
|
||||
- Cererile client - axios
|
||||
- Observatorul sistemului de fișiere - chokidar
|
||||
|
||||
### Dependențele
|
||||
|
||||
Veți avea nevoie de [Node v18.x sau cea mai recentă versiune LTS](https://nodejs.org/en/) și npm 8.x. Noi folosim spații de lucru npm în proiect
|
||||
|
||||
## Dezvoltarea
|
||||
|
||||
Bruno este dezvoltat ca o aplicație desktop. Ca să porniți aplicatia trebuie să rulați aplicația Next.js într-un terminal și apoi să rulați aplicația electron într-un alt terminal.
|
||||
|
||||
```shell
|
||||
# folosiți nodejs versiunea 18
|
||||
nvm use
|
||||
|
||||
# instalați dependențele
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# construiți documente graphql
|
||||
npm run build:graphql-docs
|
||||
|
||||
# construiți bruno query
|
||||
npm run build:bruno-query
|
||||
|
||||
# rulați aplicația next (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# rulați aplicația electron (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Depanare
|
||||
|
||||
Este posibil să întâmpinați o eroare `Unsupported platform` când rulați „npm install”. Pentru a remedia acest lucru, va trebui să ștergeți `node_modules` și `package-lock.json` și să rulați `npm install`. Aceasta ar trebui să instaleze toate pachetele necesare pentru a rula aplicația.
|
||||
|
||||
```shell
|
||||
# Ștergeți node_modules din subdirectoare
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# Ștergeți package-lock din subdirectoare
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Testarea
|
||||
|
||||
```shell
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Crearea unui Pull Request
|
||||
|
||||
- Vă rugăm să păstrați PR-urile mici și concentrate pe un singur lucru
|
||||
- Vă rugăm să urmați formatul de creare a branchurilor
|
||||
- feature/[Numele funcției]: Acest branch ar trebui să conțină modificări pentru o funcție anumită
|
||||
- Exemplu: feature/dark-mode
|
||||
- bugfix/[Numele eroarei]: Acest branch ar trebui să conţină numai remedieri pentru o eroare anumită
|
||||
- Exemplu bugfix/bug-1
|
||||
@@ -1,3 +1,5 @@
|
||||
[English](/contributing.md) | [Українська](/contributing_ua.md) | **Русский** | [Türkçe](/contributing_tr.md) | [Deutsch](/contributing_de.md) | [Français](/contributing_fr.md) | [বাংলা](docs/contributing/contributing_bn.md)
|
||||
|
||||
## Давайте вместе сделаем Бруно лучше!!!
|
||||
|
||||
Я рад, что Вы хотите усовершенствовать bruno. Ниже приведены рекомендации по запуску bruno на вашем компьютере.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## Bruno'yu birlikte daha iyi hale getirelim !!
|
||||
[English](../../contributing.md) | [Українська](docs/contributing/contributing_ua.md) | [Русский](docs/contributing/contributing_ru.md) | **Türkçe** | [Deutsch](docs/contributing/contributing_de.md) | [Français](docs/contributing/contributing_fr.md) | [Português (BR)](docs/contributing/contributing_pt_br.md) | [বাংলা](docs/contributing/contributing_bn.md) | [Español](docs/contributing/contributing_es.md) | [Română](docs/contributing/contributing_ro.md) | [Polski](docs/contributing/contributing_pl.md)
|
||||
| [简体中文](docs/contributing/contributing_cn.md)
|
||||
## Bruno'yu birlikte daha iyi hale getirelim!!!
|
||||
|
||||
Bruno'yu geliştirmek istemenizden mutluluk duyuyorum. Aşağıda, bruno'yu bilgisayarınıza getirmeye başlamak için yönergeler bulunmaktadır.
|
||||
bruno'yu geliştirmek istemenizden mutluluk duyuyoruz. Aşağıda, bruno'yu bilgisayarınıza getirmeye başlamak için yönergeler bulunmaktadır.
|
||||
|
||||
### Kullanılan Teknolojiler
|
||||
|
||||
@@ -11,7 +13,7 @@ Kullandığımız kütüphaneler
|
||||
- CSS - Tailwind
|
||||
- Kod Düzenleyiciler - Codemirror
|
||||
- Durum Yönetimi - Redux
|
||||
- Iconlar - Tabler Simgeleri
|
||||
- Iconlar - Tabler Icons
|
||||
- Formlar - formik
|
||||
- Şema Doğrulama - Yup
|
||||
- İstek İstemcisi - axios
|
||||
@@ -21,9 +23,60 @@ Kullandığımız kütüphaneler
|
||||
|
||||
[Node v18.x veya en son LTS sürümüne](https://nodejs.org/en/) ve npm 8.x'e ihtiyacınız olacaktır. Projede npm çalışma alanlarını kullanıyoruz
|
||||
|
||||
### Kodlamaya başlayalım
|
||||
## Gelişim
|
||||
|
||||
Yerel geliştirme ortamının çalıştırılmasına ilişkin talimatlar için lütfen [development.md](docs/development.md) adresine başvurun.
|
||||
Bruno bir masaüstü uygulaması olarak geliştirilmektedir. Next.js uygulamasını bir terminalde çalıştırarak uygulamayı yüklemeniz ve ardından electron uygulamasını başka bir terminalde çalıştırmanız gerekir.
|
||||
|
||||
### Bağımlılıklar
|
||||
|
||||
- NodeJS v18
|
||||
|
||||
### Yerel Geliştirme
|
||||
|
||||
```bash
|
||||
# nodejs 18 sürümünü kullan
|
||||
nvm use
|
||||
|
||||
# deps yükleyin
|
||||
npm i --legacy-peer-deps
|
||||
|
||||
# graphql dokümanlarını oluştur
|
||||
npm run build:graphql-docs
|
||||
|
||||
# bruno sorgusu oluştur
|
||||
npm run build:bruno-query
|
||||
|
||||
# sonraki uygulamayı çalıştır (terminal 1)
|
||||
npm run dev:web
|
||||
|
||||
# electron uygulamasını çalıştır (terminal 2)
|
||||
npm run dev:electron
|
||||
```
|
||||
|
||||
### Sorun Giderme
|
||||
|
||||
`npm install`'ı çalıştırdığınızda `Unsupported platform` hatası ile karşılaşabilirsiniz. Bunu düzeltmek için `node_modules` ve `package-lock.json` dosyalarını silmeniz ve `npm install` dosyasını çalıştırmanız gerekecektir. Bu, uygulamayı çalıştırmak için gereken tüm gerekli paketleri yüklemelidir.
|
||||
|
||||
|
||||
```shell
|
||||
# Alt dizinlerdeki node_modules öğelerini silme
|
||||
find ./ -type d -name "node_modules" -print0 | while read -d $'\0' dir; do
|
||||
rm -rf "$dir"
|
||||
done
|
||||
|
||||
# Alt dizinlerdeki paket kilidini silme
|
||||
find . -type f -name "package-lock.json" -delete
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
```bash
|
||||
# bruno-schema
|
||||
npm test --workspace=packages/bruno-schema
|
||||
|
||||
# bruno-lang
|
||||
npm test --workspace=packages/bruno-lang
|
||||
```
|
||||
|
||||
### Pull Request Oluşturma
|
||||
|
||||
@@ -31,5 +84,5 @@ Yerel geliştirme ortamının çalıştırılmasına ilişkin talimatlar için l
|
||||
- Lütfen şube oluşturma formatını takip edin
|
||||
- feature/[özellik adı]: Bu dal belirli bir özellik için değişiklikler içermelidir
|
||||
- Örnek: feature/dark-mode
|
||||
- bugfix/[hata adı]: Bu dal yalnızca belirli bir hata için hata düzeltmelerini içermelidir
|
||||
- bugfix/[hata adı]: Bu dal yalnızca belirli bir hata için hata düzeltmeleri içermelidir
|
||||
- Örnek bugfix/bug-1
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
[English](/contributing.md) | **Українська** | [Русский](/contributing_ru.md) | [Türkçe](/contributing_tr.md) | [Deutsch](/contributing_de.md) | [Français](/contributing_fr.md) | [বাংলা](docs/contributing/contributing_bn.md)
|
||||
|
||||
## Давайте зробимо Bruno краще, разом !!
|
||||
|
||||
Я дуже радий що Ви бажаєте покращити Bruno. Нижче наведені вказівки як розпочати розробку Bruno на Вашому комп'ютері.
|
||||
|
||||
7
docs/publishing/publishing_bn.md
Normal file
7
docs/publishing/publishing_bn.md
Normal file
@@ -0,0 +1,7 @@
|
||||
[English](/publishing.md) | [Português (BR)](docs/publishing/publishing_pt_br.md) | [Română](docs/publishing/publishing_ro.md) | [Türkçe](/docs/publishing/publishing_tr.md) | [Polski](docs/publishing/publishing_pl.md) | **বাংলা** | [Français](docs/publishing/publishing_fr.md)
|
||||
|
||||
### ব্রুনোকে নতুন প্যাকেজ ম্যানেজারে প্রকাশ করা
|
||||
|
||||
যদিও আমাদের কোড ওপেন সোর্স এবং সবার ব্যবহারের জন্য উপলব্ধ, তবে আমরা নতুন প্যাকেজ ম্যানেজারে প্রকাশনা বিবেচনা করার আগে আমাদের সাথে যোগাযোগ করার জন্য অনুরোধ করি। ব্রুনোর স্রষ্টা হিসাবে, আমি এই প্রকল্পের জন্য `Bruno` ট্রেডমার্ক ধারণ করি এবং এর বিতরণ পরিচালনা করতে চাই। যদি আপনি একটি নতুন প্যাকেজ ম্যানেজারে ব্রুনো দেখতে চান, দয়া করে একটি GitHub ইস্যু তুলুন।
|
||||
|
||||
যদিও আমাদের বেশিরভাগ বৈশিষ্ট্য বিনামূল্যে এবং ওপেন সোর্স (যা REST এবং GraphQL API গুলিকে কভার করে), আমরা ওপেন-সোর্স নীতি এবং স্থায়িত্বের মধ্যে একটি সুসঙ্গত ভারসাম্য বজায় রাখার জন্য চেষ্টা করি - https://github.com/usebruno/bruno/discussions/269
|
||||
7
docs/publishing/publishing_fr.md
Normal file
7
docs/publishing/publishing_fr.md
Normal file
@@ -0,0 +1,7 @@
|
||||
[English](/publishing.md) | [Português (BR)](docs/publishing/publishing_pt_br.md) | [Română](docs/publishing/publishing_ro.md) | [Türkçe](/docs/publishing/publishing_tr.md) | [Polski](docs/publishing/publishing_pl.md) | [বাংলা](docs/publishing/publishing_bn.md) | **Français**
|
||||
|
||||
### Publier Bruno dans un nouveau gestionnaire de paquets
|
||||
|
||||
Bien que notre code soit open source et disponible pour tout le monde, nous vous remercions de nous contacter avant de considérer sa publication sur un nouveau gestionnaire de paquets. En tant que createur de Bruno, je détiens la marque `Bruno` pour ce projet et j'aimerais gérer moi-même sa distribution. Si vous voyez Bruno sur un nouveau gestionnaire de paquets, merci de créer une _issue_ Github.
|
||||
|
||||
Bien que la majorité de nos fonctionnalités soient gratuites et open source (ce qui couvre les apis REST et GraphQL), nous nous efforçons de trouver un équilibre harmonieux entre les principes de l'open source et la pérennité - https://github.com/usebruno/bruno/discussions/269
|
||||
8
docs/publishing/publishing_pl.md
Normal file
8
docs/publishing/publishing_pl.md
Normal file
@@ -0,0 +1,8 @@
|
||||
[English](/publishing.md) | [Português (BR)](docs/publishing/publishing_pt_br.md) | [Română](docs/publishing/publishing_ro.md) | [Türkçe](/docs/publishing/publishing_tr.md) | **Polski** | [বাংলা](docs/publishing/publishing_bn.md) | [Français](docs/publishing/publishing_fr.md)
|
||||
|
||||
### Publikowanie Bruno w nowym menedżerze pakietów
|
||||
|
||||
Chociaż nasz kod jest otwartoźródłowy i dostępny dla każdego do użytku, uprzejmie prosimy o kontakt z nami przed rozważeniem publikacji w nowych menedżerach pakietów. Jako twórca Bruno, posiadam znak towarowy `Bruno` dla tego projektu i chciałbym zarządzać jego dystrybucją. Jeśli chcesz zobaczyć Bruno w nowym menedżerze pakietów, proszę zgłoś problem na GitHubie.
|
||||
|
||||
Chociaż większość naszych funkcji jest darmowa i otwartoźródłowa (co obejmuje REST i GraphQL Apis),
|
||||
staramy się osiągnąć harmonijny balans między zasadami open-source a zrównoważonym rozwojem - https://github.com/usebruno/bruno/discussions/269
|
||||
7
docs/publishing/publishing_pt_br.md
Normal file
7
docs/publishing/publishing_pt_br.md
Normal file
@@ -0,0 +1,7 @@
|
||||
[English](/publishing.md) | **Português (BR)** | [Română](docs/publishing/publishing_ro.md) | [Türkçe](/docs/publishing/publishing_tr.md) | [Polski](docs/publishing/publishing_pl.md) | [বাংলা](docs/publishing/publishing_bn.md) | [Français](docs/publishing/publishing_fr.md)
|
||||
|
||||
### Publicando Bruno em um novo gerenciador de pacotes
|
||||
|
||||
Embora nosso código seja de código aberto e esteja disponível para todos usarem, pedimos gentilmente que entre em contato conosco antes de considerar a publicação em novos gerenciadores de pacotes. Como o criador da ferramenta, mantenho a marca registrada `Bruno` para este projeto e gostaria de gerenciar sua distribuição. Se deseja ver o Bruno em um novo gerenciador de pacotes, por favor, solicite através de uma issue no GitHub.
|
||||
|
||||
Embora a maioria de nossas funcionalidades seja gratuita e de código aberto (o que abrange API's REST e GraphQL), buscamos alcançar um equilíbrio harmonioso entre os princípios de código aberto e sustentabilidade. - https://github.com/usebruno/bruno/discussions/269
|
||||
8
docs/publishing/publishing_ro.md
Normal file
8
docs/publishing/publishing_ro.md
Normal file
@@ -0,0 +1,8 @@
|
||||
[English](/publishing.md) | [Português (BR)](/docs/publishing/publishing_pt_br.md) | **Română** | [Türkçe](/docs/publishing/publishing_tr.md) | [Polski](docs/publishing/publishing_pl.md) | [বাংলা](/docs/publishing/publishing_bn.md) | [Français](docs/publishing/publishing_fr.md)
|
||||
|
||||
### Publicarea lui Bruno la un gestionar de pachete nou
|
||||
|
||||
Deși codul nostru este cu sursă deschisă și disponibil pentru utilizare pentru toată lumea, vă rugăm să ne contactați înainte de a considera publicarea pe gestionari de pachete noi. În calitate de creator al lui Bruno, dețin marca comercială `Bruno` pentru acest proiect și aș dori să gestionez distribuția acestuia. Dacă doriți să-l vedeți pe Bruno pe un gestionar de pachete nou, vă rugăm să creați un issue pe GitHub.
|
||||
|
||||
În timp ce majoritatea funcțiilor noastre sunt gratuite și cu sursă deschisă (ceea ce acoperă API-uri REST și GraphQL),
|
||||
ne străduim să găsim un echilibru armonios între principiile de sursă deschisă și sustenabilitate - https://github.com/usebruno/bruno/discussions/269
|
||||
8
docs/publishing/publishing_tr.md
Normal file
8
docs/publishing/publishing_tr.md
Normal file
@@ -0,0 +1,8 @@
|
||||
[English](../../publishing.md) | [Português (BR)](docs/publishing/publishing_pt_br.md) | [Română](docs/publishing/publishing_ro.md) | **Türkçe** | [Polski](docs/publishing/publishing_pl.md) | [বাংলা](docs/publishing/publishing_bn.md) | [Français](docs/publishing/publishing_fr.md)
|
||||
|
||||
### Bruno'yu yeni bir paket yöneticisine yayınlama
|
||||
|
||||
Kodumuz açık kaynak kodlu ve herkesin kullanımına açık olsa da, yeni paket yöneticilerinde yayınlamayı düşünmeden önce bize ulaşmanızı rica ediyoruz. Bruno'nun yaratıcısı olarak, bu proje için `Bruno` ticari markasına sahibim ve dağıtımını yönetmek istiyorum. Bruno'yu yeni bir paket yöneticisinde görmek istiyorsanız, lütfen bir GitHub sorunu oluşturun.
|
||||
|
||||
Özelliklerimizin çoğu ücretsiz ve açık kaynak olsa da (REST ve GraphQL Apis'i kapsar),
|
||||
açık kaynak ilkeleri ile sürdürülebilirlik arasında uyumlu bir denge kurmaya çalışıyoruz - https://github.com/usebruno/bruno/discussions/269
|
||||
123
docs/readme/readme_bn.md
Normal file
123
docs/readme/readme_bn.md
Normal file
@@ -0,0 +1,123 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### ব্রুনো - API অন্বেষণ এবং পরীক্ষা করার জন্য ওপেনসোর্স IDE।
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](../../readme.md) | [Українська](docs/readme/readme_ua.md) | [Русский](docs/readme/readme_ru.md) | [Türkçe](docs/readme/readme_tr.md) | [Deutsch](docs/readme/readme_de.md) | [Français](docs/readme/readme_fr.md) | [Português (BR)](docs/readme/readme_pt_br.md) | [한국어](docs/readme/readme_kr.md) | **বাংলা** | [Español](docs/readme/readme_es.md) | [Italiano](docs/readme/readme_it.md) | [Română](docs/readme/readme_ro.md) | [Polski](docs/readme/readme_pl.md)
|
||||
|
||||
ব্রুনো হল একটি নতুন এবং উদ্ভাবনী API ক্লায়েন্ট, যার লক্ষ্য পোস্টম্যান এবং অনুরূপ সরঞ্জাম দ্বারা প্রতিনিধিত্ব করা স্থিতাবস্থায় বিপ্লব ঘটানো।
|
||||
|
||||
ব্রুনো আপনার সংগ্রহগুলি সরাসরি আপনার ফাইল সিস্টেমের একটি ফোল্ডারে সঞ্চয় করে। আমরা API অনুরোধ সম্পর্কে তথ্য সংরক্ষণ করতে একটি প্লেইন টেক্সট মার্কআপ ভাষা, ব্রু ব্যবহার করি।
|
||||
|
||||
আপনি আপনার API সংগ্রহে সহযোগিতা করতে গিট বা আপনার পছন্দের যেকোনো সংস্করণ নিয়ন্ত্রণ ব্যবহার করতে পারেন।
|
||||
|
||||
ব্রুনো শুধুমাত্র অফলাইন। ব্রুনোতে ক্লাউড-সিঙ্ক যোগ করার কোন পরিকল্পনা নেই, কখনও। আমরা আপনার ডেটা গোপনীয়তার মূল্য দিই এবং বিশ্বাস করি এটি আপনার ডিভাইসে থাকা উচিত। আমাদের দীর্ঘমেয়াদী দৃষ্টি পড়ুন। [এখানে ](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
📢 ইন্ডিয়া FOSS 3.0 সম্মেলনে আমাদের সাম্প্রতিক আলোচনা দেখুন [এখানে](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### স্থাপন
|
||||
|
||||
ব্রুনো বাইনারি ডাউনলোড হিসাবে উপলব্ধ [আমাদের ওয়েবসাইটে](https://www.usebruno.com/downloads) ম্যাক, উইন্ডোজ এবং লিনাক্সের জন্য।
|
||||
|
||||
আপনি Homebrew, Chocolatey, Snap এবং Apt এর মত প্যাকেজ ম্যানেজারদের মাধ্যমে ব্রুনো ইনস্টল করতে পারেন।
|
||||
|
||||
```sh
|
||||
# Homebrew এর মাধ্যমে Mac-এ
|
||||
brew install bruno
|
||||
|
||||
# চকোলেটির মাধ্যমে উইন্ডোজে
|
||||
choco install bruno
|
||||
|
||||
# স্ন্যাপ এর মাধ্যমে লিনাক্সে
|
||||
snap install bruno
|
||||
|
||||
# Apt এর মাধ্যমে লিনাক্সে
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### একাধিক প্ল্যাটফর্মে চালান 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Git এর মাধ্যমে সহযোগিতা করুন 👩💻🧑💻
|
||||
|
||||
অথবা আপনার পছন্দের যেকোনো সংস্করণ নিয়ন্ত্রণ ব্যবস্থা
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### গুরুত্বপূর্ণ লিংক 📌
|
||||
|
||||
- [আমাদের দীর্ঘমেয়াদী দৃষ্টি](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [রোডম্যাপ](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [ডকুমেন্টেশন](https://docs.usebruno.com)
|
||||
- [ওয়েবসাইট](https://www.usebruno.com)
|
||||
- [মূল্য](https://www.usebruno.com/pricing)
|
||||
- [ডাউনলোড করুন](https://www.usebruno.com/downloads)
|
||||
|
||||
### শোকেস 🎥
|
||||
|
||||
- [প্রশংসাপত্র](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [নলেজ হাব](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [স্ক্রিপ্টম্যানিয়া](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### সমর্থন ❤️
|
||||
|
||||
উফ ! আপনি যদি প্রকল্পটি পছন্দ করেন তবে ⭐ বোতামটি টিপুন !!
|
||||
|
||||
### প্রশংসাপত্র শেয়ার করুন 📣
|
||||
|
||||
যদি ব্রুনো আপনাকে কর্মক্ষেত্রে এবং আপনার দলগুলিতে সাহায্য করে থাকে, অনুগ্রহ করে আপনার [আমাদের গিটহাব আলোচনায় প্রশংসাপত্রগুলি](https://github.com/usebruno/bruno/discussions/343) শেয়ার করতে ভুলবেন না
|
||||
|
||||
### নতুন প্যাকেজ পরিচালকদের কাছে প্রকাশ করা হচ্ছে
|
||||
|
||||
আরও তথ্যের জন্য অনুগ্রহ করে [এখানে](../publishing/publishing_bn.md) দেখুন।
|
||||
|
||||
### অবদান 👩💻🧑💻
|
||||
|
||||
আমি খুশি যে আপনি ব্রুনোর উন্নতি করতে চাইছেন। অনুগ্রহ করে [অবদানকারী নির্দেশিকা](contributing.md) দেখুন
|
||||
|
||||
আপনি কোডের মাধ্যমে অবদান রাখতে না পারলেও, অনুগ্রহ করে বাগ এবং বৈশিষ্ট্যের অনুরোধ ফাইল করতে দ্বিধা করবেন না যা আপনার ব্যবহারের ক্ষেত্রে সমাধান করার জন্য প্রয়োগ করা প্রয়োজন।
|
||||
|
||||
### লেখক
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### সাথে থাকুন 🌐
|
||||
|
||||
[𝕏 (টুইটার)](https://twitter.com/use_bruno) <br />
|
||||
[ওয়েবসাইট](https://www.usebruno.com) <br />
|
||||
[ডিসকর্ড](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[লিঙ্কডইন](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### ট্রেডমার্ক
|
||||
|
||||
**নাম**
|
||||
|
||||
`Bruno` হল একটি ট্রেডমার্ক [Anoop M D](https://www.helloanoop.com/)
|
||||
|
||||
**লোগো**
|
||||
|
||||
লোগোটি [OpenMoji](https://openmoji.org/library/emoji-1F436/) থেকে নেওয়া হয়েছে। লাইসেন্স: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### লাইসেন্স 📄
|
||||
|
||||
[MIT](license.md)
|
||||
131
docs/readme/readme_cn.md
Normal file
131
docs/readme/readme_cn.md
Normal file
@@ -0,0 +1,131 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - 开源IDE,用于探索和测试API。
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](../../readme.md) | [Українська](./readme_ua.md) | [Русский](./readme_ru.md) | [Türkçe](./readme_tr.md) | [Deutsch](./readme_de.md) | [Français](./readme_fr.md) | [Português (BR)](./readme_pt_br.md) | [한국어](./readme_kr.md) | [বাংলা](./readme_bn.md) | [Español](./readme_es.md) | [Italiano](./readme_it.md) | [Română](./readme_ro.md) | [Polski](./readme_pl.md) | [简体中文](./readme_cn.md)
|
||||
|
||||
|
||||
Bruno 是一款全新且创新的 API 客户端,旨在颠覆 Postman 和其他类似工具。
|
||||
|
||||
Bruno 直接在您的电脑文件夹中存储您的 API 信息。我们使用纯文本标记语言 Bru 来保存有关 API 的信息。
|
||||
|
||||
您可以使用 Git 或您选择的任何版本控制系统来对您的API信息进行版本控制和协作。
|
||||
|
||||
Bruno 仅限离线使用。我们计划永不向 Bruno 添加云同步功能。我们重视您的数据隐私,并认为它应该留在您的设备上。阅读我们的长期愿景 [点击查看](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
|
||||
📢 观看我们在印度 FOSS 3.0 会议上的最新演讲 [点击查看](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### 安装
|
||||
|
||||
Bruno 可以在我们的 [网站上下载](https://www.usebruno.com/downloads) Mac、Windows 和 Linux 的可执行文件。
|
||||
|
||||
您也可以通过包管理器如 Homebrew、Chocolatey、Scoop、Snap 和 Apt 安装 Bruno。
|
||||
|
||||
```sh
|
||||
# 在 Mac 电脑上用 Homebrew 安装
|
||||
brew install bruno
|
||||
|
||||
# 在 Windows 上用 Chocolatey 安装
|
||||
choco install bruno
|
||||
|
||||
# 在 Windows 上用 Scoop 安装
|
||||
scoop bucket add extras
|
||||
scoop install bruno
|
||||
|
||||
# 在 Linux 上用 Snap 安装
|
||||
snap install bruno
|
||||
|
||||
# 在 Linux 上用 Apt 安装
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### 在 Mac 上通过 Homebrew 安装 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Collaborate 安装 👩💻🧑💻
|
||||
|
||||
或者任何你选择的版本控制系统
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### 重要链接 📌
|
||||
|
||||
- [我们的愿景](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [路线图](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [文档](https://docs.usebruno.com)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
|
||||
- [网站](https://www.usebruno.com)
|
||||
- [价格](https://www.usebruno.com/pricing)
|
||||
- [下载](https://www.usebruno.com/downloads)
|
||||
- [Github 赞助](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### 展示 🎥
|
||||
|
||||
- [Testimonials](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Knowledge Hub](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### 支持 ❤️
|
||||
|
||||
如果您喜欢 Bruno 并想支持我们的开源工作,请考虑通过 [Github Sponsors](https://github.com/sponsors/helloanoop) 来赞助我们。
|
||||
|
||||
### 分享评价 📣
|
||||
|
||||
如果 Bruno 在您的工作和团队中帮助了您,请不要忘记在我们的 GitHub 讨论上分享您的 [评价](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### 发布到新的包管理器
|
||||
|
||||
有关更多信息,请参见 [此处](../../publishing.md) 。
|
||||
|
||||
### 贡献 👩💻🧑💻
|
||||
|
||||
我很高兴您希望改进bruno。请查看 [贡献指南](../../contributing.md)。
|
||||
|
||||
即使您无法通过代码做出贡献,我们仍然欢迎您提出BUG和新的功能需求。
|
||||
|
||||
### 作者
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### 联系方式 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### 商标
|
||||
|
||||
**名称**
|
||||
|
||||
`Bruno` 是由 [Anoop M D](https://www.helloanoop.com/) 持有的商标。
|
||||
|
||||
**Logo**
|
||||
|
||||
Logo 源自 [OpenMoji](https://openmoji.org/library/emoji-1F436/). License: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### 许可证 📄
|
||||
|
||||
[MIT](../../license.md)
|
||||
@@ -1,5 +1,5 @@
|
||||
<br />
|
||||
<img src="assets/images/logo-transparent.png" width="80"/>
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - Opensource IDE zum Erkunden und Testen von APIs.
|
||||
|
||||
@@ -10,25 +10,27 @@
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](/readme.md) | [Українська](/readme_ua.md) | [Русский](/readme_ru.md) | [Türkçe](/readme_tr.md) | **Deutsch** | [Français](/readme_fr.md) | [বাংলা](docs/readme/readme_bn.md)
|
||||
|
||||
Bruno ist ein neuer und innovativer API-Client, der den Status Quo von Postman und ähnlichen Tools revolutionieren soll.
|
||||
|
||||
Bruno speichert Deine Sammlungen direkt in einem Ordner in Deinem Dateisystem. Wir verwenden eine einfache Textauszeichnungssprache - Bru - um Informationen über API-Anfragen zu speichern.
|
||||
Bruno speichert deine Sammlungen direkt in einem Ordner in deinem Dateisystem. Wir verwenden eine einfache Textauszeichnungssprache - Bru - um Informationen über API-Anfragen zu speichern.
|
||||
|
||||
Du kannst Git oder eine andere Versionskontrolle deiner Wahl verwenden, um an deinen API-Sammlungen gemeinsam mit anderen zu arbeiten.
|
||||
Du kannst Git oder eine andere Versionskontrolle deiner Wahl verwenden, um gemeinsam mit anderen an deinen API-Sammlungen zu arbeiten.
|
||||
|
||||
Bruno ist ein reines Offline-Tool. Es gibt keine Pläne, Bruno eine Cloud-Synchronisation hinzuzufügen. Wir schätzen den Schutz Deiner Daten und glauben, dass sie auf Deinem Gerät bleiben sollten. Lies unsere Langzeit-Vision [hier](https://github.com/usebruno/bruno/discussions/269).
|
||||
Bruno ist ein reines Offline-Tool. Es gibt keine Pläne, Bruno um eine Cloud-Synchronisation zu erweitern. Wir schätzen den Schutz deiner Daten und glauben, dass sie auf deinem Gerät bleiben sollten. Lies unsere Langzeit-Vision [hier](https://github.com/usebruno/bruno/discussions/269).
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Einsatz auf verschiedensten Plattformen 🖥️
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Zusammenarbeiten mit Git 👩💻🧑💻
|
||||
### Zusammenarbeit mit Git 👩💻🧑💻
|
||||
|
||||
oder eine Versionskontrolle Deiner Wahl
|
||||
Oder einer Versionskontrolle deiner Wahl
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Wichtige Links 📌
|
||||
|
||||
@@ -47,21 +49,21 @@ oder eine Versionskontrolle Deiner Wahl
|
||||
|
||||
### Unterstützung ❤️
|
||||
|
||||
Wuff! Wenn Du dieses Projekt magst, klick den ⭐ Button !!
|
||||
Wuff! Wenn du dieses Projekt magst, klick den ⭐ Button !!
|
||||
|
||||
### Teile Erfahrungsberichte 📣
|
||||
|
||||
Wenn Bruno Dir bei Deiner Arbeit und in Deinen Teams geholfen hat, vergiss bitte nicht, Deine [Erfahrungsberichte auf unserer GitHub-Diskussion](https://github.com/usebruno/bruno/discussions/343) zu teilen.
|
||||
Wenn Bruno dir und in deinen Teams bei der Arbeit geholfen hat, vergiss bitte nicht, deine [Erfahrungsberichte auf unserer GitHub-Diskussion](https://github.com/usebruno/bruno/discussions/343) zu teilen.
|
||||
|
||||
### Veröffentlichung in neuen Paketmanagern
|
||||
### Bereitstellung in neuen Paket-Managern
|
||||
|
||||
Bitte [hier](publishing.md) für mehr Informationen lesen.
|
||||
Mehr Informationen findest du [hier](/publishing.md).
|
||||
|
||||
### Mitmachen 👩💻🧑💻
|
||||
|
||||
Ich freue mich, dass Du Bruno verbessern willst. Bitte schau Dir den [Leitfaden zum Mitmachen](../contributing/contributing_de.md) an.
|
||||
Ich freue mich, dass du Bruno verbessern willst. Bitte schau dir den [Leitfaden zum Mitmachen](../contributing/contributing_de.md) an.
|
||||
|
||||
Auch wenn Du nicht in der Lage bist, einen Beitrag in Form von Code zu leisten, zögere bitte nicht, uns Fehler und Funktionswünsche mitzuteilen, die implementiert werden müssen, um Deinen Anwendungsfall zu unterstützen.
|
||||
Auch wenn du nicht in der Lage bist, einen Beitrag in Form von Code zu leisten, zögere bitte nicht, uns Fehler und Funktionswünsche mitzuteilen, die implementiert werden müssen, um deinen Anwendungsfall zu unterstützen.
|
||||
|
||||
### Autoren
|
||||
|
||||
@@ -90,4 +92,4 @@ Das Logo stammt von [OpenMoji](https://openmoji.org/library/emoji-1F436/). Lizen
|
||||
|
||||
### Lizenz 📄
|
||||
|
||||
[MIT](license.md)
|
||||
[MIT](/license.md)
|
||||
|
||||
93
docs/readme/readme_es.md
Normal file
93
docs/readme/readme_es.md
Normal file
@@ -0,0 +1,93 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - IDE de código abierto para explorar y probar APIs.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
Bruno un cliente de APIs nuevo e innovador, creado con el objetivo de revolucionar el panorama actual representado por Postman y otras herramientas similares.
|
||||
|
||||
Bruno almacena tus colecciones directamente en una carpeta de tu sistema de archivos. Usamos un lenguaje de marcado de texto plano, llamado Bru, para guardar información sobre las peticiones a tus APIs.
|
||||
|
||||
Puedes usar git o cualquier otro sistema de control de versiones que prefieras para colaborar en tus colecciones.
|
||||
|
||||
Bruno funciona sin conexión a internet. No tenemos intenciones de añadir sincronización en la nube a Bruno, en ningún momento. Valoramos tu privacidad y creemos que tus datos deben permanecer en tu dispositivo. Puedes leer nuestra visión a largo plazo [aquí](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Ejecútalo en múltiples plataformas 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Colabora vía Git 👩💻🧑💻
|
||||
|
||||
O cualquier otro sistema de control de versiones que prefieras
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Enlaces importantes 📌
|
||||
|
||||
- [Nuestra Visión a Largo Plazo](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Hoja de Ruta](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Documentación](https://docs.usebruno.com)
|
||||
- [Sitio Web](https://www.usebruno.com)
|
||||
- [Precios](https://www.usebruno.com/pricing)
|
||||
- [Descargas](https://www.usebruno.com/downloads)
|
||||
|
||||
### Casos de uso 🎥
|
||||
|
||||
- [Testimonios](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Centro de Conocimiento](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scripts de la Comunidad](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### Apoya el proyecto ❤️
|
||||
|
||||
¡Guau! Si te gusta el proyecto, ¡dale al botón de ⭐!
|
||||
|
||||
### Comparte tus testimonios 📣
|
||||
|
||||
Si Bruno te ha ayudado en tu trabajo y con tus equipos, por favor, no olvides compartir tus testimonios en [nuestras discusiones de GitHub](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### Publicar en nuevos gestores de paquetes
|
||||
|
||||
Por favor, consulta [aquí](publishing.md) para más información.
|
||||
|
||||
### Contribuye 👩💻🧑💻
|
||||
|
||||
Estamos encantados de que quieras ayudar a mejorar Bruno. Por favor, consulta la [guía de contribución](contributing_es.md) para más información.
|
||||
|
||||
Incluso si no puedes contribuir con código, no dudes en reportar errores y solicitar nuevas funcionalidades que necesites para resolver tu caso de uso.
|
||||
|
||||
### Colaboradores
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Mantente en contacto 🌐
|
||||
|
||||
[X](https://twitter.com/use_bruno) <br />
|
||||
[Sitio Web](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Marca
|
||||
|
||||
**Nombre**
|
||||
|
||||
`Bruno` es una marca propiedad de [Anoop M D](https://www.helloanoop.com/).
|
||||
|
||||
**Logo**
|
||||
|
||||
El logo fue obtenido de [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licencia: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Licencia 📄
|
||||
|
||||
[MIT](license.md)
|
||||
@@ -1,5 +1,5 @@
|
||||
<br />
|
||||
<img src="assets/images/logo-transparent.png" width="80"/>
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - IDE Opensource pour explorer et tester des APIs.
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
|
||||
[English](/readme.md) | [Українська](docs/readme/readme_ua.md) | [Русский](docs/readme/readme_ru.md) | [Türkçe](docs/readme/readme_tr.md) | [Deutsch](docs/readme/readme_de.md) | **Français** | [Português (BR)](docs/readme/readme_pt_br.md) | [한국어](docs/readme/readme_kr.md) | [বাংলা](docs/readme/readme_bn.md) | [Español](docs/readme/readme_es.md) | [Italiano](docs/readme/readme_it.md) | [Română](docs/readme/readme_ro.md) | [Polski](docs/readme/readme_pl.md)
|
||||
|
||||
Bruno est un nouveau client API, innovant, qui a pour but de révolutionner le _status quo_ que représente Postman et les autres outils.
|
||||
|
||||
Bruno sauvegarde vos collections directement sur votre système de fichiers. Nous utilisons un langage de balise de type texte pour décrire les requêtes API.
|
||||
@@ -18,17 +21,51 @@ Vous pouvez utiliser git ou tout autre gestionnaire de version pour travailler d
|
||||
|
||||
Bruno ne fonctionne qu'en mode déconnecté. Il n'y a pas de d'abonnement ou de synchronisation avec le cloud Bruno, il n'y en aura jamais. Nous sommes conscients de la confidentialité de vos données et nous sommes convaincus qu'elles doivent rester sur vos appareils. Vous pouvez lire notre vision à long terme [ici (en anglais)](https://github.com/usebruno/bruno/discussions/269).
|
||||
|
||||
 <br /><br />
|
||||
|
||||
📢 Regarder notre présentation récente lors de la conférence India FOSS 3.0 (en anglais) [ici](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Installation
|
||||
|
||||
Bruno est disponible au téléchargement [sur notre site web](https://www.usebruno.com/downloads), pour Mac, Windows et Linux.
|
||||
|
||||
Vous pouvez aussi installer Bruno via un gestionnaire de paquets, comme Homebrew, Chocolatey, Scoop, Snap et Apt.
|
||||
|
||||
```sh
|
||||
# Mac via Homebrew
|
||||
brew install bruno
|
||||
|
||||
# Windows via Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# Windows via Scoop
|
||||
scoop bucket add extras
|
||||
scoop install bruno
|
||||
|
||||
# Linux via Snap
|
||||
snap install bruno
|
||||
|
||||
# Linux via Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Fonctionne sur de multiples platformes 🖥️
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Collaborer via Git 👩💻🧑💻
|
||||
|
||||
Ou n'importe quel système de gestion de sources
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Liens importants 📌
|
||||
|
||||
@@ -38,6 +75,7 @@ Ou n'importe quel système de gestion de sources
|
||||
- [Site web](https://www.usebruno.com)
|
||||
- [Prix](https://www.usebruno.com/pricing)
|
||||
- [Téléchargement](https://www.usebruno.com/downloads)
|
||||
- [Sponsors Github](https://github.com/sponsors/helloanoop)
|
||||
|
||||
### Showcase 🎥
|
||||
|
||||
@@ -55,7 +93,7 @@ Si Bruno vous a aidé dans votre travail, au sein de votre équipe, merci de pen
|
||||
|
||||
### Publier Bruno sur un nouveau gestionnaire de paquets
|
||||
|
||||
Veuillez regarder [ici](publishing.md) pour plus d'information.
|
||||
Veuillez regarder [ici](/publishing.md) pour plus d'information.
|
||||
|
||||
### Contribuer 👩💻🧑💻
|
||||
|
||||
@@ -91,4 +129,4 @@ Licence: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Licence 📄
|
||||
|
||||
[MIT](license.md)
|
||||
[MIT](/license.md)
|
||||
|
||||
121
docs/readme/readme_it.md
Normal file
121
docs/readme/readme_it.md
Normal file
@@ -0,0 +1,121 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - Opensource IDE per esplorare e testare gli APIs.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
Bruno è un nuovo ed innovativo API client, mirato a rivoluzionare lo status quo rappresentato da Postman e strumenti simili disponibili.
|
||||
|
||||
Bruno memorizza le tue raccolte direttamente in una cartella del tuo filesystem. Utilizziamo un linguaggio di markup in testo semplice chiamato Bru per salvare informazioni sulle richeste API.
|
||||
|
||||
Puoi utilizzare Git o qualsiasi sistema di controllo che preferisci per collaborare sulle tue raccolte di API.
|
||||
|
||||
Bruno funziona solo in modalità offline. Non ci sono piani per aggiungere la sincronizzazione su cloud a Bruno in futuro. Valorizziamo la privacy dei tuoi dati e crediamo che dovrebbero rimanere sul tuo dispositivo. Puoi leggere la nostra visione a lungo termine [qui (in inglese)](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
📢 Guarda la nostra presentazione più recente alla conferenza India FOSS 3.0 [qui](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Installazione
|
||||
|
||||
Bruno è disponibile come download binario [sul nostro sito](https://www.usebruno.com/downloads) per Mac, Windows e Linux.
|
||||
|
||||
Puoi installare Bruno anche tramite package manger come Homebrew, Chocolatey, Snap e Apt.
|
||||
|
||||
```sh
|
||||
# Su Mac come Homebrew
|
||||
brew install bruno
|
||||
|
||||
# Su Windows come Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# Su Linux tramite Snap
|
||||
snap install bruno
|
||||
|
||||
# Su Linux tramite Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Funziona su diverse piattaforme 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Collabora tramite Git 👩💻🧑💻
|
||||
|
||||
O con qualsiasi sistema di controllo di versioni a tua scelta
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Collegamenti importanti 📌
|
||||
|
||||
- [La nostra visione a lungo termine](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Roadmap](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Documentazione](https://docs.usebruno.com)
|
||||
- [Sito internet](https://www.usebruno.com)
|
||||
- [Prezzo](https://www.usebruno.com/pricing)
|
||||
- [Download](https://www.usebruno.com/downloads)
|
||||
|
||||
### Showcase 🎥
|
||||
|
||||
- [Testimonianze](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Centro di conoscenza](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### Supporto ❤️
|
||||
|
||||
Woof! se ti piace il progetto, premi quel ⭐ pulsante !!
|
||||
|
||||
### Testimonianze condivise 📣
|
||||
|
||||
Se Bruno ti ha aiutato con il tuo lavoro ed il tuo team, per favore non dimenticare di condividere le tue [testimonianze nella nostra discussione su GitHub](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### Pubblica Bruno su un nuovo gestore di pacchetti
|
||||
|
||||
Per favore vedi [qui](publishing.md) per accedere a più informazioni.
|
||||
|
||||
### Contribuire 👩💻🧑💻
|
||||
|
||||
Sono felice che vuoi migliorare Bruno. Per favore controlla la [guida per la partecipazione](contributing.md)
|
||||
|
||||
Anche se non sei in grado di contribuire tramite il codice, non esitare a segnalare bug e richieste di funzionalità che devono essere implementati per risolvere il tuo caso d'uso.
|
||||
|
||||
### Autori
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Resta in contatto 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Sito internet](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Marchio
|
||||
|
||||
**Nome**
|
||||
|
||||
`Bruno` è un marchio registrato appartenente a [Anoop M D](https://www.helloanoop.com/)
|
||||
|
||||
**Logo**
|
||||
|
||||
Il logo è stato creato da [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licenza: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Licenza 📄
|
||||
|
||||
[MIT](license.md)
|
||||
121
docs/readme/readme_kr.md
Normal file
121
docs/readme/readme_kr.md
Normal file
@@ -0,0 +1,121 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - API 탐색 및 테스트를 위한 오픈소스 IDE.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
Bruno는 새롭고 혁신적인 API 클라이언트로, Postman과 유사한 툴들을 혁신하는 것을 목표로 합니다.
|
||||
|
||||
Bruno는 사용자의 컬렉션을 파일 시스템의 폴더에 직접 저장합니다. 일반 텍스트 마크업 언어인 Bru를 사용해 API 요청에 대한 정보를 저장합니다.
|
||||
|
||||
Git 또는 원하는 버전 관리 도구를 사용하여 API 컬렉션을 연동할 수 있습니다.
|
||||
|
||||
브루는 오프라인 전용입니다. 브루노에 클라우드 동기화 기능을 추가할 계획은 없습니다. 저희는 사용자의 데이터 프라이버시를 소중히 여기며, 데이터는 사용자의 기기에 남아 있어야 한다고 믿습니다. 장기 비전 읽기 [링크](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
📢 Watch our recent talk at India FOSS 3.0 Conference [here](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### 설치
|
||||
|
||||
Bruno 는 여기에서 다운로드 받을 수 있습니다.[링크](https://www.usebruno.com/downloads) (맥, 윈도우, 리눅스)
|
||||
|
||||
Homebrew, Chocolatey, Snap, Apt 같은 패키지 관리자를 통해서도 Bruno를 설치할 수 있습니다.
|
||||
|
||||
```sh
|
||||
# On Mac via Homebrew
|
||||
brew install bruno
|
||||
|
||||
# On Windows via Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# On Linux via Snap
|
||||
snap install bruno
|
||||
|
||||
# On Linux via Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### 여러 플랫폼에서 실행하세요. 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Git과 연동하세요. 👩💻🧑💻
|
||||
|
||||
또는 원하는 버전 관리 시스템을 선택하세요.
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### 중요 링크 📌
|
||||
|
||||
- [Our Long Term Vision](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Roadmap](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Documentation](https://docs.usebruno.com)
|
||||
- [Website](https://www.usebruno.com)
|
||||
- [Pricing](https://www.usebruno.com/pricing)
|
||||
- [Download](https://www.usebruno.com/downloads)
|
||||
|
||||
### 쇼케이스 🎥
|
||||
|
||||
- [Testimonials](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Knowledge Hub](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### 지원 ❤️
|
||||
|
||||
프로젝트가 마음에 들면 ⭐ 버튼을 눌러 주세요.
|
||||
|
||||
### 후기 공유 📣
|
||||
|
||||
Bruno가 여러분과 여러분의 팀에 도움이 되었다면, 잊지 말고 공유해 주세요. [Github discussion 공유 링크](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### 새 패키지 관리자에게 게시
|
||||
|
||||
더 많은 정보를 확인하시려명 링크를 클릭해 주세요.[배포 가이드](publishing.md)
|
||||
|
||||
### 컨트리뷰트 👩💻🧑💻
|
||||
|
||||
컨트리뷰트에 관심이 있으시면 링크를 참고해 주세요. [컨트리뷰트 가이드](contributing.md)
|
||||
|
||||
코드를 통해 기여할 수 없더라도 사용 사례를 해결하기 위해 구현이 필요한 버그나 기능 요청을 주저하지 마시고 제출해 주세요.
|
||||
|
||||
### Authors
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Stay in touch 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Trademark
|
||||
|
||||
**Name**
|
||||
|
||||
`Bruno` is a trademark held by [Anoop M D](https://www.helloanoop.com/)
|
||||
|
||||
**Logo**
|
||||
|
||||
The logo is sourced from [OpenMoji](https://openmoji.org/library/emoji-1F436/). License: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### License 📄
|
||||
|
||||
[MIT](license.md)
|
||||
129
docs/readme/readme_pl.md
Normal file
129
docs/readme/readme_pl.md
Normal file
@@ -0,0 +1,129 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - Otwartoźródłowe IDE do exploracji i testów APIs.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](/readme.md) | [Українська](docs/readme/readme_ua.md) | [Русский](docs/readme/readme_ru.md) | [Türkçe](docs/readme/readme_tr.md) | [Deutsch](docs/readme/readme_de.md) | [Français](docs/readme/readme_fr.md) | [Português (BR)](docs/readme/readme_pt_br.md)) | [한국어](docs/readme/readme_kr.md) ) | [বাংলা](docs/readme/readme_bn.md) | [Español](docs/readme/readme_es.md) | [Italiano](docs/readme/readme_it.md) | [Română](docs/readme/readme_ro.md) | **Polski**
|
||||
|
||||
Bruno to nowy i innowacyjny klient API, którego celem jest zrewolucjonizowanie status quo reprezentowy przez Postman i podobne narzędzia.
|
||||
|
||||
Bruno przechowuje twoje kolekcje bezpośrednio w folderze na twoim systemie plików. Używamy prostego języka znaczników, Bru, do zapisywania informacji o żądaniach API.
|
||||
|
||||
Możesz użyć Git lub dowolnego systemu kontroli wersji do współpracy nad swoimi kolekcjami API.
|
||||
|
||||
Bruno działa tylko w trybie offline. Nie planujemy nigdy dodawać synchronizacji w chmurze do Bruno. Cenimy prywatność Twoich danych i wierzymy, że powinny one pozostać na Twoim urządzeniu. Przeczytaj naszą długoterminową wizję [tutaj](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
📢 Obejrzyj naszą ostatnią rozmowę na konferencji India FOSS 3.0 [tutaj](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Instalacja
|
||||
|
||||
Bruno jest dostępny jako plik binarny do pobrania [na naszej stronie internetowej](https://www.usebruno.com/downloads) dla Mac, Windows i Linux.
|
||||
|
||||
Możesz również zainstalować Bruno za pomocą menedżerów pakietów, takich jak Homebrew, Chocolatey, Scoop, Snap i Apt.
|
||||
|
||||
```sh
|
||||
# On Mac via Homebrew
|
||||
brew install bruno
|
||||
|
||||
# On Windows via Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# On Windows via Scoop
|
||||
scoop bucket add extras
|
||||
scoop install bruno
|
||||
|
||||
# On Linux via Snap
|
||||
snap install bruno
|
||||
|
||||
# On Linux via Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Uruchom na wielu platformach 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Współpracuj przez Git 👩💻🧑💻
|
||||
|
||||
Lub dowolny inny system kontroli wersji, który wybierzesz
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Ważne Linki 📌
|
||||
|
||||
- [Nasza Długoterminowa Wizja](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Mapa Drogi](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Dokumentacja](https://docs.usebruno.com)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
|
||||
- [Strona Internetowa](https://www.usebruno.com)
|
||||
- [Cennik](https://www.usebruno.com/pricing)
|
||||
- [Pobieranie](https://www.usebruno.com/downloads)
|
||||
- [Sponsorzy Github](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### Zobacz 🎥
|
||||
|
||||
- [Opinie](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Centrum Wiedzy](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### Wsparcie ❤️
|
||||
|
||||
Jeśli podoba Ci się Bruno i chcesz wspierać naszą pracę opensource, rozważ sponsorowanie nas przez [Sponsorzy Github](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### Udostępnij Opinie 📣
|
||||
|
||||
Jeśli Bruno pomógł Tobie w pracy i Twoim zespołom, nie zapomnij podzielić się swoimi [opiniami na naszej dyskusji GitHub](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### Publikowanie w Nowych Menedżerach Pakietów
|
||||
|
||||
Więcej informacji znajdziesz [tutaj](publishing.md).
|
||||
|
||||
### Współpraca 👩💻🧑💻
|
||||
|
||||
Cieszę się, że chcesz udoskonalić bruno. Proszę sprawdź [przewodnik współpracy](contributing.md)
|
||||
|
||||
Nawet jeśli nie jesteś w stanie przyczynić się poprzez kod, nie wahaj się zgłaszać błędów i wniosków o funkcje, które muszą zostać zaimplementowane, aby rozwiązać Twój przypadek użycia.
|
||||
|
||||
### Autorzy
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Pozostań w kontakcie 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Strona Internetowa](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Znak Towarowy
|
||||
|
||||
**Nazwa**
|
||||
|
||||
`Bruno` jest znakiem towarowym należącym do [Anoop M D](https://www.helloanoop.com/)
|
||||
|
||||
**Logo**
|
||||
|
||||
Logo pochodzi z [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licencja: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Licencja 📄
|
||||
|
||||
[MIT](license.md)
|
||||
121
docs/readme/readme_pt_br.md
Normal file
121
docs/readme/readme_pt_br.md
Normal file
@@ -0,0 +1,121 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - IDE de código aberto para explorar e testar APIs.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
Bruno é um novo e inovador cliente de API, com o objetivo de revolucionar o status quo representado por ferramentas como o Postman e outras semelhantes.
|
||||
|
||||
Bruno armazena suas coleções diretamente em uma pasta no seu sistema de arquivos. Utilizamos uma linguagem de marcação de texto simples, chamada Bru, para salvar informações sobre requisições de API.
|
||||
|
||||
Você pode usar o Git ou qualquer sistema de controle de versão de sua escolha para colaborar em suas coleções de API.
|
||||
|
||||
Bruno é totalmente offline. Não há planos de adicionar sincronização em nuvem ao Bruno, nunca. Valorizamos a privacidade de seus dados e acreditamos que eles devem permanecer em seu dispositivo. Saiba mais sobre nossa visão a longo prazo [aqui](https://github.com/usebruno/bruno/discussions/269).
|
||||
|
||||
📢 Assista à nossa palestra recente na India FOSS 3.0 Conference [aqui](https://www.youtube.com/watch?v=7bSMFpbcPiY).
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Instalação
|
||||
|
||||
Bruno está disponível para download como binário [em nosso site](https://www.usebruno.com/downloads) para Mac, Windows e Linux.
|
||||
|
||||
Você também pode instalar o Bruno via gerenciadores de pacotes como Homebrew, Chocolatey, Snap e Apt.
|
||||
|
||||
```sh
|
||||
# Mac via Homebrew
|
||||
brew install bruno
|
||||
|
||||
# Windows via Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# Linux via Snap
|
||||
snap install bruno
|
||||
|
||||
# Linux via Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Execute em várias plataformas 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Colaboração via Git 👩💻🧑💻
|
||||
|
||||
Ou qualquer sistema de controle de versão de sua escolha.
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Links Importantes 📌
|
||||
|
||||
- [Nossa Visão de Longo Prazo](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Roadmap](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Documentação](https://docs.usebruno.com)
|
||||
- [Website](https://www.usebruno.com)
|
||||
- [Preços](https://www.usebruno.com/pricing)
|
||||
- [Download](https://www.usebruno.com/downloads)
|
||||
|
||||
### Showcase 🎥
|
||||
|
||||
- [Depoimentos](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Hub de Conhecimento](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### Apoie ❤️
|
||||
|
||||
Au-au! Se você gosta do projeto, clique no botão ⭐!!
|
||||
|
||||
### Compartilhe sua experiência 📣
|
||||
|
||||
Se o Bruno ajudou no seu trabalho e/ou no trabalho de sua equipe, por favor, não se esqueça de compartilhar seu [depoimento em nossas discussões no GitHub](https://github.com/usebruno/bruno/discussions/343).
|
||||
|
||||
### Publicando em Novos Gerenciadores de Pacotes
|
||||
|
||||
Por favor, verifique [aqui](../publishing/publishing_pt_br.md) mais informações.
|
||||
|
||||
### Colabore 👩💻🧑💻
|
||||
|
||||
Fico feliz que você queira melhorar o Bruno. Por favor, confira o [guia de colaboração](../contributing/contributing_pt_br.md).
|
||||
|
||||
Mesmo que você não possa contribuir codificando, não deixe de relatar problemas e solicitar recursos que precisam ser implementados para atender ao contexto de seu dia a dia.
|
||||
|
||||
### Authors
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Mantenha Contato 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Trademark
|
||||
|
||||
**Nome**
|
||||
|
||||
`Bruno` é uma marca registrada de [Anoop M D](https://www.helloanoop.com/).
|
||||
|
||||
**Logo**
|
||||
|
||||
A logo é original do [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licença: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
### Licença 📄
|
||||
|
||||
[MIT](license.md)
|
||||
125
docs/readme/readme_ro.md
Normal file
125
docs/readme/readme_ro.md
Normal file
@@ -0,0 +1,125 @@
|
||||
<br />
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - Mediu integrat de dezvoltare cu sursă deschisă pentru explorarea și testarea API-urilor.
|
||||
|
||||
[](https://badge.fury.io/gh/usebruno%bruno)
|
||||
[](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
|
||||
[](https://github.com/usebruno/bruno/pulse)
|
||||
[](https://twitter.com/use_bruno)
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](/readme.md) | [Українська](/docs/readme/readme_ua.md) | [Русский](/docs/readme/readme_ru.md) | [Türkçe](/docs/readme/readme_tr.md) | [Deutsch](/docs/readme/readme_de.md) | [Français](/docs/readme/readme_fr.md) | [Português (BR)](/docs/readme/readme_pt_br.md)) | [한국어](/docs/readme/readme_kr.md) | [বাংলা](/docs/readme/readme_bn.md) | [Español](/docs/readme/readme_es.md) | [Italiano](/docs/readme/readme_it.md) | **Română**
|
||||
|
||||
Bruno este un client API nou și inovativ, care vizează să revoluționeze status quo-ul reprezentat de Postman și alte instrumente similare.
|
||||
|
||||
Bruno salvează colecțiile voastre direct într-o mapă din sistemul dvs. de fișiere. Folosim un limbaj de marcare cu text simplu, Bru, pentru a salva informații despre cererile API.
|
||||
|
||||
Puteți folosi Git sau orice altă unealtă de control al versiunii la alegere pentru a colabora la colecțiile API voastre.
|
||||
|
||||
Bruno este numai offline. Nu va exista niciodată vreun plan pentru a adăuga sincronizarea cloud la Bruno. Noi valorăm confidențialitatea datelor voastre și credem că ar trebui să rămână pe dispozitivul vostru. Citiți viziunea noastră pe termen lung [aici](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
📢 Priviți prezentarea noastră recentă de la India FOSS 3.0 Conference [aici](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Instalarea
|
||||
|
||||
Bruno este disponibil ca descărcare binară [pe website-ul nostru](https://www.usebruno.com/downloads) pentru Mac, Windows și Linux.
|
||||
|
||||
De asemenea, puteţi instala Bruno cu un gestionar de pachete precum Homebrew, Chocolatey, Snap şi Apt.
|
||||
|
||||
```sh
|
||||
# Pe Mac cu Homebrew
|
||||
brew install bruno
|
||||
|
||||
# Pe Windows cu Chocolatey
|
||||
choco install bruno
|
||||
|
||||
# Pe Linux cu Snap
|
||||
snap install bruno
|
||||
|
||||
# Pe Linux cu Apt
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Utilizați pe mai multe platforme 🖥️
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Colaborați cu Git 👩💻🧑💻
|
||||
|
||||
Sau orice unealtă de control al versiunii la alegere
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Linkuri importante 📌
|
||||
|
||||
- [Viziunea noastră pe termen lung](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Roadmap](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Documentație](https://docs.usebruno.com)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
|
||||
- [Website](https://www.usebruno.com)
|
||||
- [Prețuri](https://www.usebruno.com/pricing)
|
||||
- [Descărcări](https://www.usebruno.com/downloads)
|
||||
- [Sponsori GitHub](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### Vitrina 🎥
|
||||
|
||||
- [Recenzii](https://github.com/usebruno/bruno/discussions/343)
|
||||
- [Centrul de cunoștințe](https://github.com/usebruno/bruno/discussions/386)
|
||||
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
|
||||
|
||||
### Sprijiniți ❤️
|
||||
|
||||
Dacă vă place Bruno și doriți să sprijiniți munca noastră de sursă deschisă, puteți considera să ne sponsorizați [pe GitHub](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### Distribuiți recenziile 📣
|
||||
|
||||
Dacă Bruno va ajutat la locul de muncă și la echipele dvs., vă rugăm să nu uitați să distribuiți [recenziile în discuția noastră GitHub](https://github.com/usebruno/bruno/discussions/343)
|
||||
|
||||
### Publicarea la gestionari de pachete noi
|
||||
|
||||
Vă rugăm să citiţi [aici](/docs/publishing/publishing_ro.md) pentru mai multă informaţie.
|
||||
|
||||
### Contribuiți 👩💻🧑💻
|
||||
|
||||
Mă bucur că doriți să îmbunătățiți Bruno. Vă rugăm să consultați [ghidul pentru contribuire](/docs/contributing/contributing_ro.md)
|
||||
|
||||
Chiar dacă nu puteți face contribuții prin cod, vă rugăm să nu ezitați să raportați erori și să solicitați funcții care trebuie implementate pentru a rezolva cazul dvs. de utilizare.
|
||||
|
||||
### Autori
|
||||
|
||||
<div align="center">
|
||||
<a href="https://github.com/usebruno/bruno/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=usebruno/bruno" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Păstrați legătura 🌐
|
||||
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Marcă comercială
|
||||
|
||||
**Nume**
|
||||
|
||||
`Bruno` este o marcă deținută de [Anoop M D](https://www.helloanoop.com/)
|
||||
|
||||
**Logo**
|
||||
|
||||
Logo-ul provine de la [OpenMoji](https://openmoji.org/library/emoji-1F436/). Licența: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Licența 📄
|
||||
|
||||
[MIT](license.md)
|
||||
@@ -1,5 +1,5 @@
|
||||
<br />
|
||||
<img src="assets/images/logo-transparent.png" width="80"/>
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - IDE с открытым исходным кодом для изучения и тестирования API.
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
|
||||
[English](/readme.md) | [Українська](/readme_ua.md) | **Русский** | [Türkçe](/readme_tr.md) | [Deutsch](/readme_de.md) | [Français](/readme_fr.md) | [বাংলা](docs/readme/readme_bn.md)
|
||||
|
||||
Bruno - новый и инновационный клиент API, направленный на революцию в установившейся ситуации, представленной Postman и подобными инструментами.
|
||||
|
||||
Bruno хранит ваши коллекции непосредственно в папке в вашей файловой системе. Для сохранения информации об API-запросах мы используем язык Bru.
|
||||
@@ -18,17 +21,17 @@ Bruno хранит ваши коллекции непосредственно в
|
||||
|
||||
Bruno работает только в автономном режиме. Добавление облачной синхронизации в Bruno не планируется. Мы ценим конфиденциальность ваших данных и считаем, что они должны оставаться на вашем устройстве. Ознакомьтесь с нашим долгосрочным видением [здесь](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Работа на нескольких платформах 🖥️
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Совместная работа через Git 👩💻🧑💻
|
||||
|
||||
Или другая система контроля версий по вашему выбору
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Важные ссылки 📌
|
||||
|
||||
@@ -74,4 +77,4 @@ Bruno работает только в автономном режиме. Доб
|
||||
|
||||
### Лицензия 📄
|
||||
|
||||
[MIT](license.md)
|
||||
[MIT](/license.md)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<br />
|
||||
<img src="assets/images/logo-transparent.png" width="80"/>
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - API'leri keşfetmek ve test etmek için açık kaynaklı IDE.
|
||||
|
||||
@@ -10,33 +10,70 @@
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](../../readme.md) | [Українська](docs/readme/readme_ua.md) | [Русский](docs/readme/readme_ru.md) | **Türkçe** | [Deutsch](docs/readme/readme_de.md) | [Français](docs/readme/readme_fr.md) | [Português (BR)](docs/readme/readme_pt_br.md) | [한국어](docs/readme/readme_kr.md) | [বাংলা](docs/readme/readme_bn.md) | [Español](docs/readme/readme_es.md) | [Italiano](docs/readme/readme_it.md) | [Română](docs/readme/readme_ro.md) | [Polski](docs/readme/readme_pl.md) | [简体中文](docs/readme/readme_cn.md)
|
||||
|
||||
Bruno, Postman ve benzeri araçlar tarafından temsil edilen statükoda devrim yaratmayı amaçlayan yeni ve yenilikçi bir API istemcisidir.
|
||||
|
||||
Bruno koleksiyonlarınızı doğrudan dosya sisteminizdeki bir klasörde saklar. API istekleri hakkındaki bilgileri kaydetmek için düz bir metin biçimlendirme dili olan Bru kullanıyoruz.
|
||||
|
||||
API koleksiyonlarınız üzerinde işbirliği yapmak için git veya seçtiğiniz herhangi bir sürüm kontrolünü kullanabilirsiniz.
|
||||
API koleksiyonlarınız üzerinde işbirliği yapmak için Git veya seçtiğiniz herhangi bir sürüm kontrolünü kullanabilirsiniz.
|
||||
|
||||
Bruno yalnızca çevrimdışıdır. Bruno'ya bulut senkronizasyonu eklemek gibi bir planımız yok. Veri gizliliğinize değer veriyoruz ve cihazınızda kalması gerektiğine inanıyoruz. Uzun vadeli vizyonumuzu okuyun [burada](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
 <br /><br />
|
||||
📢 Hindistan FOSS 3.0 Konferansındaki son konuşmamızı izleyin [burada](https://www.youtube.com/watch?v=7bSMFpbcPiY)
|
||||
|
||||
 <br /><br />
|
||||
|
||||
### Kurulum
|
||||
|
||||
Bruno Mac, Windows ve Linux için ikili indirme olarak [web sitemizde](https://www.usebruno.com/downloads) mevcuttur.
|
||||
|
||||
Bruno'yu Homebrew, Chocolatey, Scoop, Snap ve Apt gibi paket yöneticileri aracılığıyla da yükleyebilirsiniz.
|
||||
|
||||
```sh
|
||||
# Homebrew aracılığıyla Mac'te
|
||||
brew install bruno
|
||||
|
||||
# Chocolatey aracılığıyla Windows'ta
|
||||
choco install bruno
|
||||
|
||||
# Scoop aracılığıyla Windows'ta
|
||||
scoop bucket add extras
|
||||
scoop install bruno
|
||||
|
||||
# Snap aracılığıyla Linux'ta
|
||||
snap install bruno
|
||||
|
||||
# Apt aracılığıyla Linux'ta
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
sudo gpg --no-default-keyring --keyring /etc/apt/keyrings/bruno.gpg --keyserver keyserver.ubuntu.com --recv-keys 9FA6017ECABE0266
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/bruno.gpg] http://debian.usebruno.com/ bruno stable" | sudo tee /etc/apt/sources.list.d/bruno.list
|
||||
|
||||
sudo apt update
|
||||
sudo apt install bruno
|
||||
```
|
||||
|
||||
### Birden fazla platformda çalıştırın 🖥️
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Git üzerinden işbirliği yapın 👩💻🧑💻
|
||||
### Git üzerinden katkıda bulunun 👩💻🧑💻
|
||||
|
||||
Veya seçtiğiniz herhangi bir sürüm kontrol sistemi
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Önemli Bağlantılar 📌
|
||||
|
||||
- [Uzun Vadeli Vizyonumuz](https://github.com/usebruno/bruno/discussions/269)
|
||||
- [Yol Haritası](https://github.com/usebruno/bruno/discussions/384)
|
||||
- [Dokümantasyon](https://docs.usebruno.com)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
|
||||
- [Web sitesi](https://www.usebruno.com)
|
||||
- [Fiyatlandırma](https://www.usebruno.com/pricing)
|
||||
- [İndir](https://www.usebruno.com/downloads)
|
||||
- [Github Sponsorları](https://github.com/sponsors/helloanoop).
|
||||
|
||||
### Vitrin 🎥
|
||||
|
||||
@@ -46,15 +83,19 @@ Veya seçtiğiniz herhangi bir sürüm kontrol sistemi
|
||||
|
||||
### Destek ❤️
|
||||
|
||||
Woof! Projeyi beğendiyseniz, şu ⭐ düğmesine basın!
|
||||
Bruno'yu seviyorsanız ve açık kaynak çalışmalarımızı desteklemek istiyorsanız, [Github Sponsorları](https://github.com/sponsors/helloanoop) aracılığıyla bize sponsor olmayı düşünün.
|
||||
|
||||
### Referansları Paylaşın 📣
|
||||
|
||||
Bruno işinizde ve ekiplerinizde size yardımcı olduysa, lütfen [github tartışmamızdaki referanslarınızı](https://github.com/usebruno/bruno/discussions/343) paylaşmayı unutmayın
|
||||
Bruno işinizde ve ekiplerinizde size yardımcı olduysa, lütfen [github tartışmamızdaki referanslarınızı](https://github.com/usebruno/bruno/discussions/343) paylaşmayı unutmayın.
|
||||
|
||||
### Yeni Paket Yöneticilerine Yayınlama
|
||||
|
||||
Daha fazla bilgi için lütfen [buraya](publishing.md) bakın.
|
||||
|
||||
### Katkıda Bulunun 👩💻🧑💻
|
||||
|
||||
Bruno'yu geliştirmek istemenize sevindim. Lütfen [katkıda bulunma kılavuzu](../contributing/contributing.md)'na göz atın
|
||||
Bruno'yu geliştirmek istemenize sevindim. Lütfen [katkıda bulunma kılavuzuna](contributing.md) göz atın
|
||||
|
||||
Kod yoluyla katkıda bulunamasanız bile, lütfen kullanım durumunuzu çözmek için uygulanması gereken hataları ve özellik isteklerini bildirmekten çekinmeyin.
|
||||
|
||||
@@ -68,11 +109,21 @@ Kod yoluyla katkıda bulunamasanız bile, lütfen kullanım durumunuzu çözmek
|
||||
|
||||
### İletişimde Kalın 🌐
|
||||
|
||||
[Twitter](https://twitter.com/use_bruno) <br />
|
||||
[𝕏 (Twitter)](https://twitter.com/use_bruno) <br />
|
||||
[Website](https://www.usebruno.com) <br />
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq)
|
||||
[Discord](https://discord.com/invite/KgcZUncpjq) <br />
|
||||
[LinkedIn](https://www.linkedin.com/company/usebruno)
|
||||
|
||||
### Ticari Marka
|
||||
|
||||
**İsim**
|
||||
|
||||
`Bruno` [Anoop M D](https://www.helloanoop.com/) tarafından sahip olunan bir ticari markadır.
|
||||
|
||||
**Logo**
|
||||
|
||||
Logo [OpenMoji](https://openmoji.org/library/emoji-1F436/) adresinden alınmıştır. Lisans: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
### Lisans 📄
|
||||
|
||||
[MIT](license.md)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<br />
|
||||
<img src="assets/images/logo-transparent.png" width="80"/>
|
||||
<img src="../../assets/images/logo-transparent.png" width="80"/>
|
||||
|
||||
### Bruno - IDE із відкритим кодом для тестування та дослідження API
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
[](https://www.usebruno.com)
|
||||
[](https://www.usebruno.com/downloads)
|
||||
|
||||
[English](/readme.md) | **Українська** | [Русский](/readme_ru.md) | [Türkçe](/readme_tr.md) | [Deutsch](/readme_de.md) | [Français](/readme_fr.md) | [বাংলা](docs/readme/readme_bn.md)
|
||||
|
||||
Bruno це новий та іноваційний API клієнт, націлений на революційну зміну статус кво, запровадженого інструментами на кшталт Postman.
|
||||
|
||||
Bruno зберігає ваші колекції напряму у теці на вашому диску. Він використовує текстову мову розмітки Bru для збереження інформації про ваші API запити.
|
||||
@@ -18,17 +20,17 @@ Bruno зберігає ваші колекції напряму у теці на
|
||||
|
||||
Bruno є повністю автономним. Немає жодних планів додавати будь-які синхронізації через хмару, ніколи. Ми цінуємо приватність ваших даних, і вважаєм, що вони мають залишитись лише на вашому комп'ютері. Взнати більше про наше бачення у довготривалій перспективі можна [тут](https://github.com/usebruno/bruno/discussions/269)
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Кросплатформенність 🖥️
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Спільна робота через Git 👩💻🧑💻
|
||||
|
||||
Або будь-яку іншу систему контролю версій на ваш вибір
|
||||
|
||||
 <br /><br />
|
||||
 <br /><br />
|
||||
|
||||
### Важливі посилання 📌
|
||||
|
||||
@@ -75,4 +77,4 @@ Bruno є повністю автономним. Немає жодних план
|
||||
|
||||
### Ліцензія 📄
|
||||
|
||||
[MIT](license.md)
|
||||
[MIT](/license.md)
|
||||
|
||||
4660
package-lock.json
generated
4660
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,12 +5,13 @@
|
||||
"packages/bruno-app",
|
||||
"packages/bruno-electron",
|
||||
"packages/bruno-cli",
|
||||
"packages/bruno-tauri",
|
||||
"packages/bruno-common",
|
||||
"packages/bruno-schema",
|
||||
"packages/bruno-query",
|
||||
"packages/bruno-js",
|
||||
"packages/bruno-lang",
|
||||
"packages/bruno-testbench",
|
||||
"packages/bruno-toml",
|
||||
"packages/bruno-graphql-docs"
|
||||
],
|
||||
"homepage": "https://usebruno.com",
|
||||
@@ -18,18 +19,20 @@
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@jest/globals": "^29.2.0",
|
||||
"@playwright/test": "^1.27.1",
|
||||
"@types/jest": "^29.5.11",
|
||||
"fs-extra": "^11.1.1",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^29.2.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"randomstring": "^1.2.2",
|
||||
"ts-jest": "^29.0.5",
|
||||
"fs-extra": "^11.1.1"
|
||||
"ts-jest": "^29.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"dev:web": "npm run dev --workspace=packages/bruno-app",
|
||||
"build:web": "npm run build --workspace=packages/bruno-app",
|
||||
"prettier:web": "npm run prettier --workspace=packages/bruno-app",
|
||||
"dev:electron": "npm run dev --workspace=packages/bruno-electron",
|
||||
"build:bruno-common": "npm run build --workspace=packages/bruno-common",
|
||||
"build:bruno-query": "npm run build --workspace=packages/bruno-query",
|
||||
"build:graphql-docs": "npm run build --workspace=packages/bruno-graphql-docs",
|
||||
"build:electron": "node ./scripts/build-electron.js",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"target": "es2017",
|
||||
"allowSyntheticDefaultImports": false,
|
||||
"baseUrl": "./",
|
||||
|
||||
@@ -20,11 +20,13 @@
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@usebruno/graphql-docs": "0.1.0",
|
||||
"@usebruno/schema": "0.6.0",
|
||||
"axios": "^0.26.0",
|
||||
"axios": "^1.5.1",
|
||||
"classnames": "^2.3.1",
|
||||
"codemirror": "^5.65.2",
|
||||
"codemirror-graphql": "^1.2.5",
|
||||
"codemirror": "5.65.2",
|
||||
"codemirror-graphql": "1.2.5",
|
||||
"cookie": "^0.6.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"file": "^0.2.2",
|
||||
"file-dialog": "^0.0.8",
|
||||
"file-saver": "^2.0.5",
|
||||
"formik": "^2.2.9",
|
||||
@@ -36,6 +38,10 @@
|
||||
"httpsnippet": "^3.0.1",
|
||||
"idb": "^7.0.0",
|
||||
"immer": "^9.0.15",
|
||||
"jsesc": "^3.0.2",
|
||||
"jsonpath-plus": "^7.2.0",
|
||||
"jshint": "^2.13.6",
|
||||
"jsonlint": "^1.6.3",
|
||||
"know-your-http-well": "^0.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^13.0.2",
|
||||
@@ -43,22 +49,30 @@
|
||||
"nanoid": "3.3.4",
|
||||
"next": "12.3.3",
|
||||
"path": "^0.12.7",
|
||||
"pdfjs-dist": "^3.11.174",
|
||||
"platform": "^1.3.6",
|
||||
"posthog-node": "^2.1.0",
|
||||
"qs": "^6.11.0",
|
||||
"query-string": "^7.0.1",
|
||||
"react": "18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-github-btn": "^1.4.0",
|
||||
"react-hot-toast": "^2.4.0",
|
||||
"react-inspector": "^6.0.2",
|
||||
"react-pdf": "^7.5.1",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-tooltip": "^5.5.2",
|
||||
"sass": "^1.46.0",
|
||||
"strip-json-comments": "^5.0.1",
|
||||
"styled-components": "^5.3.3",
|
||||
"system": "^2.0.1",
|
||||
"tailwindcss": "^2.2.19",
|
||||
"url": "^0.11.3",
|
||||
"xml-formatter": "^3.5.0",
|
||||
"yargs-parser": "^21.1.1",
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -10,12 +10,89 @@ import isEqual from 'lodash/isEqual';
|
||||
import { getEnvironmentVariables } from 'utils/collections';
|
||||
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import jsonlint from 'jsonlint';
|
||||
import { JSHINT } from 'jshint';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
|
||||
let CodeMirror;
|
||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||
|
||||
if (!SERVER_RENDERED) {
|
||||
CodeMirror = require('codemirror');
|
||||
window.jsonlint = jsonlint;
|
||||
window.JSHINT = JSHINT;
|
||||
//This should be done dynamically if possible
|
||||
const hintWords = [
|
||||
'res',
|
||||
'res.status',
|
||||
'res.statusText',
|
||||
'res.headers',
|
||||
'res.body',
|
||||
'res.responseTime',
|
||||
'res.getStatus()',
|
||||
'res.getHeader(name)',
|
||||
'res.getHeaders()',
|
||||
'res.getBody()',
|
||||
'res.getResponseTime()',
|
||||
'req',
|
||||
'req.url',
|
||||
'req.method',
|
||||
'req.headers',
|
||||
'req.body',
|
||||
'req.timeout',
|
||||
'req.getUrl()',
|
||||
'req.setUrl(url)',
|
||||
'req.getMethod()',
|
||||
'req.setMethod(method)',
|
||||
'req.getHeader(name)',
|
||||
'req.getHeaders()',
|
||||
'req.setHeader(name, value)',
|
||||
'req.setHeaders(data)',
|
||||
'req.getBody()',
|
||||
'req.setBody(data)',
|
||||
'req.setMaxRedirects(maxRedirects)',
|
||||
'req.getTimeout()',
|
||||
'req.setTimeout(timeout)',
|
||||
'bru',
|
||||
'bru.cwd()',
|
||||
'bru.getEnvName(key)',
|
||||
'bru.getProcessEnv(key)',
|
||||
'bru.getEnvVar(key)',
|
||||
'bru.setEnvVar(key,value)',
|
||||
'bru.getVar(key)',
|
||||
'bru.setVar(key,value)',
|
||||
'bru.setNextRequest(requestName)'
|
||||
];
|
||||
CodeMirror.registerHelper('hint', 'brunoJS', (editor, options) => {
|
||||
const cursor = editor.getCursor();
|
||||
const currentLine = editor.getLine(cursor.line);
|
||||
let startBru = cursor.ch;
|
||||
let endBru = startBru;
|
||||
while (endBru < currentLine.length && /[\w.]/.test(currentLine.charAt(endBru))) ++endBru;
|
||||
while (startBru && /[\w.]/.test(currentLine.charAt(startBru - 1))) --startBru;
|
||||
let curWordBru = startBru != endBru && currentLine.slice(startBru, endBru);
|
||||
|
||||
let start = cursor.ch;
|
||||
let end = start;
|
||||
while (end < currentLine.length && /[\w]/.test(currentLine.charAt(end))) ++end;
|
||||
while (start && /[\w]/.test(currentLine.charAt(start - 1))) --start;
|
||||
const jsHinter = CodeMirror.hint.javascript;
|
||||
let result = jsHinter(editor) || { list: [] };
|
||||
result.to = CodeMirror.Pos(cursor.line, end);
|
||||
result.from = CodeMirror.Pos(cursor.line, start);
|
||||
if (curWordBru) {
|
||||
hintWords.forEach((h) => {
|
||||
if (h.includes('.') == curWordBru.includes('.') && h.startsWith(curWordBru)) {
|
||||
result.list.push(curWordBru.includes('.') ? h.split('.')[1] : h);
|
||||
}
|
||||
});
|
||||
result.list?.sort();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
CodeMirror.commands.autocomplete = (cm, hint, options) => {
|
||||
cm.showHint({ hint, ...options });
|
||||
};
|
||||
}
|
||||
|
||||
export default class CodeEditor extends React.Component {
|
||||
@@ -27,6 +104,12 @@ export default class CodeEditor extends React.Component {
|
||||
// unnecessary updates during the update lifecycle.
|
||||
this.cachedValue = props.value || '';
|
||||
this.variables = {};
|
||||
|
||||
this.lintOptions = {
|
||||
esversion: 11,
|
||||
expr: true,
|
||||
asi: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -41,7 +124,8 @@ export default class CodeEditor extends React.Component {
|
||||
matchBrackets: true,
|
||||
showCursorWhenSelecting: true,
|
||||
foldGutter: true,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
|
||||
lint: this.lintOptions,
|
||||
readOnly: this.props.readOnly,
|
||||
scrollbarStyle: 'overlay',
|
||||
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
|
||||
@@ -68,9 +152,16 @@ export default class CodeEditor extends React.Component {
|
||||
},
|
||||
'Cmd-F': 'findPersistent',
|
||||
'Ctrl-F': 'findPersistent',
|
||||
'Cmd-H': 'replace',
|
||||
'Ctrl-H': 'replace',
|
||||
Tab: function (cm) {
|
||||
cm.replaceSelection(' ', 'end');
|
||||
cm.getSelection().includes('\n') || editor.getLine(cm.getCursor().line) == cm.getSelection()
|
||||
? cm.execCommand('indentMore')
|
||||
: cm.replaceSelection(' ', 'end');
|
||||
},
|
||||
'Shift-Tab': 'indentLess',
|
||||
'Ctrl-Space': 'autocomplete',
|
||||
'Cmd-Space': 'autocomplete',
|
||||
'Ctrl-Y': 'foldAll',
|
||||
'Cmd-Y': 'foldAll',
|
||||
'Ctrl-I': 'unfoldAll',
|
||||
@@ -102,10 +193,53 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
}));
|
||||
CodeMirror.registerHelper('lint', 'json', function (text) {
|
||||
let found = [];
|
||||
if (!window.jsonlint) {
|
||||
if (window.console) {
|
||||
window.console.error('Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.');
|
||||
}
|
||||
return found;
|
||||
}
|
||||
let jsonlint = window.jsonlint.parser || window.jsonlint;
|
||||
jsonlint.parseError = function (str, hash) {
|
||||
let loc = hash.loc;
|
||||
found.push({
|
||||
from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
|
||||
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
|
||||
message: str
|
||||
});
|
||||
};
|
||||
try {
|
||||
jsonlint.parse(stripJsonComments(text.replace(/(?<!"[^":{]*){{[^}]*}}(?![^"},]*")/g, '1')));
|
||||
} catch (e) {}
|
||||
return found;
|
||||
});
|
||||
if (editor) {
|
||||
editor.setOption('lint', this.props.mode && editor.getValue().trim().length > 0 ? this.lintOptions : false);
|
||||
editor.on('change', this._onEdit);
|
||||
this.addOverlay();
|
||||
}
|
||||
if (this.props.mode == 'javascript') {
|
||||
editor.on('keyup', function (cm, event) {
|
||||
const cursor = editor.getCursor();
|
||||
const currentLine = editor.getLine(cursor.line);
|
||||
let start = cursor.ch;
|
||||
let end = start;
|
||||
while (end < currentLine.length && /[^{}();\s\[\]\,]/.test(currentLine.charAt(end))) ++end;
|
||||
while (start && /[^{}();\s\[\]\,]/.test(currentLine.charAt(start - 1))) --start;
|
||||
let curWord = start != end && currentLine.slice(start, end);
|
||||
//Qualify if autocomplete will be shown
|
||||
if (
|
||||
/^(?!Shift|Tab|Enter|ArrowUp|ArrowDown|ArrowLeft|ArrowRight|\s)\w*/.test(event.key) &&
|
||||
curWord.length > 0 &&
|
||||
!/\/\/|\/\*|.*{{|`[^$]*{|`[^{]*$/.test(currentLine.slice(0, end)) &&
|
||||
/(?<!\d)[a-zA-Z\._]$/.test(curWord)
|
||||
) {
|
||||
CodeMirror.commands.autocomplete(cm, CodeMirror.hint.brunoJS, { completeSingle: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -146,6 +280,9 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.editor) {
|
||||
this.editor.refresh();
|
||||
}
|
||||
return (
|
||||
<StyledWrapper
|
||||
className="h-full w-full"
|
||||
@@ -169,6 +306,7 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
_onEdit = () => {
|
||||
if (!this.ignoreChangeEvent && this.editor) {
|
||||
this.editor.setOption('lint', this.editor.getValue().trim().length > 0 ? this.lintOptions : false);
|
||||
this.cachedValue = this.editor.getValue();
|
||||
if (this.props.onEdit) {
|
||||
this.props.onEdit(this.cachedValue);
|
||||
|
||||
@@ -61,6 +61,15 @@ const AuthMode = ({ collection }) => {
|
||||
>
|
||||
Bearer Token
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onModeChange('digest');
|
||||
}}
|
||||
>
|
||||
Digest Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@@ -0,0 +1,71 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateCollectionAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const DigestAuth = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const digestAuth = get(collection, 'root.request.auth.digest', {});
|
||||
|
||||
const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
|
||||
|
||||
const handleUsernameChange = (username) => {
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'digest',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
username: username,
|
||||
password: digestAuth.password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (password) => {
|
||||
dispatch(
|
||||
updateCollectionAuth({
|
||||
mode: 'digest',
|
||||
collectionUid: collection.uid,
|
||||
content: {
|
||||
username: digestAuth.username,
|
||||
password: password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Username</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={digestAuth.username || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleUsernameChange(val)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Password</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={digestAuth.password || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handlePasswordChange(val)}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigestAuth;
|
||||
@@ -5,6 +5,7 @@ import AuthMode from './AuthMode';
|
||||
import AwsV4Auth from './AwsV4Auth';
|
||||
import BearerAuth from './BearerAuth';
|
||||
import BasicAuth from './BasicAuth';
|
||||
import DigestAuth from './DigestAuth';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
@@ -25,6 +26,9 @@ const Auth = ({ collection }) => {
|
||||
case 'bearer': {
|
||||
return <BearerAuth collection={collection} />;
|
||||
}
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import get from 'lodash/get';
|
||||
import { updateCollectionDocs } from 'providers/ReduxStore/slices/collections';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import Markdown from 'components/MarkDown';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
@@ -14,6 +14,7 @@ const Docs = ({ collection }) => {
|
||||
const { storedTheme } = useTheme();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const docs = get(collection, 'root.docs', '');
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
|
||||
const toggleViewMode = () => {
|
||||
setIsEditing((prev) => !prev);
|
||||
@@ -44,6 +45,7 @@ const Docs = ({ collection }) => {
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
mode="application/text"
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
/>
|
||||
) : (
|
||||
<Markdown onDoubleClick={toggleViewMode} content={docs} />
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
table {
|
||||
td {
|
||||
&:first-child {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
function countRequests(items) {
|
||||
let count = 0;
|
||||
@@ -20,9 +20,9 @@ function countRequests(items) {
|
||||
return count;
|
||||
}
|
||||
|
||||
const CollectionProperties = ({ collection, onClose }) => {
|
||||
const Info = ({ collection }) => {
|
||||
return (
|
||||
<Modal size="sm" title="Collection Properties" hideFooter={true} handleCancel={onClose}>
|
||||
<StyledWrapper className="w-full flex flex-col h-full">
|
||||
<table className="w-full border-collapse">
|
||||
<tbody>
|
||||
<tr className="">
|
||||
@@ -43,8 +43,8 @@ const CollectionProperties = ({ collection, onClose }) => {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollectionProperties;
|
||||
export default Info;
|
||||
@@ -0,0 +1,27 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.settings-label {
|
||||
width: 110px;
|
||||
}
|
||||
|
||||
.textbox {
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.15rem 0.45rem;
|
||||
box-shadow: none;
|
||||
border-radius: 0px;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
transition: border-color ease-in-out 0.1s;
|
||||
border-radius: 3px;
|
||||
background-color: ${(props) => props.theme.modal.input.bg};
|
||||
border: 1px solid ${(props) => props.theme.modal.input.border};
|
||||
|
||||
&:focus {
|
||||
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,96 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import toast from 'react-hot-toast';
|
||||
import { updateBrunoConfig } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
const PresetsSettings = ({ collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
brunoConfig: { presets: presets = {} }
|
||||
} = collection;
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
requestType: presets.requestType || 'http',
|
||||
requestUrl: presets.requestUrl || ''
|
||||
},
|
||||
onSubmit: (newPresets) => {
|
||||
const brunoConfig = cloneDeep(collection.brunoConfig);
|
||||
brunoConfig.presets = newPresets;
|
||||
dispatch(updateBrunoConfig(brunoConfig, collection.uid));
|
||||
toast.success('Collection presets updated');
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<h1 className="font-medium mb-3">Collection Presets</h1>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div className="mb-3 flex items-center">
|
||||
<label className="settings-label flex items-center" htmlFor="enabled">
|
||||
Request Type
|
||||
</label>
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
id="http"
|
||||
className="cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="http"
|
||||
checked={formik.values.requestType === 'http'}
|
||||
/>
|
||||
<label htmlFor="http" className="ml-1 cursor-pointer select-none">
|
||||
HTTP
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="graphql"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="graphql"
|
||||
checked={formik.values.requestType === 'graphql'}
|
||||
/>
|
||||
<label htmlFor="graphql" className="ml-1 cursor-pointer select-none">
|
||||
GraphQL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3 flex items-center">
|
||||
<label className="settings-label" htmlFor="requestUrl">
|
||||
Base URL
|
||||
</label>
|
||||
<div className="flex items-center">
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
<input
|
||||
id="request-url"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
className="block textbox"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.requestUrl || ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default PresetsSettings;
|
||||
@@ -11,22 +11,20 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
|
||||
protocol: Yup.string().oneOf(['http', 'https', 'socks4', 'socks5']),
|
||||
hostname: Yup.string()
|
||||
.when('enabled', {
|
||||
is: true,
|
||||
is: 'true',
|
||||
then: (hostname) => hostname.required('Specify the hostname for your proxy.'),
|
||||
otherwise: (hostname) => hostname.nullable()
|
||||
})
|
||||
.max(1024),
|
||||
port: Yup.number()
|
||||
.when('enabled', {
|
||||
is: true,
|
||||
then: (port) => port.required('Specify port between 1 and 65535').typeError('Specify port between 1 and 65535'),
|
||||
otherwise: (port) => port.nullable().transform((_, val) => (val ? Number(val) : null))
|
||||
})
|
||||
.min(1)
|
||||
.max(65535),
|
||||
.max(65535)
|
||||
.typeError('Specify port between 1 and 65535')
|
||||
.nullable()
|
||||
.transform((_, val) => (val ? Number(val) : null)),
|
||||
auth: Yup.object()
|
||||
.when('enabled', {
|
||||
is: true,
|
||||
is: 'true',
|
||||
then: Yup.object({
|
||||
enabled: Yup.boolean(),
|
||||
username: Yup.string()
|
||||
@@ -166,7 +164,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
http
|
||||
HTTP
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -177,7 +175,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
https
|
||||
HTTPS
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -188,7 +186,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
socks4
|
||||
SOCKS4
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -199,7 +197,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
socks5
|
||||
SOCKS5
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
import { updateCollectionRequestScript, updateCollectionResponseScript } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
@@ -13,6 +13,7 @@ const Script = ({ collection }) => {
|
||||
const responseScript = get(collection, 'root.request.script.res', '');
|
||||
|
||||
const { storedTheme } = useTheme();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
|
||||
const onRequestScriptEdit = (value) => {
|
||||
dispatch(
|
||||
@@ -47,6 +48,7 @@ const Script = ({ collection }) => {
|
||||
onEdit={onRequestScriptEdit}
|
||||
mode="javascript"
|
||||
onSave={handleSave}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 mt-6">
|
||||
@@ -58,6 +60,7 @@ const Script = ({ collection }) => {
|
||||
onEdit={onResponseScriptEdit}
|
||||
mode="javascript"
|
||||
onSave={handleSave}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
import { updateCollectionTests } from 'providers/ReduxStore/slices/collections';
|
||||
import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
@@ -12,6 +12,7 @@ const Tests = ({ collection }) => {
|
||||
const tests = get(collection, 'root.request.tests', '');
|
||||
|
||||
const { storedTheme } = useTheme();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
|
||||
const onEdit = (value) => {
|
||||
dispatch(
|
||||
@@ -33,6 +34,7 @@ const Tests = ({ collection }) => {
|
||||
onEdit={onEdit}
|
||||
mode="javascript"
|
||||
onSave={handleSave}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
/>
|
||||
|
||||
<div className="mt-6">
|
||||
|
||||
@@ -13,6 +13,8 @@ import Auth from './Auth';
|
||||
import Script from './Script';
|
||||
import Test from './Tests';
|
||||
import Docs from './Docs';
|
||||
import Presets from './Presets';
|
||||
import Info from './Info';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const CollectionSettings = ({ collection }) => {
|
||||
@@ -84,6 +86,9 @@ const CollectionSettings = ({ collection }) => {
|
||||
case 'tests': {
|
||||
return <Test collection={collection} />;
|
||||
}
|
||||
case 'presets': {
|
||||
return <Presets collection={collection} />;
|
||||
}
|
||||
case 'proxy': {
|
||||
return <ProxySettings proxyConfig={proxyConfig} onUpdate={onProxySettingsUpdate} />;
|
||||
}
|
||||
@@ -99,6 +104,9 @@ const CollectionSettings = ({ collection }) => {
|
||||
case 'docs': {
|
||||
return <Docs collection={collection} />;
|
||||
}
|
||||
case 'info': {
|
||||
return <Info collection={collection} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,6 +131,9 @@ const CollectionSettings = ({ collection }) => {
|
||||
<div className={getTabClassname('tests')} role="tab" onClick={() => setTab('tests')}>
|
||||
Tests
|
||||
</div>
|
||||
<div className={getTabClassname('presets')} role="tab" onClick={() => setTab('presets')}>
|
||||
Presets
|
||||
</div>
|
||||
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
|
||||
Proxy
|
||||
</div>
|
||||
@@ -132,6 +143,9 @@ const CollectionSettings = ({ collection }) => {
|
||||
<div className={getTabClassname('docs')} role="tab" onClick={() => setTab('docs')}>
|
||||
Docs
|
||||
</div>
|
||||
<div className={getTabClassname('info')} role="tab" onClick={() => setTab('info')}>
|
||||
Info
|
||||
</div>
|
||||
</div>
|
||||
<section className={`flex ${['auth', 'script', 'docs', 'clientCert'].includes(tab) ? '' : 'mt-4'}`}>
|
||||
{getTabPanel(tab)}
|
||||
|
||||
16
packages/bruno-app/src/components/Cookies/StyledWrapper.js
Normal file
16
packages/bruno-app/src/components/Cookies/StyledWrapper.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
|
||||
thead {
|
||||
color: ${(props) => props.theme.table.thead.color};
|
||||
font-size: 0.8125rem;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
54
packages/bruno-app/src/components/Cookies/index.js
Normal file
54
packages/bruno-app/src/components/Cookies/index.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import Modal from 'components/Modal';
|
||||
import { IconTrash } from '@tabler/icons';
|
||||
import { deleteCookiesForDomain } from 'providers/ReduxStore/slices/app';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const CollectionProperties = ({ onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const cookies = useSelector((state) => state.app.cookies) || [];
|
||||
|
||||
const handleDeleteDomain = (domain) => {
|
||||
dispatch(deleteCookiesForDomain(domain))
|
||||
.then(() => {
|
||||
toast.success('Domain deleted successfully');
|
||||
})
|
||||
.catch((err) => console.log(err) && toast.error('Failed to delete domain'));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal size="md" title="Cookies" hideFooter={true} handleCancel={onClose}>
|
||||
<StyledWrapper>
|
||||
<table className="w-full border-collapse" style={{ marginTop: '-1rem' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="py-2 px-2 text-left">Domain</th>
|
||||
<th className="py-2 px-2 text-left">Cookie</th>
|
||||
<th className="py-2 px-2 text-center" style={{ width: 80 }}>
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{cookies.map((cookie) => (
|
||||
<tr key={cookie.domain}>
|
||||
<td className="py-2 px-2">{cookie.domain}</td>
|
||||
<td className="py-2 px-2 break-all">{cookie.cookieString}</td>
|
||||
<td className="text-center">
|
||||
<button tabIndex="-1" onClick={() => handleDeleteDomain(cookie.domain)}>
|
||||
<IconTrash strokeWidth={1.5} size={20} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</StyledWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CollectionProperties;
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'github-markdown-css/github-markdown.css';
|
||||
import get from 'lodash/get';
|
||||
import { updateRequestDocs } from 'providers/ReduxStore/slices/collections';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import Markdown from 'components/MarkDown';
|
||||
import CodeEditor from 'components/CodeEditor';
|
||||
@@ -14,6 +14,7 @@ const Documentation = ({ item, collection }) => {
|
||||
const { storedTheme } = useTheme();
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const docs = item.draft ? get(item, 'draft.request.docs') : get(item, 'request.docs');
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
|
||||
const toggleViewMode = () => {
|
||||
setIsEditing((prev) => !prev);
|
||||
@@ -45,6 +46,7 @@ const Documentation = ({ item, collection }) => {
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={storedTheme}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
value={docs || ''}
|
||||
onEdit={onEdit}
|
||||
onSave={onSave}
|
||||
|
||||
@@ -10,6 +10,7 @@ import StyledWrapper from './StyledWrapper';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { uuid } from 'utils/common';
|
||||
import { variableNameRegex } from 'utils/common/regex';
|
||||
|
||||
const EnvironmentVariables = ({ environment, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -23,12 +24,15 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
enabled: Yup.boolean(),
|
||||
name: Yup.string()
|
||||
.required('Name cannot be empty')
|
||||
.matches(/^(?!\d)\w*$/, 'Name contains invalid characters')
|
||||
.matches(
|
||||
variableNameRegex,
|
||||
'Name contains invalid characters. Must only contain alphanumeric characters, "-", "_", "." and cannot start with a digit.'
|
||||
)
|
||||
.trim(),
|
||||
secret: Yup.boolean(),
|
||||
type: Yup.string(),
|
||||
uid: Yup.string(),
|
||||
value: Yup.string().trim()
|
||||
value: Yup.string().trim().nullable()
|
||||
})
|
||||
),
|
||||
onSubmit: (values) => {
|
||||
@@ -48,7 +52,6 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
|
||||
const ErrorMessage = ({ name }) => {
|
||||
const meta = formik.getFieldMeta(name);
|
||||
console.log(name, meta);
|
||||
if (!meta.error) {
|
||||
return null;
|
||||
}
|
||||
@@ -110,7 +113,7 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
className="mousetrap"
|
||||
id={`${index}.name`}
|
||||
name={`${index}.name`}
|
||||
value={formik.values[index].name}
|
||||
value={variable.name}
|
||||
onChange={formik.handleChange}
|
||||
/>
|
||||
<ErrorMessage name={`${index}.name`} />
|
||||
|
||||
@@ -71,6 +71,14 @@ const StyledMarkdownBodyWrapper = styled.div`
|
||||
pre {
|
||||
background: ${(props) => props.theme.sidebar.bg};
|
||||
}
|
||||
|
||||
table {
|
||||
th,
|
||||
td {
|
||||
border: 1px solid ${(props) => props.theme.table.border};
|
||||
background-color: ${(props) => props.theme.bg};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import * as React from 'react';
|
||||
|
||||
const md = new MarkdownIt();
|
||||
|
||||
const Markdown = ({ onDoubleClick, content }) => {
|
||||
const handleOnDoubleClick = (event) => {
|
||||
switch (event.detail) {
|
||||
case 2: {
|
||||
onDoubleClick();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
if (event?.detail === 2) {
|
||||
onDoubleClick();
|
||||
}
|
||||
};
|
||||
const htmlFromMarkdown = md.render(content || '');
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ModalHeader = ({ title, handleCancel }) => (
|
||||
@@ -62,6 +62,9 @@ const Modal = ({
|
||||
confirmDisabled,
|
||||
hideCancel,
|
||||
hideFooter,
|
||||
disableCloseOnOutsideClick,
|
||||
disableEscapeKey,
|
||||
onClick,
|
||||
closeModalFadeTimeout = 500
|
||||
}) => {
|
||||
const [isClosing, setIsClosing] = useState(false);
|
||||
@@ -78,12 +81,13 @@ const Modal = ({
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (disableEscapeKey) return;
|
||||
document.addEventListener('keydown', escFunction, false);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', escFunction, false);
|
||||
};
|
||||
}, []);
|
||||
}, [disableEscapeKey, document]);
|
||||
|
||||
let classes = 'bruno-modal';
|
||||
if (isClosing) {
|
||||
@@ -93,7 +97,7 @@ const Modal = ({
|
||||
classes += ' modal-footer-none';
|
||||
}
|
||||
return (
|
||||
<StyledWrapper className={classes}>
|
||||
<StyledWrapper className={classes} onClick={onClick ? (e) => onClick(e) : null}>
|
||||
<div className={`bruno-modal-card modal-${size}`}>
|
||||
<ModalHeader title={title} handleCancel={() => closeModal({ type: 'icon' })} />
|
||||
<ModalContent>{children}</ModalContent>
|
||||
@@ -111,9 +115,13 @@ const Modal = ({
|
||||
{/* Clicking on backdrop closes the modal */}
|
||||
<div
|
||||
className="bruno-modal-backdrop"
|
||||
onClick={() => {
|
||||
closeModal({ type: 'backdrop' });
|
||||
}}
|
||||
onClick={
|
||||
disableCloseOnOutsideClick
|
||||
? null
|
||||
: () => {
|
||||
closeModal({ type: 'backdrop' });
|
||||
}
|
||||
}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useFormik } from 'formik';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { savePreferences } from 'providers/ReduxStore/slices/app';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import * as Yup from 'yup';
|
||||
import toast from 'react-hot-toast';
|
||||
import path from 'path';
|
||||
import slash from 'utils/common/slash';
|
||||
import { IconTrash } from '@tabler/icons';
|
||||
|
||||
const General = ({ close }) => {
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const dispatch = useDispatch();
|
||||
const inputFileCaCertificateRef = useRef();
|
||||
|
||||
const preferencesSchema = Yup.object().shape({
|
||||
sslVerification: Yup.boolean(),
|
||||
customCaCertificate: Yup.object({
|
||||
enabled: Yup.boolean(),
|
||||
filePath: Yup.string().nullable()
|
||||
}),
|
||||
storeCookies: Yup.boolean(),
|
||||
sendCookies: Yup.boolean(),
|
||||
timeout: Yup.mixed()
|
||||
.transform((value, originalValue) => {
|
||||
return originalValue === '' ? undefined : value;
|
||||
@@ -28,7 +39,13 @@ const General = ({ close }) => {
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
sslVerification: preferences.request.sslVerification,
|
||||
timeout: preferences.request.timeout
|
||||
customCaCertificate: {
|
||||
enabled: get(preferences, 'request.customCaCertificate.enabled', false),
|
||||
filePath: get(preferences, 'request.customCaCertificate.filePath', null)
|
||||
},
|
||||
timeout: preferences.request.timeout,
|
||||
storeCookies: get(preferences, 'request.storeCookies', true),
|
||||
sendCookies: get(preferences, 'request.sendCookies', true)
|
||||
},
|
||||
validationSchema: preferencesSchema,
|
||||
onSubmit: async (values) => {
|
||||
@@ -47,7 +64,13 @@ const General = ({ close }) => {
|
||||
...preferences,
|
||||
request: {
|
||||
sslVerification: newPreferences.sslVerification,
|
||||
timeout: newPreferences.timeout
|
||||
customCaCertificate: {
|
||||
enabled: newPreferences.customCaCertificate.enabled,
|
||||
filePath: newPreferences.customCaCertificate.filePath
|
||||
},
|
||||
timeout: newPreferences.timeout,
|
||||
storeCookies: newPreferences.storeCookies,
|
||||
sendCookies: newPreferences.sendCookies
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -57,24 +80,112 @@ const General = ({ close }) => {
|
||||
.catch((err) => console.log(err) && toast.error('Failed to update preferences'));
|
||||
};
|
||||
|
||||
const addCaCertificate = (e) => {
|
||||
formik.setFieldValue('customCaCertificate.filePath', e.target.files[0]?.path);
|
||||
};
|
||||
|
||||
const deleteCaCertificate = () => {
|
||||
formik.setFieldValue('customCaCertificate.filePath', null);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div className="flex items-center mt-2">
|
||||
<label className="block font-medium mr-2 select-none" style={{ minWidth: 200 }} htmlFor="sslVerification">
|
||||
SSL/TLS Certificate Verification
|
||||
</label>
|
||||
<input
|
||||
id="ssl-cert-verification"
|
||||
id="sslVerification"
|
||||
type="checkbox"
|
||||
name="sslVerification"
|
||||
checked={formik.values.sslVerification}
|
||||
onChange={formik.handleChange}
|
||||
className="mousetrap mr-0"
|
||||
/>
|
||||
<label className="block ml-2 select-none" htmlFor="sslVerification">
|
||||
SSL/TLS Certificate Verification
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center mt-2">
|
||||
<input
|
||||
id="customCaCertificateEnabled"
|
||||
type="checkbox"
|
||||
name="customCaCertificate.enabled"
|
||||
checked={formik.values.customCaCertificate.enabled}
|
||||
onChange={formik.handleChange}
|
||||
className="mousetrap mr-0"
|
||||
/>
|
||||
<label className="block ml-2 select-none" htmlFor="customCaCertificateEnabled">
|
||||
Use custom CA Certificate
|
||||
</label>
|
||||
</div>
|
||||
{formik.values.customCaCertificate.filePath ? (
|
||||
<div
|
||||
className={`flex items-center mt-2 pl-6 ${formik.values.customCaCertificate.enabled ? '' : 'opacity-25'}`}
|
||||
>
|
||||
<span className="flex items-center border px-2 rounded-md">
|
||||
{path.basename(slash(formik.values.customCaCertificate.filePath))}
|
||||
<button
|
||||
type="button"
|
||||
tabIndex="-1"
|
||||
className="pl-1"
|
||||
disabled={formik.values.customCaCertificate.enabled ? false : true}
|
||||
onClick={deleteCaCertificate}
|
||||
>
|
||||
<IconTrash strokeWidth={1.5} size={14} />
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={`flex items-center mt-2 pl-6 ${formik.values.customCaCertificate.enabled ? '' : 'opacity-25'}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
tabIndex="-1"
|
||||
className="flex items-center border px-2 rounded-md"
|
||||
disabled={formik.values.customCaCertificate.enabled ? false : true}
|
||||
onClick={() => inputFileCaCertificateRef.current.click()}
|
||||
>
|
||||
select file
|
||||
<input
|
||||
id="caCertFilePath"
|
||||
type="file"
|
||||
name="customCaCertificate.filePath"
|
||||
className="hidden"
|
||||
ref={inputFileCaCertificateRef}
|
||||
disabled={formik.values.customCaCertificate.enabled ? false : true}
|
||||
onChange={addCaCertificate}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center mt-2">
|
||||
<input
|
||||
id="storeCookies"
|
||||
type="checkbox"
|
||||
name="storeCookies"
|
||||
checked={formik.values.storeCookies}
|
||||
onChange={formik.handleChange}
|
||||
className="mousetrap mr-0"
|
||||
/>
|
||||
<label className="block ml-2 select-none" htmlFor="storeCookies">
|
||||
Store Cookies automatically
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center mt-2">
|
||||
<input
|
||||
id="sendCookies"
|
||||
type="checkbox"
|
||||
name="sendCookies"
|
||||
checked={formik.values.sendCookies}
|
||||
onChange={formik.handleChange}
|
||||
className="mousetrap mr-0"
|
||||
/>
|
||||
<label className="block ml-2 select-none" htmlFor="sendCookies">
|
||||
Send Cookies automatically
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-col mt-6">
|
||||
<label className="block font-medium select-none" htmlFor="timeout">
|
||||
<label className="block select-none" htmlFor="timeout">
|
||||
Request Timeout (in ms)
|
||||
</label>
|
||||
<input
|
||||
|
||||
@@ -22,13 +22,11 @@ const ProxySettings = ({ close }) => {
|
||||
})
|
||||
.max(1024),
|
||||
port: Yup.number()
|
||||
.when('enabled', {
|
||||
is: true,
|
||||
then: (port) => port.required('Specify port between 1 and 65535').typeError('Specify port between 1 and 65535'),
|
||||
otherwise: (port) => port.nullable().transform((_, val) => (val ? Number(val) : null))
|
||||
})
|
||||
.min(1)
|
||||
.max(65535),
|
||||
.max(65535)
|
||||
.typeError('Specify port between 1 and 65535')
|
||||
.nullable()
|
||||
.transform((_, val) => (val ? Number(val) : null)),
|
||||
auth: Yup.object()
|
||||
.when('enabled', {
|
||||
is: true,
|
||||
@@ -129,7 +127,7 @@ const ProxySettings = ({ close }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
http
|
||||
HTTP
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -140,7 +138,7 @@ const ProxySettings = ({ close }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
https
|
||||
HTTPS
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -151,7 +149,7 @@ const ProxySettings = ({ close }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
socks4
|
||||
SOCKS4
|
||||
</label>
|
||||
<label className="flex items-center ml-4">
|
||||
<input
|
||||
@@ -162,7 +160,7 @@ const ProxySettings = ({ close }) => {
|
||||
onChange={formik.handleChange}
|
||||
className="mr-1"
|
||||
/>
|
||||
socks5
|
||||
SOCKS5
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@ const Theme = () => {
|
||||
theme: storedTheme
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
theme: Yup.string().oneOf(['light', 'dark']).required('theme is required')
|
||||
theme: Yup.string().oneOf(['light', 'dark', 'system']).required('theme is required')
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
setStoredTheme(values.theme);
|
||||
@@ -55,6 +55,22 @@ const Theme = () => {
|
||||
<label htmlFor="dark-theme" className="ml-1 cursor-pointer select-none">
|
||||
Dark
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="system-theme"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="theme"
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
formik.handleSubmit();
|
||||
}}
|
||||
value="system"
|
||||
checked={formik.values.theme === 'system'}
|
||||
/>
|
||||
<label htmlFor="system-theme" className="ml-1 cursor-pointer select-none">
|
||||
System
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -62,6 +62,15 @@ const AuthMode = ({ item, collection }) => {
|
||||
>
|
||||
Bearer Token
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
onModeChange('digest');
|
||||
}}
|
||||
>
|
||||
Digest Auth
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.single-line-editor-wrapper {
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border: solid 1px ${(props) => props.theme.input.border};
|
||||
background-color: ${(props) => props.theme.input.bg};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import { updateAuth } from 'providers/ReduxStore/slices/collections';
|
||||
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const DigestAuth = ({ item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const digestAuth = item.draft ? get(item, 'draft.request.auth.digest', {}) : get(item, 'request.auth.digest', {});
|
||||
|
||||
const handleRun = () => dispatch(sendRequest(item, collection.uid));
|
||||
const handleSave = () => dispatch(saveRequest(item.uid, collection.uid));
|
||||
|
||||
const handleUsernameChange = (username) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'digest',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
username: username,
|
||||
password: digestAuth.password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handlePasswordChange = (password) => {
|
||||
dispatch(
|
||||
updateAuth({
|
||||
mode: 'digest',
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item.uid,
|
||||
content: {
|
||||
username: digestAuth.username,
|
||||
password: password
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-2 w-full">
|
||||
<label className="block font-medium mb-2">Username</label>
|
||||
<div className="single-line-editor-wrapper mb-2">
|
||||
<SingleLineEditor
|
||||
value={digestAuth.username || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handleUsernameChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="block font-medium mb-2">Password</label>
|
||||
<div className="single-line-editor-wrapper">
|
||||
<SingleLineEditor
|
||||
value={digestAuth.password || ''}
|
||||
theme={storedTheme}
|
||||
onSave={handleSave}
|
||||
onChange={(val) => handlePasswordChange(val)}
|
||||
onRun={handleRun}
|
||||
collection={collection}
|
||||
/>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigestAuth;
|
||||
@@ -4,6 +4,7 @@ import AuthMode from './AuthMode';
|
||||
import AwsV4Auth from './AwsV4Auth';
|
||||
import BearerAuth from './BearerAuth';
|
||||
import BasicAuth from './BasicAuth';
|
||||
import DigestAuth from './DigestAuth';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const Auth = ({ item, collection }) => {
|
||||
@@ -20,6 +21,9 @@ const Auth = ({ item, collection }) => {
|
||||
case 'bearer': {
|
||||
return <BearerAuth collection={collection} item={item} />;
|
||||
}
|
||||
case 'digest': {
|
||||
return <DigestAuth collection={collection} item={item} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useRef, forwardRef } from 'react';
|
||||
import useGraphqlSchema from './useGraphqlSchema';
|
||||
import { IconBook, IconDownload, IconLoader2, IconCheckmark } from '@tabler/icons';
|
||||
import { IconBook, IconDownload, IconLoader2, IconRefresh } from '@tabler/icons';
|
||||
import get from 'lodash/get';
|
||||
import { findEnvironmentInCollection } from 'utils/collections';
|
||||
import Dropdown from '../../Dropdown';
|
||||
@@ -30,8 +30,8 @@ const GraphQLSchemaActions = ({ item, collection, onSchemaLoad, toggleDocs }) =>
|
||||
return (
|
||||
<div ref={ref} className="dropdown-icon cursor-pointer flex hover:underline ml-2">
|
||||
{isSchemaLoading && <IconLoader2 className="animate-spin" size={18} strokeWidth={1.5} />}
|
||||
{!isSchemaLoading && schema && <IconDownload size={18} strokeWidth={1.5} />}
|
||||
{!isSchemaLoading && !schema && <IconCheckmark size={18} strokeWidth={1.5} />}
|
||||
{!isSchemaLoading && schema && <IconRefresh size={18} strokeWidth={1.5} />}
|
||||
{!isSchemaLoading && !schema && <IconDownload size={18} strokeWidth={1.5} />}
|
||||
<span className="ml-1">Schema</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
|
||||
requestUrlChanged({
|
||||
itemUid: item.uid,
|
||||
collectionUid: collection.uid,
|
||||
url: value
|
||||
url: value && typeof value === 'string' ? value.trim() : value
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import toast from 'react-hot-toast';
|
||||
import { variableNameRegex } from 'utils/common/regex';
|
||||
|
||||
const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -32,13 +33,10 @@ const VarsTable = ({ item, collection, vars, varType }) => {
|
||||
case 'name': {
|
||||
const value = e.target.value;
|
||||
|
||||
if (/^(?!\d).*$/.test(value) === false) {
|
||||
toast.error('Variable names must not start with a number!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (/^\w*$/.test(value) === false) {
|
||||
toast.error('Variable contains invalid character! Variables must only contain alpha-numeric characters.');
|
||||
if (variableNameRegex.test(value) === false) {
|
||||
toast.error(
|
||||
'Variable contains invalid characters! Variables must only contain alpha-numeric characters, "-", "_", "."'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ const CollectionToolBar = ({ collection }) => {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="flex items-center p-2">
|
||||
<div className="flex flex-1 items-center">
|
||||
<div className="flex flex-1 items-center cursor-pointer hover:underline" onClick={viewCollectionSettings}>
|
||||
<IconFiles size={18} strokeWidth={1.5} />
|
||||
<span className="ml-2 mr-4 font-semibold">{collection.name}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
import React from 'react';
|
||||
import { IconAlertTriangle } from '@tabler/icons';
|
||||
import Modal from 'components/Modal';
|
||||
|
||||
const ConfirmRequestClose = ({ onCancel, onCloseWithoutSave, onSaveAndClose }) => {
|
||||
const _handleCancel = ({ type }) => {
|
||||
if (type === 'button') {
|
||||
return onCloseWithoutSave();
|
||||
}
|
||||
|
||||
return onCancel();
|
||||
};
|
||||
|
||||
const ConfirmRequestClose = ({ item, onCancel, onCloseWithoutSave, onSaveAndClose }) => {
|
||||
return (
|
||||
<Modal
|
||||
size="sm"
|
||||
size="md"
|
||||
title="Unsaved changes"
|
||||
confirmText="Save and Close"
|
||||
cancelText="Close without saving"
|
||||
handleConfirm={onSaveAndClose}
|
||||
handleCancel={_handleCancel}
|
||||
disableEscapeKey={true}
|
||||
disableCloseOnOutsideClick={true}
|
||||
closeModalFadeTimeout={150}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
hideFooter={true}
|
||||
>
|
||||
<div className="font-normal">You have unsaved changes in you request.</div>
|
||||
<div className="flex items-center font-normal">
|
||||
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
|
||||
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
|
||||
</div>
|
||||
<div className="font-normal mt-4">
|
||||
You have unsaved changes in request <span className="font-semibold">{item.name}</span>.
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between mt-6">
|
||||
<div>
|
||||
<button className="btn btn-sm btn-danger" onClick={onCloseWithoutSave}>
|
||||
Don't Save
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button className="btn btn-close btn-sm mr-2" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
<button className="btn btn-secondary btn-sm" onClick={onSaveAndClose}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,15 +3,15 @@ import get from 'lodash/get';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
import { saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { deleteRequestDraft } from 'providers/ReduxStore/slices/collections';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { findItemInCollection } from 'utils/collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import RequestTabNotFound from './RequestTabNotFound';
|
||||
import ConfirmRequestClose from './ConfirmRequestClose';
|
||||
import SpecialTab from './SpecialTab';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import darkTheme from 'themes/dark';
|
||||
import lightTheme from 'themes/light';
|
||||
import { findItemInCollection } from 'utils/collections';
|
||||
import ConfirmRequestClose from './ConfirmRequestClose';
|
||||
import RequestTabNotFound from './RequestTabNotFound';
|
||||
import SpecialTab from './SpecialTab';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const RequestTab = ({ tab, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -92,6 +92,7 @@ const RequestTab = ({ tab, collection }) => {
|
||||
<StyledWrapper className="flex items-center justify-between tab-container px-1">
|
||||
{showConfirmClose && (
|
||||
<ConfirmRequestClose
|
||||
item={item}
|
||||
onCancel={() => setShowConfirmClose(false)}
|
||||
onCloseWithoutSave={() => {
|
||||
dispatch(
|
||||
@@ -136,6 +137,8 @@ const RequestTab = ({ tab, collection }) => {
|
||||
onClick={(e) => {
|
||||
if (!item.draft) return handleCloseClick(e);
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
setShowConfirmClose(true);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { IconFilter } from '@tabler/icons';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Tooltip as ReactTooltip } from 'react-tooltip';
|
||||
|
||||
const QueryResultFilter = ({ onChange, mode }) => {
|
||||
const tooltipText = useMemo(() => {
|
||||
if (mode.includes('json')) {
|
||||
return 'Filter with JSONPath';
|
||||
}
|
||||
|
||||
if (mode.includes('xml')) {
|
||||
return 'Filter with XPath';
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [mode]);
|
||||
|
||||
return (
|
||||
<div className={'response-filter relative'}>
|
||||
<div className="absolute inset-y-0 left-0 pl-4 flex items-center">
|
||||
<div className="text-gray-500 sm:text-sm" id="request-filter-icon">
|
||||
<IconFilter size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{tooltipText && <ReactTooltip anchorId={'request-filter-icon'} html={tooltipText} />}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
name="response-filter"
|
||||
id="response-filter"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
className="block w-full pl-10 py-1 sm:text-sm"
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default QueryResultFilter;
|
||||
@@ -2,6 +2,11 @@ import CodeEditor from 'components/CodeEditor/index';
|
||||
import { get } from 'lodash';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { Document, Page } from 'react-pdf';
|
||||
import { useState } from 'react';
|
||||
import 'pdfjs-dist/build/pdf.worker';
|
||||
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
|
||||
import 'react-pdf/dist/esm/Page/TextLayer.css';
|
||||
|
||||
const QueryResultPreview = ({
|
||||
previewTab,
|
||||
@@ -19,6 +24,10 @@ const QueryResultPreview = ({
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [numPages, setNumPages] = useState(null);
|
||||
function onDocumentLoadSuccess({ numPages }) {
|
||||
setNumPages(numPages);
|
||||
}
|
||||
// Fail safe, so we don't render anything with an invalid tab
|
||||
if (!allowedPreviewModes.includes(previewTab)) {
|
||||
return null;
|
||||
@@ -45,6 +54,17 @@ const QueryResultPreview = ({
|
||||
case 'preview-image': {
|
||||
return <img src={`data:${contentType.replace(/\;(.*)/, '')};base64,${dataBuffer}`} className="mx-auto" />;
|
||||
}
|
||||
case 'preview-pdf': {
|
||||
return (
|
||||
<div className="preview-pdf" style={{ height: '100%', overflow: 'auto', maxHeight: 'calc(100vh - 220px)' }}>
|
||||
<Document file={`data:application/pdf;base64,${dataBuffer}`} onLoadSuccess={onDocumentLoadSuccess}>
|
||||
{Array.from(new Array(numPages), (el, index) => (
|
||||
<Page key={`page_${index + 1}`} pageNumber={index + 1} renderAnnotationLayer={false} />
|
||||
))}
|
||||
</Document>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
default:
|
||||
case 'raw': {
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
||||
const StyledWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 1.25rem calc(100% - 1.25rem);
|
||||
grid-template-rows: ${(props) => (props.queryFilterEnabled ? '1.25rem 1fr 2.25rem' : '1.25rem 1fr')};
|
||||
|
||||
/* This is a hack to force Codemirror to use all available space */
|
||||
> div {
|
||||
@@ -18,6 +18,19 @@ const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.react-pdf__Page {
|
||||
margin-top: 10px;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.react-pdf__Page__textContent {
|
||||
border: 1px solid darkgrey;
|
||||
box-shadow: 5px 5px 5px 1px #ccc;
|
||||
border-radius: 0px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.react-pdf__Page__canvas {
|
||||
margin: 0 auto;
|
||||
}
|
||||
div[role='tablist'] {
|
||||
.active {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
@@ -27,6 +40,22 @@ const StyledWrapper = styled.div`
|
||||
.muted {
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
}
|
||||
|
||||
.response-filter {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
border: ${(props) => props.theme.sidebar.search.border};
|
||||
border-radius: 2px;
|
||||
background-color: ${(props) => props.theme.sidebar.search.bg};
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { debounce } from 'lodash';
|
||||
import QueryResultFilter from './QueryResultFilter';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { getContentType, safeStringifyJSON, safeParseXML } from 'utils/common';
|
||||
@@ -10,12 +13,20 @@ import { useMemo } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
|
||||
const formatResponse = (data, mode) => {
|
||||
if (!data) {
|
||||
const formatResponse = (data, mode, filter) => {
|
||||
if (data === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (mode.includes('json')) {
|
||||
if (filter) {
|
||||
try {
|
||||
data = JSONPath({ path: filter, json: data });
|
||||
} catch (e) {
|
||||
console.warn('Could not filter with JSONPath.', e.message);
|
||||
}
|
||||
}
|
||||
|
||||
return safeStringifyJSON(data, true);
|
||||
}
|
||||
|
||||
@@ -28,7 +39,7 @@ const formatResponse = (data, mode) => {
|
||||
return safeStringifyJSON(parsed, true);
|
||||
}
|
||||
|
||||
if (['text', 'html'].includes(mode) || typeof data === 'string') {
|
||||
if (typeof data === 'string') {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -37,10 +48,15 @@ const formatResponse = (data, mode) => {
|
||||
|
||||
const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEventListener, headers, error }) => {
|
||||
const contentType = getContentType(headers);
|
||||
const mode = getCodeMirrorModeBasedOnContentType(contentType);
|
||||
const formattedData = formatResponse(data, mode);
|
||||
const mode = getCodeMirrorModeBasedOnContentType(contentType, data);
|
||||
const [filter, setFilter] = useState(null);
|
||||
const formattedData = formatResponse(data, mode, filter);
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
const debouncedResultFilterOnChange = debounce((e) => {
|
||||
setFilter(e.target.value);
|
||||
}, 250);
|
||||
|
||||
const allowedPreviewModes = useMemo(() => {
|
||||
// Always show raw
|
||||
const allowedPreviewModes = ['raw'];
|
||||
@@ -49,6 +65,8 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven
|
||||
allowedPreviewModes.unshift('preview-web');
|
||||
} else if (mode.includes('image')) {
|
||||
allowedPreviewModes.unshift('preview-image');
|
||||
} else if (contentType.includes('pdf')) {
|
||||
allowedPreviewModes.unshift('preview-pdf');
|
||||
}
|
||||
|
||||
return allowedPreviewModes;
|
||||
@@ -79,8 +97,14 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven
|
||||
));
|
||||
}, [allowedPreviewModes, previewTab]);
|
||||
|
||||
const queryFilterEnabled = useMemo(() => mode.includes('json'), [mode]);
|
||||
|
||||
return (
|
||||
<StyledWrapper className="w-full h-full" style={{ maxWidth: width }}>
|
||||
<StyledWrapper
|
||||
className="w-full h-full relative"
|
||||
style={{ maxWidth: width }}
|
||||
queryFilterEnabled={queryFilterEnabled}
|
||||
>
|
||||
<div className="flex justify-end gap-2 text-xs" role="tablist">
|
||||
{tabs}
|
||||
</div>
|
||||
@@ -96,19 +120,22 @@ const QueryResult = ({ item, collection, data, dataBuffer, width, disableRunEven
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<QueryResultPreview
|
||||
previewTab={previewTab}
|
||||
data={data}
|
||||
dataBuffer={dataBuffer}
|
||||
formattedData={formattedData}
|
||||
item={item}
|
||||
contentType={contentType}
|
||||
mode={mode}
|
||||
collection={collection}
|
||||
allowedPreviewModes={allowedPreviewModes}
|
||||
disableRunEventListener={disableRunEventListener}
|
||||
storedTheme={storedTheme}
|
||||
/>
|
||||
<>
|
||||
<QueryResultPreview
|
||||
previewTab={previewTab}
|
||||
data={data}
|
||||
dataBuffer={dataBuffer}
|
||||
formattedData={formattedData}
|
||||
item={item}
|
||||
contentType={contentType}
|
||||
mode={mode}
|
||||
collection={collection}
|
||||
allowedPreviewModes={allowedPreviewModes}
|
||||
disableRunEventListener={disableRunEventListener}
|
||||
storedTheme={storedTheme}
|
||||
/>
|
||||
{queryFilterEnabled && <QueryResultFilter onChange={debouncedResultFilterOnChange} mode={mode} />}
|
||||
</>
|
||||
)}
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
font-size: 0.8125rem;
|
||||
color: ${(props) => props.theme.requestTabPanel.responseStatus};
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { IconEraser } from '@tabler/icons';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { responseCleared } from 'providers/ReduxStore/slices/collections/index';
|
||||
|
||||
const ResponseClear = ({ collection, item }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const clearResponse = () =>
|
||||
dispatch(
|
||||
responseCleared({
|
||||
itemUid: item.uid,
|
||||
collectionUid: collection.uid,
|
||||
response: null
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledWrapper className="ml-2 flex items-center">
|
||||
<button onClick={clearResponse} title="Clear response">
|
||||
<IconEraser size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
export default ResponseClear;
|
||||
@@ -10,7 +10,6 @@ const ResponseSave = ({ item }) => {
|
||||
|
||||
const saveResponseToFile = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(item);
|
||||
ipcRenderer
|
||||
.invoke('renderer:save-response-to-file', response, item.requestSent.url)
|
||||
.then(resolve)
|
||||
@@ -22,7 +21,7 @@ const ResponseSave = ({ item }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper className="ml-4 flex items-center">
|
||||
<StyledWrapper className="ml-2 flex items-center">
|
||||
<button onClick={saveResponseToFile} disabled={!response.dataBuffer} title="Save response to file">
|
||||
<IconDownload size={16} strokeWidth={1.5} />
|
||||
</button>
|
||||
|
||||
@@ -15,6 +15,7 @@ import TestResults from './TestResults';
|
||||
import TestResultsLabel from './TestResultsLabel';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import ResponseSave from 'src/components/ResponsePane/ResponseSave';
|
||||
import ResponseClear from 'src/components/ResponsePane/ResponseClear';
|
||||
|
||||
const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -104,6 +105,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
</div>
|
||||
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
|
||||
Headers
|
||||
{response.headers?.length > 0 && <sup className="ml-1 font-medium">{response.headers.length}</sup>}
|
||||
</div>
|
||||
<div className={getTabClassname('timeline')} role="tab" onClick={() => selectTab('timeline')}>
|
||||
Timeline
|
||||
@@ -113,6 +115,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
</div>
|
||||
{!isLoading ? (
|
||||
<div className="flex flex-grow justify-end items-center">
|
||||
<ResponseClear item={item} collection={collection} />
|
||||
<ResponseSave item={item} />
|
||||
<StatusCode status={response.status} />
|
||||
<ResponseTime duration={response.duration} />
|
||||
|
||||
@@ -15,9 +15,9 @@ import StyledWrapper from './StyledWrapper';
|
||||
const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
const [selectedTab, setSelectedTab] = useState('response');
|
||||
|
||||
const { requestSent, responseReceived, testResults } = item;
|
||||
const { requestSent, responseReceived, testResults, assertionResults } = item;
|
||||
|
||||
const headers = get(item, 'responseReceived.headers', {});
|
||||
const headers = get(item, 'responseReceived.headers', []);
|
||||
const status = get(item, 'responseReceived.status', 0);
|
||||
const size = get(item, 'responseReceived.size', 0);
|
||||
const duration = get(item, 'responseReceived.duration', 0);
|
||||
@@ -47,7 +47,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
return <Timeline request={requestSent} response={responseReceived} />;
|
||||
}
|
||||
case 'tests': {
|
||||
return <TestResults results={testResults} />;
|
||||
return <TestResults results={testResults} assertionResults={assertionResults} />;
|
||||
}
|
||||
|
||||
default: {
|
||||
@@ -70,12 +70,13 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
</div>
|
||||
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
|
||||
Headers
|
||||
{headers?.length > 0 && <sup className="ml-1 font-medium">{headers.length}</sup>}
|
||||
</div>
|
||||
<div className={getTabClassname('timeline')} role="tab" onClick={() => selectTab('timeline')}>
|
||||
Timeline
|
||||
</div>
|
||||
<div className={getTabClassname('tests')} role="tab" onClick={() => selectTab('tests')}>
|
||||
<TestResultsLabel results={testResults} />
|
||||
<TestResultsLabel results={testResults} assertionResults={assertionResults} />
|
||||
</div>
|
||||
<div className="flex flex-grow justify-end items-center">
|
||||
<StatusCode status={status} />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import path from 'path';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { get, each, cloneDeep } from 'lodash';
|
||||
import { get, cloneDeep } from 'lodash';
|
||||
import { runCollectionFolder } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections';
|
||||
import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections';
|
||||
@@ -31,35 +31,39 @@ export default function RunnerResults({ collection }) {
|
||||
}, [collection, setSelectedItem]);
|
||||
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
const items = cloneDeep(get(collection, 'runnerResult.items', []));
|
||||
const runnerInfo = get(collection, 'runnerResult.info', {});
|
||||
each(items, (item) => {
|
||||
const info = findItemInCollection(collectionCopy, item.uid);
|
||||
|
||||
item.name = info.name;
|
||||
item.type = info.type;
|
||||
item.filename = info.filename;
|
||||
item.pathname = info.pathname;
|
||||
item.relativePath = getRelativePath(collection.pathname, info.pathname);
|
||||
|
||||
if (item.status !== 'error') {
|
||||
if (item.testResults) {
|
||||
const failed = item.testResults.filter((result) => result.status === 'fail');
|
||||
|
||||
item.testStatus = failed.length ? 'fail' : 'pass';
|
||||
} else {
|
||||
item.testStatus = 'pass';
|
||||
const items = cloneDeep(get(collection, 'runnerResult.items', []))
|
||||
.map((item) => {
|
||||
const info = findItemInCollection(collectionCopy, item.uid);
|
||||
if (!info) {
|
||||
return null;
|
||||
}
|
||||
const newItem = {
|
||||
...item,
|
||||
name: info.name,
|
||||
type: info.type,
|
||||
filename: info.filename,
|
||||
pathname: info.pathname,
|
||||
relativePath: getRelativePath(collection.pathname, info.pathname)
|
||||
};
|
||||
if (newItem.status !== 'error') {
|
||||
if (newItem.testResults) {
|
||||
const failed = newItem.testResults.filter((result) => result.status === 'fail');
|
||||
newItem.testStatus = failed.length ? 'fail' : 'pass';
|
||||
} else {
|
||||
newItem.testStatus = 'pass';
|
||||
}
|
||||
|
||||
if (item.assertionResults) {
|
||||
const failed = item.assertionResults.filter((result) => result.status === 'fail');
|
||||
|
||||
item.assertionStatus = failed.length ? 'fail' : 'pass';
|
||||
} else {
|
||||
item.assertionStatus = 'pass';
|
||||
if (newItem.assertionResults) {
|
||||
const failed = newItem.assertionResults.filter((result) => result.status === 'fail');
|
||||
newItem.assertionStatus = failed.length ? 'fail' : 'pass';
|
||||
} else {
|
||||
newItem.assertionStatus = 'pass';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return newItem;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
const runCollection = () => {
|
||||
dispatch(runCollectionFolder(collection.uid, null, true));
|
||||
@@ -109,12 +113,12 @@ export default function RunnerResults({ collection }) {
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="px-4">
|
||||
<StyledWrapper className="px-4 pb-4 flex flex-grow flex-col relative">
|
||||
<div className="font-medium mt-6 mb-4 title flex items-center">
|
||||
Runner
|
||||
<IconRun size={20} strokeWidth={1.5} className="ml-2" />
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-1">
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="py-2 font-medium test-summary">
|
||||
Total Requests: {items.length}, Passed: {passedRequests.length}, Failed: {failedRequests.length}
|
||||
@@ -168,26 +172,24 @@ export default function RunnerResults({ collection }) {
|
||||
</li>
|
||||
))
|
||||
: null}
|
||||
{item.assertionResults
|
||||
? item.assertionResults.map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
<IconCheck size={18} strokeWidth={2} className="mr-2" />
|
||||
{result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span className="test-failure flex items-center">
|
||||
<IconX size={18} strokeWidth={2} className="mr-2" />
|
||||
{result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
<span className="error-message pl-8 text-xs">{result.error}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
: null}
|
||||
{item.assertionResults?.map((result) => (
|
||||
<li key={result.uid}>
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success flex items-center">
|
||||
<IconCheck size={18} strokeWidth={2} className="mr-2" />
|
||||
{result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span className="test-failure flex items-center">
|
||||
<IconX size={18} strokeWidth={2} className="mr-2" />
|
||||
{result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
<span className="error-message pl-8 text-xs">{result.error}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,155 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { cloneCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import toast from 'react-hot-toast';
|
||||
import Tooltip from 'components/Tooltip';
|
||||
import Modal from 'components/Modal';
|
||||
|
||||
const CloneCollection = ({ onClose, collection }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
collectionName: '',
|
||||
collectionFolderName: '',
|
||||
collectionLocation: ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
collectionName: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(50, 'must be 50 characters or less')
|
||||
.required('collection name is required'),
|
||||
collectionFolderName: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(50, 'must be 50 characters or less')
|
||||
.matches(/^[\w\-. ]+$/, 'Folder name contains invalid characters')
|
||||
.required('folder name is required'),
|
||||
collectionLocation: Yup.string().min(1, 'location is required').required('location is required')
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
dispatch(
|
||||
cloneCollection(
|
||||
values.collectionName,
|
||||
values.collectionFolderName,
|
||||
values.collectionLocation,
|
||||
collection.pathname
|
||||
)
|
||||
)
|
||||
.then(() => {
|
||||
toast.success('Collection created');
|
||||
onClose();
|
||||
})
|
||||
.catch(() => toast.error('An error occurred while creating the collection'));
|
||||
}
|
||||
});
|
||||
|
||||
const browse = () => {
|
||||
dispatch(browseDirectory())
|
||||
.then((dirPath) => {
|
||||
// When the user closes the diolog without selecting anything dirPath will be false
|
||||
if (typeof dirPath === 'string') {
|
||||
formik.setFieldValue('collectionLocation', dirPath);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
formik.setFieldValue('collectionLocation', '');
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [inputRef]);
|
||||
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
|
||||
return (
|
||||
<Modal size="sm" title="Clone Collection" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="collection-name" className="flex items-center font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-name"
|
||||
type="text"
|
||||
name="collectionName"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
if (formik.values.collectionName === formik.values.collectionFolderName) {
|
||||
formik.setFieldValue('collectionFolderName', e.target.value);
|
||||
}
|
||||
}}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionName || ''}
|
||||
/>
|
||||
{formik.touched.collectionName && formik.errors.collectionName ? (
|
||||
<div className="text-red-500">{formik.errors.collectionName}</div>
|
||||
) : null}
|
||||
|
||||
<label htmlFor="collection-location" className="block font-semibold mt-3">
|
||||
Location
|
||||
</label>
|
||||
<input
|
||||
id="collection-location"
|
||||
type="text"
|
||||
name="collectionLocation"
|
||||
readOnly={true}
|
||||
className="block textbox mt-2 w-full cursor-pointer"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionLocation || ''}
|
||||
onClick={browse}
|
||||
/>
|
||||
{formik.touched.collectionLocation && formik.errors.collectionLocation ? (
|
||||
<div className="text-red-500">{formik.errors.collectionLocation}</div>
|
||||
) : null}
|
||||
<div className="mt-1">
|
||||
<span className="text-link cursor-pointer hover:underline" onClick={browse}>
|
||||
Browse
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<label htmlFor="collection-folder-name" className="flex items-center mt-3">
|
||||
<span className="font-semibold">Folder Name</span>
|
||||
<Tooltip
|
||||
text="This folder will be created under the selected location"
|
||||
tooltipId="collection-folder-name-tooltip"
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
id="collection-folder-name"
|
||||
type="text"
|
||||
name="collectionFolderName"
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={formik.handleChange}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionFolderName || ''}
|
||||
/>
|
||||
{formik.touched.collectionFolderName && formik.errors.collectionFolderName ? (
|
||||
<div className="text-red-500">{formik.errors.collectionFolderName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloneCollection;
|
||||
@@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
position: relative;
|
||||
|
||||
.copy-to-clipboard {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -2,30 +2,54 @@ import CodeEditor from 'components/CodeEditor/index';
|
||||
import get from 'lodash/get';
|
||||
import { HTTPSnippet } from 'httpsnippet';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { buildHarRequest } from 'utils/codegenerator/har';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import toast from 'react-hot-toast';
|
||||
import { IconCopy } from '@tabler/icons';
|
||||
import { findCollectionByItemUid } from '../../../../../../../utils/collections/index';
|
||||
|
||||
const CodeView = ({ language, item }) => {
|
||||
const { storedTheme } = useTheme();
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
const { target, client, language: lang } = language;
|
||||
const requestHeaders = item.draft ? get(item, 'draft.request.headers') : get(item, 'request.headers');
|
||||
const collection = findCollectionByItemUid(
|
||||
useSelector((state) => state.collections.collections),
|
||||
item.uid
|
||||
);
|
||||
|
||||
const headers = [...(collection?.root?.request?.headers || []), ...(requestHeaders || [])];
|
||||
|
||||
let snippet = '';
|
||||
|
||||
try {
|
||||
snippet = new HTTPSnippet(buildHarRequest(item.request)).convert(target, client);
|
||||
snippet = new HTTPSnippet(buildHarRequest({ request: item.request, headers })).convert(target, client);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
snippet = 'Error generating code snippet';
|
||||
}
|
||||
|
||||
return (
|
||||
<CodeEditor
|
||||
readOnly
|
||||
value={snippet}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
theme={storedTheme}
|
||||
mode={lang}
|
||||
/>
|
||||
<>
|
||||
<StyledWrapper>
|
||||
<CopyToClipboard
|
||||
className="copy-to-clipboard"
|
||||
text={snippet}
|
||||
onCopy={() => toast.success('Copied to clipboard!')}
|
||||
>
|
||||
<IconCopy size={25} strokeWidth={1.5} />
|
||||
</CopyToClipboard>
|
||||
<CodeEditor
|
||||
readOnly
|
||||
value={snippet}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
theme={storedTheme}
|
||||
mode={lang}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ const Wrapper = styled.div`
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&:hover,
|
||||
&.item-hovered {
|
||||
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
|
||||
.menu-icon {
|
||||
.dropdown {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useSelector, useDispatch } from 'react-redux';
|
||||
import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs';
|
||||
import { collectionFolderClicked } from 'providers/ReduxStore/slices/collections';
|
||||
import { moveItem } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import NewRequest from 'components/Sidebar/NewRequest';
|
||||
import NewFolder from 'components/Sidebar/NewFolder';
|
||||
@@ -23,6 +24,7 @@ import { getDefaultRequestPaneTab } from 'utils/collections';
|
||||
import { hideHomePage } from 'providers/ReduxStore/slices/app';
|
||||
import toast from 'react-hot-toast';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import NetworkError from 'components/ResponsePane/NetworkError/index';
|
||||
|
||||
const CollectionItem = ({ item, collection, searchText }) => {
|
||||
const tabs = useSelector((state) => state.tabs.tabs);
|
||||
@@ -84,7 +86,8 @@ const CollectionItem = ({ item, collection, searchText }) => {
|
||||
});
|
||||
|
||||
const itemRowClassName = classnames('flex collection-item-name items-center', {
|
||||
'item-focused-in-tab': item.uid == activeTabUid
|
||||
'item-focused-in-tab': item.uid == activeTabUid,
|
||||
'item-hovered': isOver
|
||||
});
|
||||
|
||||
const scrollToTheActiveTab = () => {
|
||||
@@ -94,6 +97,14 @@ const CollectionItem = ({ item, collection, searchText }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleRun = async () => {
|
||||
dispatch(sendRequest(item, collection.uid)).catch((err) =>
|
||||
toast.custom((t) => <NetworkError onClose={() => toast.dismiss(t.id)} />, {
|
||||
duration: 5000
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleClick = (event) => {
|
||||
//scroll to the active tab
|
||||
setTimeout(scrollToTheActiveTab, 50);
|
||||
@@ -295,15 +306,25 @@ const CollectionItem = ({ item, collection, searchText }) => {
|
||||
>
|
||||
Rename
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
dropdownTippyRef.current.hide();
|
||||
setCloneItemModalOpen(true);
|
||||
}}
|
||||
>
|
||||
Clone
|
||||
</div>
|
||||
{!isFolder && (
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
dropdownTippyRef.current.hide();
|
||||
setCloneItemModalOpen(true);
|
||||
handleClick(null);
|
||||
handleRun();
|
||||
}}
|
||||
>
|
||||
Clone
|
||||
Run
|
||||
</div>
|
||||
)}
|
||||
{!isFolder && item.type === 'http-request' && (
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useState, forwardRef, useRef, useEffect } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { uuid } from 'utils/common';
|
||||
import filter from 'lodash/filter';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { IconChevronRight, IconDots } from '@tabler/icons';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
@@ -15,21 +14,21 @@ import NewFolder from 'components/Sidebar/NewFolder';
|
||||
import CollectionItem from './CollectionItem';
|
||||
import RemoveCollection from './RemoveCollection';
|
||||
import ExportCollection from './ExportCollection';
|
||||
import CollectionProperties from './CollectionProperties';
|
||||
import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search';
|
||||
import { isItemAFolder, isItemARequest, transformCollectionToSaveToExportAsFile } from 'utils/collections';
|
||||
import exportCollection from 'utils/collections/export';
|
||||
|
||||
import RenameCollection from './RenameCollection';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import CloneCollection from './CloneCollection/index';
|
||||
|
||||
const Collection = ({ collection, searchText }) => {
|
||||
const [showNewFolderModal, setShowNewFolderModal] = useState(false);
|
||||
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
||||
const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false);
|
||||
const [showCloneCollectionModalOpen, setShowCloneCollectionModalOpen] = useState(false);
|
||||
const [showExportCollectionModal, setShowExportCollectionModal] = useState(false);
|
||||
const [showRemoveCollectionModal, setShowRemoveCollectionModal] = useState(false);
|
||||
const [collectionPropertiesModal, setCollectionPropertiesModal] = useState(false);
|
||||
const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -80,9 +79,14 @@ const Collection = ({ collection, searchText }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportClick = () => {
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
exportCollection(transformCollectionToSaveToExportAsFile(collectionCopy));
|
||||
const viewCollectionSettings = () => {
|
||||
dispatch(
|
||||
addTab({
|
||||
uid: uuid(),
|
||||
collectionUid: collection.uid,
|
||||
type: 'collection-settings'
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const [{ isOver }, drop] = useDrop({
|
||||
@@ -131,8 +135,8 @@ const Collection = ({ collection, searchText }) => {
|
||||
{showExportCollectionModal && (
|
||||
<ExportCollection collection={collection} onClose={() => setShowExportCollectionModal(false)} />
|
||||
)}
|
||||
{collectionPropertiesModal && (
|
||||
<CollectionProperties collection={collection} onClose={() => setCollectionPropertiesModal(false)} />
|
||||
{showCloneCollectionModalOpen && (
|
||||
<CloneCollection collection={collection} onClose={() => setShowCloneCollectionModalOpen(false)} />
|
||||
)}
|
||||
<div className="flex py-1 collection-name items-center" ref={drop}>
|
||||
<div
|
||||
@@ -170,6 +174,15 @@ const Collection = ({ collection, searchText }) => {
|
||||
>
|
||||
New Folder
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowCloneCollectionModalOpen(true);
|
||||
}}
|
||||
>
|
||||
Clone
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
@@ -201,19 +214,19 @@ const Collection = ({ collection, searchText }) => {
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setCollectionPropertiesModal(true);
|
||||
setShowRemoveCollectionModal(true);
|
||||
}}
|
||||
>
|
||||
Properties
|
||||
Remove
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowRemoveCollectionModal(true);
|
||||
viewCollectionSettings();
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
Settings
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
color: ${(props) => props.theme.text};
|
||||
.collection-options {
|
||||
svg {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.label {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
185
packages/bruno-app/src/components/Sidebar/GoldenEdition/index.js
Normal file
185
packages/bruno-app/src/components/Sidebar/GoldenEdition/index.js
Normal file
@@ -0,0 +1,185 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Modal from 'components/Modal/index';
|
||||
import { PostHog } from 'posthog-node';
|
||||
import { uuid } from 'utils/common';
|
||||
import { IconHeart, IconUser, IconUsers } from '@tabler/icons';
|
||||
import platformLib from 'platform';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
|
||||
let posthogClient = null;
|
||||
const posthogApiKey = 'phc_7gtqSrrdZRohiozPMLIacjzgHbUlhalW1Bu16uYijMR';
|
||||
const getPosthogClient = () => {
|
||||
if (posthogClient) {
|
||||
return posthogClient;
|
||||
}
|
||||
|
||||
posthogClient = new PostHog(posthogApiKey);
|
||||
return posthogClient;
|
||||
};
|
||||
const getAnonymousTrackingId = () => {
|
||||
let id = localStorage.getItem('bruno.anonymousTrackingId');
|
||||
|
||||
if (!id || !id.length || id.length !== 21) {
|
||||
id = uuid();
|
||||
localStorage.setItem('bruno.anonymousTrackingId', id);
|
||||
}
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
const HeartIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 w-5 h-4 text-yellow-600"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path fillRule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const CheckIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
className="flex-shrink-0 w-5 h-5 text-green-500"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const GoldenEdition = ({ onClose }) => {
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
const anonymousId = getAnonymousTrackingId();
|
||||
const client = getPosthogClient();
|
||||
client.capture({
|
||||
distinctId: anonymousId,
|
||||
event: 'golden-edition-modal-opened',
|
||||
properties: {
|
||||
os: platformLib.os.family
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const goldenEditionBuyClick = () => {
|
||||
const anonymousId = getAnonymousTrackingId();
|
||||
const client = getPosthogClient();
|
||||
client.capture({
|
||||
distinctId: anonymousId,
|
||||
event: 'golden-edition-buy-clicked',
|
||||
properties: {
|
||||
os: platformLib.os.family
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const goldenEditon = [
|
||||
'Inbuilt Bru File Explorer',
|
||||
'Visual Git (Like Gitlens for Vscode)',
|
||||
'GRPC, Websocket, SocketIO, MQTT',
|
||||
'Intergration with Secret Managers',
|
||||
'Load Data from File for Collection Run',
|
||||
'Developer Tools',
|
||||
'OpenAPI Designer',
|
||||
'Performance/Load Testing',
|
||||
'Inbuilt Terminal',
|
||||
'Custom Themes'
|
||||
];
|
||||
|
||||
const [pricingOption, setPricingOption] = useState('individuals');
|
||||
|
||||
const handlePricingOptionChange = (option) => {
|
||||
setPricingOption(option);
|
||||
};
|
||||
|
||||
const themeBasedContainerClassNames = storedTheme === 'light' ? 'text-gray-900' : 'text-white';
|
||||
const themeBasedTabContainerClassNames = storedTheme === 'light' ? 'bg-gray-200' : 'bg-gray-800';
|
||||
const themeBasedActiveTabClassNames =
|
||||
storedTheme === 'light' ? 'bg-white text-gray-900 font-medium' : 'bg-gray-700 text-white font-medium';
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Modal size="sm" title={'Golden Edition'} handleCancel={onClose} hideFooter={true}>
|
||||
<div className={`flex flex-col w-full ${themeBasedContainerClassNames}`}>
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold">Golden Edition</h3>
|
||||
<a
|
||||
onClick={() => {
|
||||
goldenEditionBuyClick();
|
||||
window.open('https://www.usebruno.com/pricing', '_blank');
|
||||
}}
|
||||
target="_blank"
|
||||
className="flex text-white bg-yellow-600 hover:bg-yellow-700 font-medium rounded-lg text-sm px-4 py-2 text-center cursor-pointer"
|
||||
>
|
||||
<IconHeart size={18} strokeWidth={1.5} />{' '}
|
||||
<span className="ml-2">{pricingOption === 'individuals' ? 'Buy' : 'Subscribe'}</span>
|
||||
</a>
|
||||
</div>
|
||||
{pricingOption === 'individuals' ? (
|
||||
<div>
|
||||
<div className="my-4">
|
||||
<span className="text-3xl font-extrabold">$19</span>
|
||||
</div>
|
||||
<p className="bg-yellow-200 text-black rounded-md px-2 py-1 mb-2 inline-flex text-sm">One Time Payment</p>
|
||||
<p className="text-sm">perpetual license for 2 devices, with 2 years of updates</p>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="my-4">
|
||||
<span className="text-3xl font-extrabold">$2</span>
|
||||
</div>
|
||||
<p>/user/month</p>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`flex items-center justify-between my-8 w-40 rounded-full p-1 ${themeBasedTabContainerClassNames}`}
|
||||
style={{ width: '24rem' }}
|
||||
>
|
||||
<div
|
||||
className={`cursor-pointer w-1/2 h-8 flex items-center justify-center rounded-full ${
|
||||
pricingOption === 'individuals' ? themeBasedActiveTabClassNames : 'text-gray-500'
|
||||
}`}
|
||||
onClick={() => handlePricingOptionChange('individuals')}
|
||||
>
|
||||
<IconUser className="text-gray-500 mr-2 icon" size={16} strokeWidth={1.5} /> Individuals
|
||||
</div>
|
||||
<div
|
||||
className={`cursor-pointer w-1/2 h-8 flex items-center justify-center rounded-full ${
|
||||
pricingOption === 'organizations' ? themeBasedActiveTabClassNames : 'text-gray-500'
|
||||
}`}
|
||||
onClick={() => handlePricingOptionChange('organizations')}
|
||||
>
|
||||
<IconUsers className="text-gray-500 mr-2 icon" size={16} strokeWidth={1.5} /> Organizations
|
||||
</div>
|
||||
</div>
|
||||
<ul role="list" className="space-y-3 text-left">
|
||||
<li className="flex items-center space-x-3">
|
||||
<HeartIcon />
|
||||
<span>Support Bruno's Development</span>
|
||||
</li>
|
||||
{goldenEditon.map((item, index) => (
|
||||
<li className="flex items-center space-x-3" key={index}>
|
||||
<CheckIcon />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoldenEdition;
|
||||
@@ -21,7 +21,6 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, collectionName }) =>
|
||||
.required('name is required')
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
console.log('here');
|
||||
handleSubmit(values.collectionLocation);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -35,6 +35,10 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textarea.curl-command {
|
||||
min-height: 150px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import React, { useRef, useEffect, useCallback } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -11,17 +11,43 @@ import { addTab } from 'providers/ReduxStore/slices/tabs';
|
||||
import HttpMethodSelector from 'components/RequestPane/QueryUrl/HttpMethodSelector';
|
||||
import { getDefaultRequestPaneTab } from 'utils/collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { getRequestFromCurlCommand } from 'utils/curl';
|
||||
|
||||
const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const inputRef = useRef();
|
||||
const {
|
||||
brunoConfig: { presets: collectionPresets = {} }
|
||||
} = collection;
|
||||
|
||||
const getRequestType = (collectionPresets) => {
|
||||
if (!collectionPresets || !collectionPresets.requestType) {
|
||||
return 'http-request';
|
||||
}
|
||||
|
||||
// Note: Why different labels for the same thing?
|
||||
// http-request and graphql-request are used inside the app's json representation of a request
|
||||
// http and graphql are used in Bru DSL as well as collection exports
|
||||
// We need to eventually standardize the app's DSL to use the same labels as bru DSL
|
||||
if (collectionPresets.requestType === 'http') {
|
||||
return 'http-request';
|
||||
}
|
||||
|
||||
if (collectionPresets.requestType === 'graphql') {
|
||||
return 'graphql-request';
|
||||
}
|
||||
|
||||
return 'http-request';
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
requestName: '',
|
||||
requestType: 'http-request',
|
||||
requestUrl: '',
|
||||
requestMethod: 'GET'
|
||||
requestType: getRequestType(collectionPresets),
|
||||
requestUrl: collectionPresets.requestUrl || '',
|
||||
requestMethod: 'GET',
|
||||
curlCommand: ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
requestName: Yup.string()
|
||||
@@ -35,7 +61,18 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
const trimmedValue = value ? value.trim().toLowerCase() : '';
|
||||
return !['collection', 'folder'].includes(trimmedValue);
|
||||
}
|
||||
})
|
||||
}),
|
||||
curlCommand: Yup.string().when('requestType', {
|
||||
is: (requestType) => requestType === 'from-curl',
|
||||
then: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.required('curlCommand is required')
|
||||
.test({
|
||||
name: 'curlCommand',
|
||||
message: `Invalid cURL Command`,
|
||||
test: (value) => getRequestFromCurlCommand(value) !== null
|
||||
})
|
||||
})
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
if (isEphemeral) {
|
||||
@@ -61,6 +98,22 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
onClose();
|
||||
})
|
||||
.catch((err) => toast.error(err ? err.message : 'An error occurred while adding the request'));
|
||||
} else if (values.requestType === 'from-curl') {
|
||||
const request = getRequestFromCurlCommand(values.curlCommand);
|
||||
dispatch(
|
||||
newHttpRequest({
|
||||
requestName: values.requestName,
|
||||
requestType: 'http-request',
|
||||
requestUrl: request.url,
|
||||
requestMethod: request.method,
|
||||
collectionUid: collection.uid,
|
||||
itemUid: item ? item.uid : null,
|
||||
headers: request.headers,
|
||||
body: request.body
|
||||
})
|
||||
)
|
||||
.then(() => onClose())
|
||||
.catch((err) => toast.error(err ? err.message : 'An error occurred while adding the request'));
|
||||
} else {
|
||||
dispatch(
|
||||
newHttpRequest({
|
||||
@@ -86,6 +139,25 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
|
||||
const handlePaste = useCallback(
|
||||
(event) => {
|
||||
const clipboardData = event.clipboardData || window.clipboardData;
|
||||
const pastedData = clipboardData.getData('Text');
|
||||
|
||||
// Check if pasted data looks like a cURL command
|
||||
const curlCommandRegex = /^\s*curl\s/i;
|
||||
if (curlCommandRegex.test(pastedData)) {
|
||||
// Switch to the 'from-curl' request type
|
||||
formik.setFieldValue('requestType', 'from-curl');
|
||||
formik.setFieldValue('curlCommand', pastedData);
|
||||
|
||||
// Prevent the default paste behavior to avoid pasting into the textarea
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Modal size="md" title="New Request" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
@@ -124,15 +196,28 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
<label htmlFor="graphql-request" className="ml-1 cursor-pointer select-none">
|
||||
GraphQL
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="from-curl"
|
||||
className="cursor-pointer ml-auto"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="from-curl"
|
||||
checked={formik.values.requestType === 'from-curl'}
|
||||
/>
|
||||
|
||||
<label htmlFor="from-curl" className="ml-1 cursor-pointer select-none">
|
||||
From cURL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="requestName" className="block font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-name"
|
||||
id="request-name"
|
||||
type="text"
|
||||
name="requestName"
|
||||
ref={inputRef}
|
||||
@@ -148,38 +233,58 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
<div className="text-red-500">{formik.errors.requestName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{formik.values.requestType !== 'from-curl' ? (
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
URL
|
||||
</label>
|
||||
|
||||
<div className="mt-4">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
URL
|
||||
</label>
|
||||
|
||||
<div className="flex items-center mt-2 ">
|
||||
<div className="flex items-center h-full method-selector-container">
|
||||
<HttpMethodSelector
|
||||
method={formik.values.requestMethod}
|
||||
onMethodSelect={(val) => formik.setFieldValue('requestMethod', val)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
<input
|
||||
id="request-url"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
className="px-3 w-full "
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.requestUrl || ''}
|
||||
/>
|
||||
<div className="flex items-center mt-2 ">
|
||||
<div className="flex items-center h-full method-selector-container">
|
||||
<HttpMethodSelector
|
||||
method={formik.values.requestMethod}
|
||||
onMethodSelect={(val) => formik.setFieldValue('requestMethod', val)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
<input
|
||||
id="request-url"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
className="px-3 w-full "
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.requestUrl || ''}
|
||||
onPaste={handlePaste}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{formik.touched.requestUrl && formik.errors.requestUrl ? (
|
||||
<div className="text-red-500">{formik.errors.requestUrl}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
cURL Command
|
||||
</label>
|
||||
<textarea
|
||||
name="curlCommand"
|
||||
placeholder="Enter cURL request here.."
|
||||
className="block textbox w-full mt-4 curl-command"
|
||||
value={formik.values.curlCommand}
|
||||
onChange={formik.handleChange}
|
||||
></textarea>
|
||||
{formik.touched.curlCommand && formik.errors.curlCommand ? (
|
||||
<div className="text-red-500">{formik.errors.curlCommand}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{formik.touched.requestUrl && formik.errors.requestUrl ? (
|
||||
<div className="text-red-500">{formik.errors.requestUrl}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -3,21 +3,25 @@ import Collections from './Collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import GitHubButton from 'react-github-btn';
|
||||
import Preferences from 'components/Preferences';
|
||||
import Cookies from 'components/Cookies';
|
||||
import GoldenEdition from './GoldenEdition';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { IconSettings } from '@tabler/icons';
|
||||
import { IconSettings, IconCookie, IconHeart } from '@tabler/icons';
|
||||
import { updateLeftSidebarWidth, updateIsDragging, showPreferences } from 'providers/ReduxStore/slices/app';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
|
||||
const MIN_LEFT_SIDEBAR_WIDTH = 222;
|
||||
const MIN_LEFT_SIDEBAR_WIDTH = 221;
|
||||
const MAX_LEFT_SIDEBAR_WIDTH = 600;
|
||||
|
||||
const Sidebar = () => {
|
||||
const leftSidebarWidth = useSelector((state) => state.app.leftSidebarWidth);
|
||||
const preferencesOpen = useSelector((state) => state.app.showPreferences);
|
||||
const [goldenEditonOpen, setGoldenEditonOpen] = useState(false);
|
||||
|
||||
const [asideWidth, setAsideWidth] = useState(leftSidebarWidth);
|
||||
const [cookiesOpen, setCookiesOpen] = useState(false);
|
||||
|
||||
const { storedTheme } = useTheme();
|
||||
|
||||
@@ -77,8 +81,10 @@ const Sidebar = () => {
|
||||
return (
|
||||
<StyledWrapper className="flex relative h-screen">
|
||||
<aside>
|
||||
{goldenEditonOpen && <GoldenEdition onClose={() => setGoldenEditonOpen(false)} />}
|
||||
<div className="flex flex-row h-screen w-full">
|
||||
{preferencesOpen && <Preferences onClose={() => dispatch(showPreferences(false))} />}
|
||||
{cookiesOpen && <Cookies onClose={() => setCookiesOpen(false)} />}
|
||||
|
||||
<div className="flex flex-col w-full" style={{ width: asideWidth }}>
|
||||
<div className="flex flex-col flex-grow">
|
||||
@@ -91,21 +97,34 @@ const Sidebar = () => {
|
||||
<IconSettings
|
||||
size={18}
|
||||
strokeWidth={1.5}
|
||||
className="mr-2 hover:text-gray-700"
|
||||
className="mr-2 hover:text-gray-700"
|
||||
onClick={() => dispatch(showPreferences(true))}
|
||||
/>
|
||||
<IconCookie
|
||||
size={18}
|
||||
strokeWidth={1.5}
|
||||
className="mr-2 hover:text-gray-700"
|
||||
onClick={() => setCookiesOpen(true)}
|
||||
/>
|
||||
<IconHeart
|
||||
size={18}
|
||||
strokeWidth={1.5}
|
||||
className="mr-2 hover:text-gray-700"
|
||||
onClick={() => setGoldenEditonOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className="pl-1" style={{ position: 'relative', top: '3px' }}>
|
||||
<GitHubButton
|
||||
{/* This will get moved to home page */}
|
||||
{/* <GitHubButton
|
||||
href="https://github.com/usebruno/bruno"
|
||||
data-color-scheme={storedTheme}
|
||||
data-show-count="true"
|
||||
aria-label="Star usebruno/bruno on GitHub"
|
||||
>
|
||||
Star
|
||||
</GitHubButton>
|
||||
</GitHubButton> */}
|
||||
</div>
|
||||
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.0.0</div>
|
||||
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.6.1</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -122,7 +122,7 @@ class SingleLineEditor extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
this.editor.setValue(this.props.value || '');
|
||||
this.editor.setValue(String(this.props.value) || '');
|
||||
this.editor.on('change', this._onEdit);
|
||||
this.addOverlay();
|
||||
}
|
||||
@@ -151,8 +151,8 @@ class SingleLineEditor extends Component {
|
||||
this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default');
|
||||
}
|
||||
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue && this.editor) {
|
||||
this.cachedValue = this.props.value;
|
||||
this.editor.setValue(this.props.value || '');
|
||||
this.cachedValue = String(this.props.value);
|
||||
this.editor.setValue(String(this.props.value) || '');
|
||||
}
|
||||
this.ignoreChangeEvent = false;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,18 @@ const GlobalStyle = createGlobalStyle`
|
||||
}
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
color: ${(props) => props.theme.button.danger.color};
|
||||
background: ${(props) => props.theme.button.danger.bg};
|
||||
border: solid 1px ${(props) => props.theme.button.danger.border};
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
color: ${(props) => props.theme.button.secondary.color};
|
||||
background: ${(props) => props.theme.button.secondary.bg};
|
||||
|
||||
@@ -22,8 +22,10 @@ if (!SERVER_RENDERED) {
|
||||
require('codemirror/addon/fold/brace-fold');
|
||||
require('codemirror/addon/fold/foldgutter');
|
||||
require('codemirror/addon/fold/xml-fold');
|
||||
require('codemirror/addon/hint/javascript-hint');
|
||||
require('codemirror/addon/hint/show-hint');
|
||||
require('codemirror/addon/lint/lint');
|
||||
require('codemirror/addon/lint/json-lint');
|
||||
require('codemirror/addon/mode/overlay');
|
||||
require('codemirror/addon/scroll/simplescrollbars');
|
||||
require('codemirror/addon/search/jump-to-line');
|
||||
@@ -38,6 +40,7 @@ if (!SERVER_RENDERED) {
|
||||
require('codemirror-graphql/mode');
|
||||
|
||||
require('utils/codemirror/brunoVarInfo');
|
||||
require('utils/codemirror/javascript-lint');
|
||||
}
|
||||
|
||||
export default function Main() {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import each from 'lodash/each';
|
||||
import filter from 'lodash/filter';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { findCollectionByUid, flattenItems, isItemARequest } from 'utils/collections';
|
||||
import { pluralizeWord } from 'utils/common';
|
||||
import { completeQuitFlow } from 'providers/ReduxStore/slices/app';
|
||||
import { saveMultipleRequests } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { IconAlertTriangle } from '@tabler/icons';
|
||||
import Modal from 'components/Modal';
|
||||
|
||||
const SaveRequestsModal = ({ onClose }) => {
|
||||
const MAX_UNSAVED_REQUESTS_TO_SHOW = 5;
|
||||
const currentDrafts = [];
|
||||
const collections = useSelector((state) => state.collections.collections);
|
||||
const tabs = useSelector((state) => state.tabs.tabs);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const tabsByCollection = groupBy(tabs, (t) => t.collectionUid);
|
||||
Object.keys(tabsByCollection).forEach((collectionUid) => {
|
||||
const collection = findCollectionByUid(collections, collectionUid);
|
||||
if (collection) {
|
||||
const items = flattenItems(collection.items);
|
||||
const drafts = filter(items, (item) => isItemARequest(item) && item.draft);
|
||||
each(drafts, (draft) => {
|
||||
currentDrafts.push({
|
||||
...draft,
|
||||
collectionUid: collectionUid
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (currentDrafts.length === 0) {
|
||||
return dispatch(completeQuitFlow());
|
||||
}
|
||||
}, [currentDrafts, dispatch]);
|
||||
|
||||
const closeWithoutSave = () => {
|
||||
dispatch(completeQuitFlow());
|
||||
onClose();
|
||||
};
|
||||
|
||||
const closeWithSave = () => {
|
||||
dispatch(saveMultipleRequests(currentDrafts))
|
||||
.then(() => dispatch(completeQuitFlow()))
|
||||
.then(() => onClose());
|
||||
};
|
||||
|
||||
if (!currentDrafts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md"
|
||||
title="Unsaved changes"
|
||||
confirmText="Save and Close"
|
||||
cancelText="Close without saving"
|
||||
handleCancel={onClose}
|
||||
disableEscapeKey={true}
|
||||
disableCloseOnOutsideClick={true}
|
||||
closeModalFadeTimeout={150}
|
||||
hideFooter={true}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
|
||||
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
|
||||
</div>
|
||||
<p className="mt-4">
|
||||
Do you want to save the changes you made to the following{' '}
|
||||
<span className="font-medium">{currentDrafts.length}</span> {pluralizeWord('request', currentDrafts.length)}?
|
||||
</p>
|
||||
|
||||
<ul className="mt-4">
|
||||
{currentDrafts.slice(0, MAX_UNSAVED_REQUESTS_TO_SHOW).map((item) => {
|
||||
return (
|
||||
<li key={item.uid} className="mt-1 text-xs">
|
||||
{item.filename}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
{currentDrafts.length > MAX_UNSAVED_REQUESTS_TO_SHOW && (
|
||||
<p className="mt-1 text-xs">
|
||||
...{currentDrafts.length - MAX_UNSAVED_REQUESTS_TO_SHOW} additional{' '}
|
||||
{pluralizeWord('request', currentDrafts.length - MAX_UNSAVED_REQUESTS_TO_SHOW)} not shown
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between mt-6">
|
||||
<div>
|
||||
<button className="btn btn-sm btn-danger" onClick={closeWithoutSave}>
|
||||
Don't Save
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button className="btn btn-close btn-sm mr-2" onClick={onClose}>
|
||||
Cancel
|
||||
</button>
|
||||
<button className="btn btn-secondary btn-sm" onClick={closeWithSave}>
|
||||
{currentDrafts.length > 1 ? 'Save All' : 'Save'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SaveRequestsModal;
|
||||
@@ -0,0 +1,32 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import SaveRequestsModal from './SaveRequestsModal';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
|
||||
const ConfirmAppClose = () => {
|
||||
const { ipcRenderer } = window;
|
||||
const [showConfirmClose, setShowConfirmClose] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isElectron()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clearListener = ipcRenderer.on('main:start-quit-flow', () => {
|
||||
setShowConfirmClose(true);
|
||||
});
|
||||
|
||||
return () => {
|
||||
clearListener();
|
||||
};
|
||||
}, [isElectron, ipcRenderer, dispatch, setShowConfirmClose]);
|
||||
|
||||
if (!showConfirmClose) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SaveRequestsModal onClose={() => setShowConfirmClose(false)} />;
|
||||
};
|
||||
|
||||
export default ConfirmAppClose;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user