Compare commits

...

15 Commits

Author SHA1 Message Date
Anoop M D
87404132ae chore: bump to v1.14.0 2024-04-22 20:23:24 +05:30
Bassam Mejlaoui
7019a77ec6 Update readme.md (#1642)
Adding Arabic
2024-04-22 00:21:33 +05:30
Bassam Mejlaoui
ad9169d78a Create readme_ar.md (#1640)
* Create readme_ar.md
Translation of README and Additional Sections to Arabic
2024-04-22 00:20:47 +05:30
Baptiste Poulain
54e99cbbd7 fix(tailwindcss-forms): messes up input fields everywhere, removed it (#1817)
* fix(tailwindcss-forms): messes up input fields everywhere, removed it
* fix(tailwindcss-form): update package-lock.json
---------
Co-authored-by: bpoulaindev <bpoulainpro@gmail.com>
Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
2024-04-22 00:17:50 +05:30
Max Bauer
eb9862b8f5 bugfix: hide autocomplete on escape keyup (#2091) 2024-04-22 00:14:34 +05:30
Bijin A B
7a3cc4e040 fix: error boundary adding return-to-app and force-quit options (#2131)
* fix: error boundary adding return-to-app and force-quit options

* fix: method context

* fix: method context

* chore: increased print width to 200 in prettier

* chore: reverted the prettier printWidth increase

* feat: remove box-shadow from error boundary message layout

---------

Co-authored-by: Anoop M D <anoop.md1421@gmail.com>
2024-04-22 00:11:51 +05:30
Adam Rashid
60cb9da83e flx: add unique key prop to ImportCollection options (#2133)
This PR uses the key of the options object in import collection to suppress the unique key prop warning.

Fixes issue #2078
2024-04-22 00:00:21 +05:30
Gustavo Ferreira da Silva
28e4159c21 feat: toggle password visibility (#2127)
* Toggle password in ProxySettings

Input 'password' in ProxySettings (Preferences) can be toggled to be visible or not.

* Solving button overlap

- Button to toggle password won't cover the text;
- Added toggle password feature in CollectionSettings (ClientCertSettings and ProxySettings).
2024-04-21 23:50:16 +05:30
Anoop M D
59ffb0166f chore: reverted the prettier printWidth increase 2024-04-19 00:34:04 +05:30
Anoop M D
e8ec74107d chore: increased print width to 200 in prettier 2024-04-18 18:25:48 +05:30
Anoop M D
d027d90ed5 fix(#2079): test cases to catch getEnvName() error in the future 2024-04-18 17:52:36 +05:30
Anoop M D
7bbc4727be fix: fixed ui error when ignores setting is not found in bruno.json 2024-04-18 17:43:32 +05:30
Anoop M D
386a8df151 feat: bumped node version to use to v20 2024-04-18 17:42:49 +05:30
Anoop M D
2ed35108d7 chore: moved codemirror autocomplete util to its own file 2024-04-17 14:30:19 +05:30
Sanjai Kumar
a0860febee Refactor Bruno language parser to support multiline text blocks (#2086)
* Refactor Bruno language parser to support multiline text blocks

* Refactor SingleLineEditor component to allow newlines and adjust height dynamically

* Refactor SingleLineEditor component to fix overflow issue

* Fix overflow issue in SingleLineEditor component

* Refactor SingleLineEditor and MultiLineEditor components to fix overflow issues and allow newlines

* Fix overflow and scrolling issues in MultiLineEditor component

---------

Co-authored-by: Sanjai Kumar <sk@sk.local>
2024-04-17 14:19:44 +05:30
31 changed files with 873 additions and 154 deletions

2
.nvmrc
View File

@@ -1 +1 @@
v18.13.0
v20.9.0

139
docs/readme/readme_ar.md Normal file
View File

@@ -0,0 +1,139 @@
<br />
<img src="assets/images/logo-transparent.png" width="80"/>
### برونو - بيئة تطوير مفتوحة المصدر لاستكشاف واختبار واجهات برمجة التطبيقات (APIs).
[![GitHub version](https://badge.fury.io/gh/usebruno%2Fbruno.svg)](https://badge.fury.io/gh/usebruno%bruno)
[![CI](https://github.com/usebruno/bruno/actions/workflows/unit-tests.yml/badge.svg?branch=main)](https://github.com/usebruno/bruno/workflows/unit-tests.yml)
[![Commit Activity](https://img.shields.io/github/commit-activity/m/usebruno/bruno)](https://github.com/usebruno/bruno/pulse)
[![X](https://img.shields.io/twitter/follow/use_bruno?style=social&logo=x)](https://twitter.com/use_bruno)
[![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com)
[![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads)
**English** | [Українська](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](docs/readme/readme_pl.md) | [简体中文](docs/readme/readme_cn.md) | [正體中文](docs/readme/readme_zhtw.md) | [العربية](docs/readme/readme_ar.md)
برونو هو عميل API جديد ومبتكر، يهدف إلى ثورة الحالة الحالية التي يمثلها برنامج Postman وأدوات مماثلة هناك.
يقوم برونو بتخزين مجموعاتك مباشرة في مجلد على نظام الملفات الخاص بك. نحن نستخدم لغة ترميز النص العادية، Bru، لحفظ معلومات حول طلبات واجهة برمجة التطبيقات (API).
يمكنك استخدام Git أو أي نظام تحكم في الإصدار الذي تفضله للتعاون على مجموعات API الخاصة بك.
برونو هو خاص بالاستخدام دون اتصال بالإنترنت. ليس هناك خطط لإضافة مزامنة السحابة إلى برونو أبدًا. نحن نقدر خصوصية بياناتك ونعتقد أنه يجب أن تظل على جهازك. اقرأ رؤيتنا على المدى الطويل [هنا](https://github.com/usebruno/bruno/discussions/269)
📢 شاهد حديثنا الأخير في مؤتمر India FOSS 3.0 [هنا](https://www.youtube.com/watch?v=7bSMFpbcPiY)
![bruno](https://github.com/usebruno/bruno/blob/main/assets/images/landing-2.png) <br /><br />
### الطبعة الذهبية ✨
غالبية ميزاتنا مجانية ومفتوحة المصدر.
نحن نسعى لتحقيق توازن متناغم بين [مبادئ الشفافية والاستدامة](https://github.com/usebruno/bruno/discussions/269)
طلبات الشراء لـ [الطبعة الذهبية](https://www.usebruno.com/pricing) ستطلق قريبًا بسعر ~~$19~~ **$9** ! <br/>
[اشترك هنا](https://usebruno.ck.page/4c65576bd4) لتصلك إشعارات عند الإطلاق.
### التثبيت
برونو متاح كتنزيل ثنائي [على موقعنا على الويب](https://www.usebruno.com/downloads) لأنظمة التشغيل Mac و Windows و Linux.
يمكنك أيضًا تثبيت برونو عبر مديري الحزم مثل Homebrew و Chocolatey و Scoop و Snap و Flatpak و Apt.
```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 عبر Flatpak
flatpak install com.usebruno.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
```
### التشغيل عبر منصات متعددة 🖥️
![bruno](https://github.com/usebruno/bruno/blob/main/assets/images/run-anywhere.png) <br /><br />
### التعاون عبر Git 👩‍💻🧑‍💻
أو أي نظام تحكم في الإصدار الذي تفضله
![bruno](https://github.com/usebruno/bruno/blob/main/assets/images/version-control.png) <br /><br />
### الروابط المهمة 📌
- [رؤيتنا على المدى الطويل](https://github.com/usebruno/bruno/discussions/269)
- [خارطة الطريق](https://github.com/usebruno/bruno/discussions/384)
- [التوثيق](https://docs.usebruno.com)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/bruno)
- [الموقع الإلكتروني](https://www.usebruno.com)
- [التسعير](https://www.usebruno.com/pricing)
- [التنزيل](https://www.usebruno.com/downloads)
- [Github Sponsors](https://github.com/sponsors/helloanoop).
### عروض 🎥
- [الشهادات](https://github.com/usebruno/bruno/discussions/343)
- [مركز المعرفة](https://github.com/usebruno/bruno/discussions/386)
- [Scriptmania](https://github.com/usebruno/bruno/discussions/385)
### الدعم ❤️
إذا كنت تحب برونو وترغب في دعم عملنا مفتوح المصدر، فكر في رعايتنا عبر [Github Sponsors](https://github.com/sponsors/helloanoop).
### شارك الشهادات 📣
إذا كان برونو قد ساعدك في العمل وفرقك، فلا تنسى مشاركة [شهاداتك في مناقشتنا على GitHub](https://github.com/usebruno/bruno/discussions/343)
### نشر إلى مديري الحزم الجديدة
يرجى الرجوع [هنا](publishing.md) لمزيد من المعلومات.
### تواصل معنا 🌐
[𝕏 (تويتر)](https://twitter.com/use_bruno) <br />
[الموقع الإلكتروني](https://www.usebruno.com) <br />
[ديسكورد](https://discord.com/invite/KgcZUncpjq) <br />
[لينكدإن](https://www.linkedin.com/company/usebruno)
### علامة تجارية
**الاسم**
`برونو` هو علامة تجارية تمتلكها [أنوب إم دي](https://www.helloanoop.com/)
**الشعار**
الشعار من [OpenMoji](https://openmoji.org/library/emoji-1F436/). الترخيص: CC [BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
### المساهمة 👩‍💻🧑‍💻
يسعدني أنك تتطلع لتحسين برونو. يرجى الاطلاع على [دليل المساهمة](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>
### الرخصة 📄
[MIT](license.md)

190
package-lock.json generated
View File

@@ -37,6 +37,7 @@
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -2771,6 +2772,7 @@
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
@@ -2786,6 +2788,7 @@
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
"version": "6.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -2796,6 +2799,7 @@
},
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
"version": "6.2.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -2806,10 +2810,12 @@
},
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
"version": "9.2.2",
"dev": true,
"license": "MIT"
},
"node_modules/@isaacs/cliui/node_modules/string-width": {
"version": "5.1.2",
"dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
@@ -2825,6 +2831,7 @@
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@@ -2838,6 +2845,7 @@
},
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
"version": "8.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
@@ -3979,6 +3987,7 @@
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -5518,6 +5527,7 @@
},
"node_modules/any-promise": {
"version": "1.3.0",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
@@ -5646,6 +5656,7 @@
},
"node_modules/arg": {
"version": "5.0.2",
"dev": true,
"license": "MIT"
},
"node_modules/argparse": {
@@ -6707,6 +6718,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -7469,6 +7481,7 @@
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
@@ -7657,6 +7670,7 @@
},
"node_modules/cssesc": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"bin": {
"cssesc": "bin/cssesc"
@@ -7995,6 +8009,7 @@
},
"node_modules/didyoumean": {
"version": "1.2.2",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/diff-sequences": {
@@ -8053,6 +8068,7 @@
},
"node_modules/dlv": {
"version": "1.1.3",
"dev": true,
"license": "MIT"
},
"node_modules/dmg-builder": {
@@ -8220,6 +8236,7 @@
},
"node_modules/eastasianwidth": {
"version": "0.2.0",
"dev": true,
"license": "MIT"
},
"node_modules/ecc-jsbn": {
@@ -9297,6 +9314,7 @@
},
"node_modules/foreground-child": {
"version": "3.1.1",
"dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.0",
@@ -9311,6 +9329,7 @@
},
"node_modules/foreground-child/node_modules/signal-exit": {
"version": "4.1.0",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
@@ -10710,6 +10729,7 @@
},
"node_modules/is-core-module": {
"version": "2.13.1",
"dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.0"
@@ -10966,6 +10986,7 @@
},
"node_modules/isexe": {
"version": "2.0.0",
"dev": true,
"license": "ISC"
},
"node_modules/isobject": {
@@ -11091,6 +11112,7 @@
},
"node_modules/jackspeak": {
"version": "2.3.6",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
@@ -12256,6 +12278,7 @@
},
"node_modules/lilconfig": {
"version": "2.1.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -12842,6 +12865,7 @@
},
"node_modules/minipass": {
"version": "5.0.0",
"devOptional": true,
"license": "ISC",
"engines": {
"node": ">=8"
@@ -12942,6 +12966,7 @@
},
"node_modules/mz": {
"version": "2.7.0",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0",
@@ -13296,6 +13321,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true,
"engines": {
"node": ">= 6"
}
@@ -13642,6 +13668,7 @@
},
"node_modules/path-key": {
"version": "3.1.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -13649,10 +13676,12 @@
},
"node_modules/path-parse": {
"version": "1.0.7",
"dev": true,
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "1.10.1",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^9.1.1 || ^10.0.0",
@@ -13667,6 +13696,7 @@
},
"node_modules/path-scurry/node_modules/lru-cache": {
"version": "10.2.0",
"dev": true,
"license": "ISC",
"engines": {
"node": "14 || >=16.14"
@@ -13859,6 +13889,7 @@
},
"node_modules/pirates": {
"version": "4.0.6",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -14024,6 +14055,7 @@
},
"node_modules/postcss": {
"version": "8.4.35",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -14138,6 +14170,7 @@
},
"node_modules/postcss-import": {
"version": "15.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"postcss-value-parser": "^4.0.0",
@@ -14155,6 +14188,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dev": true,
"dependencies": {
"camelcase-css": "^2.0.1"
},
@@ -14366,6 +14400,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
"dev": true,
"dependencies": {
"postcss-selector-parser": "^6.0.11"
},
@@ -14562,6 +14597,7 @@
},
"node_modules/postcss-selector-parser": {
"version": "6.0.15",
"dev": true,
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -14606,6 +14642,7 @@
},
"node_modules/postcss/node_modules/nanoid": {
"version": "3.3.7",
"dev": true,
"funding": [
{
"type": "github",
@@ -15233,6 +15270,7 @@
},
"node_modules/read-cache": {
"version": "1.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"pify": "^2.3.0"
@@ -15240,6 +15278,7 @@
},
"node_modules/read-cache/node_modules/pify": {
"version": "2.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -15721,6 +15760,7 @@
},
"node_modules/resolve": {
"version": "1.22.8",
"dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.13.0",
@@ -16275,6 +16315,7 @@
},
"node_modules/shebang-command": {
"version": "2.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -16285,6 +16326,7 @@
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -16637,6 +16679,7 @@
"node_modules/string-width-cjs": {
"name": "string-width",
"version": "4.2.3",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -16679,6 +16722,7 @@
"node_modules/strip-ansi-cjs": {
"name": "strip-ansi",
"version": "6.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -16821,6 +16865,7 @@
},
"node_modules/sucrase": {
"version": "3.35.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
@@ -16841,6 +16886,7 @@
},
"node_modules/sucrase/node_modules/brace-expansion": {
"version": "2.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
@@ -16848,6 +16894,7 @@
},
"node_modules/sucrase/node_modules/commander": {
"version": "4.1.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 6"
@@ -16855,6 +16902,7 @@
},
"node_modules/sucrase/node_modules/glob": {
"version": "10.3.10",
"dev": true,
"license": "ISC",
"dependencies": {
"foreground-child": "^3.1.0",
@@ -16875,6 +16923,7 @@
},
"node_modules/sucrase/node_modules/minimatch": {
"version": "9.0.3",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -16909,6 +16958,7 @@
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -17081,6 +17131,7 @@
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
"integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
"dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -17117,6 +17168,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -17128,6 +17180,7 @@
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
"dev": true,
"bin": {
"jiti": "bin/jiti.js"
}
@@ -17136,6 +17189,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
@@ -17170,6 +17224,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
"dev": true,
"engines": {
"node": ">=14"
},
@@ -17181,6 +17236,7 @@
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
"integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
"dev": true,
"bin": {
"yaml": "bin.mjs"
},
@@ -17359,6 +17415,7 @@
},
"node_modules/thenify": {
"version": "3.3.1",
"dev": true,
"license": "MIT",
"dependencies": {
"any-promise": "^1.0.0"
@@ -17366,6 +17423,7 @@
},
"node_modules/thenify-all": {
"version": "1.6.0",
"dev": true,
"license": "MIT",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
@@ -17540,6 +17598,7 @@
},
"node_modules/ts-interface-checker": {
"version": "0.1.13",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/ts-jest": {
@@ -18282,6 +18341,7 @@
},
"node_modules/which": {
"version": "2.0.2",
"dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -18341,6 +18401,7 @@
"node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -19688,7 +19749,7 @@
},
"packages/bruno-electron": {
"name": "bruno",
"version": "v1.12.3",
"version": "v1.13.1",
"dependencies": {
"@aws-sdk/credential-providers": "3.525.0",
"@usebruno/common": "0.1.0",
@@ -20870,7 +20931,8 @@
},
"dependencies": {
"@alloc/quick-lru": {
"version": "5.2.0"
"version": "5.2.0",
"dev": true
},
"@ampproject/remapping": {
"version": "2.2.1",
@@ -22613,6 +22675,7 @@
},
"@isaacs/cliui": {
"version": "8.0.2",
"dev": true,
"requires": {
"string-width": "^5.1.2",
"string-width-cjs": "npm:string-width@^4.2.0",
@@ -22623,16 +22686,20 @@
},
"dependencies": {
"ansi-regex": {
"version": "6.0.1"
"version": "6.0.1",
"dev": true
},
"ansi-styles": {
"version": "6.2.1"
"version": "6.2.1",
"dev": true
},
"emoji-regex": {
"version": "9.2.2"
"version": "9.2.2",
"dev": true
},
"string-width": {
"version": "5.1.2",
"dev": true,
"requires": {
"eastasianwidth": "^0.2.0",
"emoji-regex": "^9.2.2",
@@ -22641,12 +22708,14 @@
},
"strip-ansi": {
"version": "7.1.0",
"dev": true,
"requires": {
"ansi-regex": "^6.0.1"
}
},
"wrap-ansi": {
"version": "8.1.0",
"dev": true,
"requires": {
"ansi-styles": "^6.1.0",
"string-width": "^5.0.1",
@@ -23422,6 +23491,7 @@
},
"@pkgjs/parseargs": {
"version": "0.11.0",
"dev": true,
"optional": true
},
"@playwright/test": {
@@ -23949,14 +24019,6 @@
"mini-svg-data-uri": "^1.2.3"
}
},
"@tailwindcss/forms": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz",
"integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==",
"requires": {
"mini-svg-data-uri": "^1.2.3"
}
},
"@tippyjs/react": {
"version": "4.2.6",
"requires": {
@@ -25534,7 +25596,8 @@
"dev": true
},
"any-promise": {
"version": "1.3.0"
"version": "1.3.0",
"dev": true
},
"anymatch": {
"version": "3.1.3",
@@ -25633,7 +25696,8 @@
}
},
"arg": {
"version": "5.0.2"
"version": "5.0.2",
"dev": true
},
"argparse": {
"version": "2.0.1"
@@ -27164,7 +27228,8 @@
"camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
"dev": true
},
"camelize": {
"version": "1.0.1"
@@ -27650,6 +27715,7 @@
},
"cross-spawn": {
"version": "7.0.3",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -27757,7 +27823,8 @@
"dev": true
},
"cssesc": {
"version": "3.0.0"
"version": "3.0.0",
"dev": true
},
"cssnano": {
"version": "5.1.15",
@@ -27951,7 +28018,8 @@
"optional": true
},
"didyoumean": {
"version": "1.2.2"
"version": "1.2.2",
"dev": true
},
"diff-sequences": {
"version": "29.6.3",
@@ -27990,7 +28058,8 @@
}
},
"dlv": {
"version": "1.1.3"
"version": "1.1.3",
"dev": true
},
"dmg-builder": {
"version": "23.0.2",
@@ -28111,7 +28180,8 @@
"dev": true
},
"eastasianwidth": {
"version": "0.2.0"
"version": "0.2.0",
"dev": true
},
"ecc-jsbn": {
"version": "0.1.2",
@@ -28817,13 +28887,15 @@
},
"foreground-child": {
"version": "3.1.1",
"dev": true,
"requires": {
"cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1"
},
"dependencies": {
"signal-exit": {
"version": "4.1.0"
"version": "4.1.0",
"dev": true
}
}
},
@@ -29678,6 +29750,7 @@
},
"is-core-module": {
"version": "2.13.1",
"dev": true,
"requires": {
"hasown": "^2.0.0"
}
@@ -29819,7 +29892,8 @@
"dev": true
},
"isexe": {
"version": "2.0.0"
"version": "2.0.0",
"dev": true
},
"isobject": {
"version": "3.0.1"
@@ -29899,6 +29973,7 @@
},
"jackspeak": {
"version": "2.3.6",
"dev": true,
"requires": {
"@isaacs/cliui": "^8.0.2",
"@pkgjs/parseargs": "^0.11.0"
@@ -30690,7 +30765,8 @@
"dev": true
},
"lilconfig": {
"version": "2.1.0"
"version": "2.1.0",
"dev": true
},
"lines-and-columns": {
"version": "1.2.4"
@@ -31055,7 +31131,8 @@
"dev": true
},
"minipass": {
"version": "5.0.0"
"version": "5.0.0",
"devOptional": true
},
"minizlib": {
"version": "2.1.2",
@@ -31118,6 +31195,7 @@
},
"mz": {
"version": "2.7.0",
"dev": true,
"requires": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
@@ -31330,7 +31408,8 @@
"object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"dev": true
},
"object-inspect": {
"version": "1.13.1"
@@ -31546,20 +31625,24 @@
"dev": true
},
"path-key": {
"version": "3.1.1"
"version": "3.1.1",
"dev": true
},
"path-parse": {
"version": "1.0.7"
"version": "1.0.7",
"dev": true
},
"path-scurry": {
"version": "1.10.1",
"dev": true,
"requires": {
"lru-cache": "^9.1.1 || ^10.0.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"dependencies": {
"lru-cache": {
"version": "10.2.0"
"version": "10.2.0",
"dev": true
}
}
},
@@ -31687,7 +31770,8 @@
}
},
"pirates": {
"version": "4.0.6"
"version": "4.0.6",
"dev": true
},
"pixelmatch": {
"version": "4.0.2",
@@ -31781,6 +31865,7 @@
},
"postcss": {
"version": "8.4.35",
"dev": true,
"requires": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
@@ -31788,7 +31873,8 @@
},
"dependencies": {
"nanoid": {
"version": "3.3.7"
"version": "3.3.7",
"dev": true
}
}
},
@@ -31840,6 +31926,7 @@
},
"postcss-import": {
"version": "15.1.0",
"dev": true,
"requires": {
"postcss-value-parser": "^4.0.0",
"read-cache": "^1.0.0",
@@ -31850,6 +31937,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
"dev": true,
"requires": {
"camelcase-css": "^2.0.1"
}
@@ -31958,6 +32046,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
"dev": true,
"requires": {
"postcss-selector-parser": "^6.0.11"
}
@@ -32056,6 +32145,7 @@
},
"postcss-selector-parser": {
"version": "6.0.15",
"dev": true,
"requires": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -32448,12 +32538,14 @@
},
"read-cache": {
"version": "1.0.0",
"dev": true,
"requires": {
"pify": "^2.3.0"
},
"dependencies": {
"pify": {
"version": "2.3.0"
"version": "2.3.0",
"dev": true
}
}
},
@@ -32788,6 +32880,7 @@
},
"resolve": {
"version": "1.22.8",
"dev": true,
"requires": {
"is-core-module": "^2.13.0",
"path-parse": "^1.0.7",
@@ -33142,12 +33235,14 @@
},
"shebang-command": {
"version": "2.0.0",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0"
"version": "3.0.0",
"dev": true
},
"side-channel": {
"version": "1.0.5",
@@ -33375,6 +33470,7 @@
},
"string-width-cjs": {
"version": "npm:string-width@4.2.3",
"dev": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -33402,6 +33498,7 @@
},
"strip-ansi-cjs": {
"version": "npm:strip-ansi@6.0.1",
"dev": true,
"requires": {
"ansi-regex": "^5.0.1"
}
@@ -33472,6 +33569,7 @@
},
"sucrase": {
"version": "3.35.0",
"dev": true,
"requires": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
@@ -33484,15 +33582,18 @@
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
}
},
"commander": {
"version": "4.1.1"
"version": "4.1.1",
"dev": true
},
"glob": {
"version": "10.3.10",
"dev": true,
"requires": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.5",
@@ -33503,6 +33604,7 @@
},
"minimatch": {
"version": "9.0.3",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
}
@@ -33523,7 +33625,8 @@
}
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0"
"version": "1.0.0",
"dev": true
},
"svg2png": {
"version": "4.1.1",
@@ -33645,6 +33748,7 @@
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
"integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
"dev": true,
"requires": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -33674,6 +33778,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dev": true,
"requires": {
"is-glob": "^4.0.3"
}
@@ -33681,12 +33786,14 @@
"jiti": {
"version": "1.21.0",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q=="
"integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
"dev": true
},
"postcss-load-config": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
"integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
"dev": true,
"requires": {
"lilconfig": "^3.0.0",
"yaml": "^2.3.4"
@@ -33695,14 +33802,16 @@
"lilconfig": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz",
"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ=="
"integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==",
"dev": true
}
}
},
"yaml": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.1.tgz",
"integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg=="
"integrity": "sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==",
"dev": true
}
}
},
@@ -33812,12 +33921,14 @@
},
"thenify": {
"version": "3.3.1",
"dev": true,
"requires": {
"any-promise": "^1.0.0"
}
},
"thenify-all": {
"version": "1.6.0",
"dev": true,
"requires": {
"thenify": ">= 3.1.0 < 4"
}
@@ -33932,7 +34043,8 @@
}
},
"ts-interface-checker": {
"version": "0.1.13"
"version": "0.1.13",
"dev": true
},
"ts-jest": {
"version": "29.1.2",
@@ -34379,6 +34491,7 @@
},
"which": {
"version": "2.0.2",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -34418,6 +34531,7 @@
},
"wrap-ansi-cjs": {
"version": "npm:wrap-ansi@7.0.0",
"dev": true,
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",

View File

@@ -17,7 +17,6 @@
"@fortawesome/react-fontawesome": "^0.1.16",
"@reduxjs/toolkit": "^1.8.0",
"@tabler/icons": "^1.46.0",
"@tailwindcss/forms": "^0.5.7",
"@tippyjs/react": "^4.2.6",
"@usebruno/common": "0.1.0",
"@usebruno/graphql-docs": "0.1.0",

View File

@@ -232,7 +232,7 @@ export default class CodeEditor extends React.Component {
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) &&
/^(?!Shift|Tab|Enter|Escape|ArrowUp|ArrowDown|ArrowLeft|ArrowRight|\s)\w*/.test(event.key) &&
curWord.length > 0 &&
!/\/\/|\/\*|.*{{|`[^$]*{|`[^{]*$/.test(currentLine.slice(0, end)) &&
/(?<!\d)[a-zA-Z\._]$/.test(curWord)

View File

@@ -9,7 +9,7 @@ const StyledWrapper = styled.div`
color: ${(props) => props.theme.colors.text.yellow};
}
input {
.non-passphrase-input {
width: 300px;
}

View File

@@ -3,6 +3,8 @@ import { IconCertificate, IconTrash, IconWorld } from '@tabler/icons';
import { useFormik } from 'formik';
import { uuid } from 'utils/common';
import * as Yup from 'yup';
import { IconEye, IconEyeOff } from '@tabler/icons';
import { useState } from 'react';
import StyledWrapper from './StyledWrapper';
@@ -29,6 +31,8 @@ const ClientCertSettings = ({ clientCertConfig, onUpdate, onRemove }) => {
formik.values[e.name] = e.files[0].path;
};
const [passwordVisible, setPasswordVisible] = useState(false);
return (
<StyledWrapper className="w-full h-full">
<div className="text-xs mb-4 text-muted">Add client certificates to be used for specific domains.</div>
@@ -63,7 +67,7 @@ const ClientCertSettings = ({ clientCertConfig, onUpdate, onRemove }) => {
type="text"
name="domain"
placeholder="*.example.org"
className="block textbox"
className="block textbox non-passphrase-input"
onChange={formik.handleChange}
value={formik.values.domain || ''}
/>
@@ -79,7 +83,7 @@ const ClientCertSettings = ({ clientCertConfig, onUpdate, onRemove }) => {
id="certFilePath"
type="file"
name="certFilePath"
className="block"
className="block non-passphrase-input"
onChange={(e) => getFile(e.target)}
/>
{formik.touched.certFilePath && formik.errors.certFilePath ? (
@@ -94,7 +98,7 @@ const ClientCertSettings = ({ clientCertConfig, onUpdate, onRemove }) => {
id="keyFilePath"
type="file"
name="keyFilePath"
className="block"
className="block non-passphrase-input"
onChange={(e) => getFile(e.target)}
/>
{formik.touched.keyFilePath && formik.errors.keyFilePath ? (
@@ -105,14 +109,23 @@ const ClientCertSettings = ({ clientCertConfig, onUpdate, onRemove }) => {
<label className="settings-label" htmlFor="passphrase">
Passphrase
</label>
<input
id="passphrase"
type="password"
name="passphrase"
className="block textbox"
onChange={formik.handleChange}
value={formik.values.passphrase || ''}
/>
<div className="textbox flex flex-row items-center w-[300px] h-[1.70rem] relative">
<input
id="passphrase"
type={passwordVisible ? 'text' : 'password'}
name="passphrase"
className="outline-none w-64 bg-transparent"
onChange={formik.handleChange}
value={formik.values.passphrase || ''}
/>
<button
type="button"
className="btn btn-sm absolute right-0 l"
onClick={() => setPasswordVisible(!passwordVisible)}
>
{passwordVisible ? <IconEyeOff size={18} strokeWidth={1.5} /> : <IconEye size={18} strokeWidth={1.5} />}
</button>
</div>
{formik.touched.passphrase && formik.errors.passphrase ? (
<div className="ml-1 text-red-500">{formik.errors.passphrase}</div>
) : null}

View File

@@ -36,7 +36,7 @@ const Info = ({ collection }) => {
</tr>
<tr className="">
<td className="py-2 px-2 text-right">Ignored files&nbsp;:</td>
<td className="py-2 px-2 break-all">{collection.brunoConfig.ignore.map((x) => `'${x}'`).join(', ')}</td>
<td className="py-2 px-2 break-all">{collection.brunoConfig?.ignore?.map((x) => `'${x}'`).join(', ')}</td>
</tr>
<tr className="">
<td className="py-2 px-2 text-right">Environments&nbsp;:</td>

View File

@@ -4,6 +4,8 @@ import Tooltip from 'components/Tooltip';
import StyledWrapper from './StyledWrapper';
import * as Yup from 'yup';
import toast from 'react-hot-toast';
import { IconEye, IconEyeOff } from '@tabler/icons';
import { useState } from 'react';
const ProxySettings = ({ proxyConfig, onUpdate }) => {
const proxySchema = Yup.object({
@@ -78,6 +80,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
});
}
});
const [passwordVisible, setPasswordVisible] = useState(false);
useEffect(() => {
formik.setValues({
@@ -277,18 +280,27 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<input
id="auth.password"
type="password"
name="auth.password"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[1.70rem] relative">
<input
id="auth.password"
type={passwordVisible ? 'text' : 'password'}
name="auth.password"
className="outline-none bg-transparent w-[10.5rem]"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
<button
type="button"
className="btn btn-sm absolute right-0"
onClick={() => setPasswordVisible(!passwordVisible)}
>
{passwordVisible ? <IconEyeOff size={18} strokeWidth={1.5} /> : <IconEye size={18} strokeWidth={1.5} />}
</button>
</div>
{formik.touched.auth?.password && formik.errors.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.auth.password}</div>
) : null}

View File

@@ -0,0 +1,57 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
width: 100%;
height: fit-content;
max-height: 200px;
overflow: auto;
.CodeMirror {
background: transparent;
height: fit-content;
font-size: 14px;
line-height: 30px;
overflow: hidden;
.CodeMirror-scroll {
overflow: hidden !important;
${'' /* padding-bottom: 50px !important; */}
position: relative;
display: contents;
margin: 0px;
padding: 0px;
}
.CodeMirror-vscrollbar,
.CodeMirror-hscrollbar,
.CodeMirror-scrollbar-filler {
display: none;
}
.CodeMirror-lines {
padding: 0;
}
.CodeMirror-cursor {
height: 20px !important;
margin-top: 5px !important;
border-left: 1px solid ${(props) => props.theme.text} !important;
}
pre {
font-family: Inter, sans-serif !important;
font-weight: 400;
}
.CodeMirror-line {
color: ${(props) => props.theme.text};
padding: 0;
}
.CodeMirror-selected {
background-color: rgba(212, 125, 59, 0.3);
}
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,140 @@
import React, { Component } from 'react';
import isEqual from 'lodash/isEqual';
import { getAllVariables } from 'utils/collections';
import { defineCodeMirrorBrunoVariablesMode } from 'utils/common/codemirror';
import StyledWrapper from './StyledWrapper';
let CodeMirror;
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
if (!SERVER_RENDERED) {
CodeMirror = require('codemirror');
}
class MultiLineEditor extends Component {
constructor(props) {
super(props);
// Keep a cached version of the value, this cache will be updated when the
// editor is updated, which can later be used to protect the editor from
// unnecessary updates during the update lifecycle.
this.cachedValue = props.value || '';
this.editorRef = React.createRef();
this.variables = {};
}
componentDidMount() {
// Initialize CodeMirror as a single line editor
/** @type {import("codemirror").Editor} */
this.editor = CodeMirror(this.editorRef.current, {
lineWrapping: false,
lineNumbers: false,
theme: this.props.theme === 'dark' ? 'monokai' : 'default',
mode: 'brunovariables',
brunoVarInfo: {
variables: getAllVariables(this.props.collection)
},
scrollbarStyle: null,
tabindex: 0,
extraKeys: {
Enter: () => {
if (this.props.onRun) {
this.props.onRun();
}
},
'Ctrl-Enter': () => {
if (this.props.onRun) {
this.props.onRun();
}
},
'Cmd-Enter': () => {
if (this.props.onRun) {
this.props.onRun();
}
},
'Alt-Enter': () => {
this.editor.setValue(this.editor.getValue() + '\n');
this.editor.setCursor({ line: this.editor.lineCount(), ch: 0 });
},
'Shift-Enter': () => {
this.editor.setValue(this.editor.getValue() + '\n');
this.editor.setCursor({ line: this.editor.lineCount(), ch: 0 });
},
'Cmd-S': () => {
if (this.props.onSave) {
this.props.onSave();
}
},
'Ctrl-S': () => {
if (this.props.onSave) {
this.props.onSave();
}
},
'Cmd-F': () => {},
'Ctrl-F': () => {},
// Tabbing disabled to make tabindex work
Tab: false,
'Shift-Tab': false
}
});
if (this.props.autocomplete) {
this.editor.on('keyup', (cm, event) => {
if (!cm.state.completionActive /*Enables keyboard navigation in autocomplete list*/ && event.keyCode != 13) {
/*Enter - do not open autocomplete list just after item has been selected in it*/
CodeMirror.commands.autocomplete(cm, CodeMirror.hint.anyword, { autocomplete: this.props.autocomplete });
}
});
}
this.editor.setValue(String(this.props.value) || '');
this.editor.on('change', this._onEdit);
this.addOverlay();
}
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {
this.cachedValue = this.editor.getValue();
if (this.props.onChange) {
this.props.onChange(this.cachedValue);
}
}
};
componentDidUpdate(prevProps) {
// Ensure the changes caused by this update are not interpreted as
// user-input changes which could otherwise result in an infinite
// event loop.
this.ignoreChangeEvent = true;
let variables = getAllVariables(this.props.collection);
if (!isEqual(variables, this.variables)) {
this.editor.options.brunoVarInfo.variables = variables;
this.addOverlay();
}
if (this.props.theme !== prevProps.theme && this.editor) {
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 = String(this.props.value);
this.editor.setValue(String(this.props.value) || '');
}
if (this.editorRef?.current) {
this.editorRef.current.scrollTo(0, 10000);
}
this.ignoreChangeEvent = false;
}
componentWillUnmount() {
this.editor.getWrapperElement().remove();
}
addOverlay = () => {
let variables = getAllVariables(this.props.collection);
this.variables = variables;
defineCodeMirrorBrunoVariablesMode(variables, 'text/plain');
this.editor.setOption('mode', 'brunovariables');
};
render() {
return <StyledWrapper ref={this.editorRef} className="single-line-editor"></StyledWrapper>;
}
}
export default MultiLineEditor;

View File

@@ -6,6 +6,8 @@ import { savePreferences } from 'providers/ReduxStore/slices/app';
import StyledWrapper from './StyledWrapper';
import { useDispatch, useSelector } from 'react-redux';
import { IconEye, IconEyeOff } from '@tabler/icons';
import { useState } from 'react';
const ProxySettings = ({ close }) => {
const preferences = useSelector((state) => state.app.preferences);
@@ -88,6 +90,8 @@ const ProxySettings = ({ close }) => {
});
};
const [passwordVisible, setPasswordVisible] = useState(false);
useEffect(() => {
formik.setValues({
enabled: preferences.proxy.enabled || false,
@@ -164,6 +168,7 @@ const ProxySettings = ({ close }) => {
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="hostname">
Hostname
@@ -240,18 +245,27 @@ const ProxySettings = ({ close }) => {
<label className="settings-label" htmlFor="auth.password">
Password
</label>
<input
id="auth.password"
type="password"
name="auth.password"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
<div className="textbox flex flex-row items-center w-[13.2rem] h-[2.25rem] relative">
<input
id="auth.password"
type={passwordVisible ? `text` : 'password'}
name="auth.password"
className="outline-none w-[10.5rem] bg-transparent"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.auth.password}
onChange={formik.handleChange}
/>
<button
type="button"
className="btn btn-sm absolute right-0"
onClick={() => setPasswordVisible(!passwordVisible)}
>
{passwordVisible ? <IconEyeOff size={18} strokeWidth={2} /> : <IconEye size={18} strokeWidth={2} />}
</button>
</div>
{formik.touched.auth?.password && formik.errors.auth?.password ? (
<div className="ml-3 text-red-500">{formik.errors.auth.password}</div>
) : null}

View File

@@ -9,7 +9,7 @@ import {
updateFormUrlEncodedParam,
deleteFormUrlEncodedParam
} from 'providers/ReduxStore/slices/collections';
import SingleLineEditor from 'components/SingleLineEditor';
import MultiLineEditor from 'components/MultiLineEditor';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
@@ -92,7 +92,7 @@ const FormUrlEncodedParams = ({ item, collection }) => {
/>
</td>
<td>
<SingleLineEditor
<MultiLineEditor
value={param.value}
theme={storedTheme}
onSave={onSave}

View File

@@ -9,7 +9,7 @@ import {
updateMultipartFormParam,
deleteMultipartFormParam
} from 'providers/ReduxStore/slices/collections';
import SingleLineEditor from 'components/SingleLineEditor';
import MultiLineEditor from 'components/MultiLineEditor';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import StyledWrapper from './StyledWrapper';
import FilePickerEditor from 'components/FilePickerEditor';
@@ -121,7 +121,7 @@ const MultipartFormParams = ({ item, collection }) => {
collection={collection}
/>
) : (
<SingleLineEditor
<MultiLineEditor
onSave={onSave}
theme={storedTheme}
value={param.value}
@@ -137,6 +137,7 @@ const MultipartFormParams = ({ item, collection }) => {
)
}
onRun={handleRun}
allowNewlines={true}
collection={collection}
/>
)}

View File

@@ -115,6 +115,7 @@ const RequestHeaders = ({ item, collection }) => {
)
}
onRun={handleRun}
allowNewlines={true}
collection={collection}
/>
</td>

View File

@@ -79,7 +79,7 @@ const ImportCollection = ({ onClose, handleSubmit }) => {
</div>
<div className="flex justify-start w-full mt-4 max-w-[450px]">
{Object.entries(options || {}).map(([key, option]) => (
<div className="relative flex items-start">
<div key={key} className="relative flex items-start">
<div className="flex h-6 items-center">
<input
id="comments"

View File

@@ -129,7 +129,7 @@ const Sidebar = () => {
Star
</GitHubButton> */}
</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.13.1</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.14.0</div>
</div>
</div>
</div>

View File

@@ -13,20 +13,15 @@ const StyledWrapper = styled.div`
line-height: 30px;
overflow: hidden;
.CodeMirror-vscrollbar {
display: none !important;
}
.CodeMirror-scroll {
overflow: hidden !important;
padding-bottom: 50px !important;
}
.CodeMirror-hscrollbar {
display: none !important;
}
.CodeMirror-vscrollbar,
.CodeMirror-hscrollbar,
.CodeMirror-scrollbar-filler {
display: none !important;
display: none;
}
.CodeMirror-lines {
@@ -46,8 +41,7 @@ const StyledWrapper = styled.div`
.CodeMirror-line {
color: ${(props) => props.theme.text};
padding-left: 0;
padding-right: 0;
padding: 0;
}
.CodeMirror-selected {

View File

@@ -9,40 +9,6 @@ const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODE
if (!SERVER_RENDERED) {
CodeMirror = require('codemirror');
CodeMirror.registerHelper('hint', 'anyword', (editor, options) => {
const word = /[\w$-]+/;
const wordlist = (options && options.autocomplete) || [];
let cur = editor.getCursor(),
curLine = editor.getLine(cur.line);
let end = cur.ch,
start = end;
while (start && word.test(curLine.charAt(start - 1))) --start;
let curWord = start != end && curLine.slice(start, end);
// Check if curWord is a valid string before proceeding
if (typeof curWord !== 'string' || curWord.length < 3) {
return null; // Abort the hint
}
const list = (options && options.list) || [];
const re = new RegExp(word.source, 'g');
for (let dir = -1; dir <= 1; dir += 2) {
let line = cur.line,
endLine = Math.min(Math.max(line + dir * 500, editor.firstLine()), editor.lastLine()) + dir;
for (; line != endLine; line += dir) {
let text = editor.getLine(line),
m;
while ((m = re.exec(text))) {
if (line == cur.line && curWord.length < 3) continue;
list.push(...wordlist.filter((el) => el.toLowerCase().startsWith(curWord.toLowerCase())));
}
}
}
return { list: [...new Set(list)], from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end) };
});
CodeMirror.commands.autocomplete = (cm, hint, options) => {
cm.showHint({ hint, ...options });
};
}
class SingleLineEditor extends Component {

View File

@@ -41,6 +41,7 @@ if (!SERVER_RENDERED) {
require('utils/codemirror/brunoVarInfo');
require('utils/codemirror/javascript-lint');
require('utils/codemirror/autocomplete');
}
export default function Main() {

View File

@@ -1,5 +1,7 @@
import React from 'react';
import Bruno from 'components/Bruno/index';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
@@ -14,29 +16,61 @@ class ErrorBoundary extends React.Component {
}
componentDidCatch(error, errorInfo) {
console.log({ error, errorInfo });
this.setState({ hasError: true, error, errorInfo });
}
returnToApp() {
const { ipcRenderer } = window;
ipcRenderer.invoke('open-file');
this.setState({ hasError: false, error: null, errorInfo: null });
}
forceQuit() {
const { ipcRenderer } = window;
ipcRenderer.invoke('main:force-quit');
}
render() {
if (this.state.hasError) {
return (
<div className="flex items-center justify-center p-10">
<div className="bg-white rounded-lg shadow-lg p-4 w-full">
<div className="flex text-center justify-center p-20 h-full">
<div className="bg-white rounded-lg p-10 w-full">
<div className="m-auto" style={{ width: '256px' }}>
<Bruno width={256} />
</div>
<h1 className="text-2xl font-semibold text-red-600 mb-2">Oops! Something went wrong</h1>
<p className="text-red-600 mb-2">{this.state.error && this.state.error.toString()}</p>
{this.state.error && this.state.error.stack && (
<pre className="bg-gray-100 p-2 rounded-lg overflow-auto">{this.state.error.stack}</pre>
)}
<p className="text-red-500 mb-2">
If you are using an official production build: the above error is most likely a bug!
<br />
Please report this under:
<a
className="text-link hover:underline cursor-pointer ml-2"
href="https://github.com/usebruno/bruno/issues"
target="_blank"
>
https://github.com/usebruno/bruno/issues
</a>
</p>
<button
className="bg-red-500 text-white px-4 py-2 mt-4 rounded hover:bg-red-600 transition"
onClick={() => {
this.setState({ hasError: false, error: null });
}}
onClick={() => this.returnToApp()}
>
Close
Return to App
</button>
<div className="text-red-500 mt-3">
<a href="" className="hover:underline cursor-pointer" onClick={this.forceQuit}>
Force Quit
</a>
</div>
</div>
</div>
);
}
return this.props.children;
}
}

View File

@@ -60,7 +60,7 @@ const trackStart = () => {
event: 'start',
properties: {
os: platformLib.os.family,
version: '1.13.1'
version: '1.14.0'
}
});
};

View File

@@ -0,0 +1,40 @@
let CodeMirror;
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
if (!SERVER_RENDERED) {
CodeMirror = require('codemirror');
CodeMirror.registerHelper('hint', 'anyword', (editor, options) => {
const word = /[\w$-]+/;
const wordlist = (options && options.autocomplete) || [];
let cur = editor.getCursor(),
curLine = editor.getLine(cur.line);
let end = cur.ch,
start = end;
while (start && word.test(curLine.charAt(start - 1))) --start;
let curWord = start != end && curLine.slice(start, end);
// Check if curWord is a valid string before proceeding
if (typeof curWord !== 'string' || curWord.length < 3) {
return null; // Abort the hint
}
const list = (options && options.list) || [];
const re = new RegExp(word.source, 'g');
for (let dir = -1; dir <= 1; dir += 2) {
let line = cur.line,
endLine = Math.min(Math.max(line + dir * 500, editor.firstLine()), editor.lastLine()) + dir;
for (; line != endLine; line += dir) {
let text = editor.getLine(line),
m;
while ((m = re.exec(text))) {
if (line == cur.line && curWord.length < 3) continue;
list.push(...wordlist.filter((el) => el.toLowerCase().startsWith(curWord.toLowerCase())));
}
}
}
return { list: [...new Set(list)], from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end) };
});
CodeMirror.commands.autocomplete = (cm, hint, options) => {
cm.showHint({ hint, ...options });
};
}

View File

@@ -1,5 +1,5 @@
{
"version": "v1.13.1",
"version": "v1.14.0",
"name": "bruno",
"description": "Opensource API Client for Exploring and Testing APIs",
"homepage": "https://www.usebruno.com",

View File

@@ -628,6 +628,10 @@ const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) =
ipcMain.handle('main:complete-quit-flow', () => {
mainWindow.destroy();
});
ipcMain.handle('main:force-quit', () => {
process.exit();
});
};
const registerCollectionsIpc = (mainWindow, watcher, lastOpenedCollections) => {

View File

@@ -35,12 +35,16 @@ const grammar = ohm.grammar(`Bru {
keychar = ~(tagend | st | nl | ":") any
valuechar = ~(nl | tagend) any
// Multiline text block surrounded by '''
multilinetextblockdelimiter = "'''"
multilinetextblock = multilinetextblockdelimiter (~multilinetextblockdelimiter any)* multilinetextblockdelimiter
// Dictionary Blocks
dictionary = st* "{" pairlist? tagend
pairlist = optionalnl* pair (~tagend stnl* pair)* (~tagend space)*
pair = st* key st* ":" st* value st*
key = keychar*
value = valuechar*
value = multilinetextblock | valuechar*
// Dictionary for Assert Block
assertdictionary = st* "{" assertpairlist? tagend
@@ -186,6 +190,19 @@ const sem = grammar.createSemantics().addAttribute('ast', {
return chars.sourceString ? chars.sourceString.trim() : '';
},
value(chars) {
try {
let isMultiline = chars.sourceString?.startsWith(`'''`) && chars.sourceString?.endsWith(`'''`);
if (isMultiline) {
const multilineString = chars.sourceString?.replace(/^'''|'''$/g, '');
return multilineString
.split('\n')
.map((line) => line.slice(4))
.join('\n');
}
return chars.sourceString ? chars.sourceString.trim() : '';
} catch (err) {
console.error(err);
}
return chars.sourceString ? chars.sourceString.trim() : '';
},
assertdictionary(_1, _2, pairlist, _3) {

View File

@@ -12,6 +12,23 @@ const stripLastLine = (text) => {
return text.replace(/(\r?\n)$/, '');
};
const getValueString = (value) => {
const hasNewLines = value.includes('\n');
if (!hasNewLines) {
return value;
}
// Add one level of indentation to the contents of the multistring
const indentedLines = value
.split('\n')
.map((line) => ` ${line}`)
.join('\n');
// Join the lines back together with newline characters and enclose them in triple single quotes
return `'''\n${indentedLines}\n'''`;
};
const jsonToBru = (json) => {
const { meta, http, query, headers, auth, body, script, tests, vars, assertions, docs } = json;
@@ -202,24 +219,23 @@ ${indentString(body.sparql)}
}
if (body && body.formUrlEncoded && body.formUrlEncoded.length) {
bru += `body:form-urlencoded {`;
bru += `body:form-urlencoded {\n`;
if (enabled(body.formUrlEncoded).length) {
bru += `\n${indentString(
enabled(body.formUrlEncoded)
.map((item) => `${item.name}: ${item.value}`)
.join('\n')
)}`;
const enabledValues = enabled(body.formUrlEncoded)
.map((item) => `${item.name}: ${getValueString(item.value)}`)
.join('\n');
bru += `${indentString(enabledValues)}\n`;
}
if (disabled(body.formUrlEncoded).length) {
bru += `\n${indentString(
disabled(body.formUrlEncoded)
.map((item) => `~${item.name}: ${item.value}`)
.join('\n')
)}`;
const disabledValues = disabled(body.formUrlEncoded)
.map((item) => `~${item.name}: ${getValueString(item.value)}`)
.join('\n');
bru += `${indentString(disabledValues)}\n`;
}
bru += '\n}\n\n';
bru += '}\n\n';
}
if (body && body.multipartForm && body.multipartForm.length) {

View File

@@ -0,0 +1,54 @@
meta {
name: get-env-name
type: http
seq: 1
}
get {
url: {{host}}/ping
body: none
auth: none
}
auth:awsv4 {
accessKeyId: a
secretAccessKey: b
sessionToken: c
service: d
region: e
profileName: f
}
script:pre-request {
const envName = bru.getEnvName();
bru.setVar("testEnvName", envName);
}
tests {
test("should get env name in scripts", function() {
const testEnvName = bru.getVar("testEnvName");
expect(testEnvName).to.equal("Prod");
});
}
docs {
# API Documentation
## Introduction
Welcome to the API documentation for [Your API Name]. This document provides instructions on how to make requests to the API and covers available authentication methods.
## Authentication
Before making requests to the API, you need to authenticate your application. [Your API Name] supports the following authentication methods:
### API Key
To use API key authentication, include your API key in the request headers as follows:
```http
GET /api/endpoint
Host: api.example.com
Authorization: Bearer YOUR_API_KEY
}

View File

@@ -0,0 +1,49 @@
meta {
name: get-env-var
type: http
seq: 2
}
get {
url: {{host}}/ping
body: none
auth: none
}
auth:awsv4 {
accessKeyId: a
secretAccessKey: b
sessionToken: c
service: d
region: e
profileName: f
}
tests {
test("should get env var in scripts", function() {
const host = bru.getEnvVar("host")
expect(host).to.equal("https://testbench-sanity.usebruno.com");
});
}
docs {
# API Documentation
## Introduction
Welcome to the API documentation for [Your API Name]. This document provides instructions on how to make requests to the API and covers available authentication methods.
## Authentication
Before making requests to the API, you need to authenticate your application. [Your API Name] supports the following authentication methods:
### API Key
To use API key authentication, include your API key in the request headers as follows:
```http
GET /api/endpoint
Host: api.example.com
Authorization: Bearer YOUR_API_KEY
}

View File

@@ -0,0 +1,54 @@
meta {
name: set-env-var
type: http
seq: 3
}
get {
url: {{host}}/ping
body: none
auth: none
}
auth:awsv4 {
accessKeyId: a
secretAccessKey: b
sessionToken: c
service: d
region: e
profileName: f
}
script:post-response {
bru.setEnvVar("testSetEnvVar", "bruno-29653")
}
tests {
test("should set env var in scripts", function() {
const testSetEnvVar = bru.getEnvVar("testSetEnvVar")
console.log(testSetEnvVar);
expect(testSetEnvVar).to.equal("bruno-29653");
});
}
docs {
# API Documentation
## Introduction
Welcome to the API documentation for [Your API Name]. This document provides instructions on how to make requests to the API and covers available authentication methods.
## Authentication
Before making requests to the API, you need to authenticate your application. [Your API Name] supports the following authentication methods:
### API Key
To use API key authentication, include your API key in the request headers as follows:
```http
GET /api/endpoint
Host: api.example.com
Authorization: Bearer YOUR_API_KEY
}

View File

@@ -10,7 +10,7 @@
[![Website](https://img.shields.io/badge/Website-Visit-blue)](https://www.usebruno.com)
[![Download](https://img.shields.io/badge/Download-Latest-brightgreen)](https://www.usebruno.com/downloads)
**English** | [Українська](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](docs/readme/readme_pl.md) | [简体中文](docs/readme/readme_cn.md) | [正體中文](docs/readme/readme_zhtw.md)
**English** | [Українська](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](docs/readme/readme_pl.md) | [简体中文](docs/readme/readme_cn.md) | [正體中文](docs/readme/readme_zhtw.md) | [العربية](docs/readme/readme_ar.md)
Bruno is a new and innovative API client, aimed at revolutionizing the status quo represented by Postman and similar tools out there.